У меня есть класс c++, обернутый PyBind11. Проблема в том, что когда скрипт Python завершается, c++destructor не вызывается автоматически. Это вызывает неаккуратный выход, потому что деструктор должен освободить сетевые ресурсы.
В качестве обходного пути необходимо явно удалить объект Python, но я не понимаю, почему!
Пожалуйста, может кто-нибудь объяснить, что здесь не так и как заставить destructor вызываться автоматически, когда объект Python удаляется сборщиком мусора?
Код привязки Pybind11:
py::class_<pcs::Listener>(m, "listener")
.def(py::init<const py::object &, const std::string &, const std::string &, const std::string &, const std::string &, const std::set<std::string> &, const std::string & , const bool & , const bool & >(), R"pbdoc(
Monitors network traffic.
When a desired data source is detected a client instance is connected to consume the data stream.
Reconstructs data on receipt, like a jigsaw. Makes requests to fill any gaps. Verifies the data as sequential.
Data is output by callback to Python. Using the method specified in the constructor, which must accept a string argument.
)pbdoc");
В Питоне:
#Function to callback
def print_string(str):
print("Python; " + str)
lstnr = listener(print_string, 'tcp://127.0.0.1:9001', clientCertPath, serverCertPath, proxyCertPath, desiredSources, 'time_series_data', enableCurve, enableVerbose)
#Run for a minute
cnt = 0
while cnt < 60:
cnt += 1
time.sleep(1)
#Need to call the destructor explicity for some reason
del lstnr






Как упоминалось в комментарии, непосредственной причиной такого поведения является сборщик мусора Python: когда счетчик ссылок для объекта становится равным нулю, сборщик мусора мая уничтожает объект (и тем самым вызывает деструктор С++), но не нужно сделать это в тот момент.
Эта идея более подробно раскрыта в ответе здесь:
https://stackoverflow.com/a/38238013/790979
Как также упоминалось в приведенной выше ссылке, если вам нужно выполнить очистку в конце срока службы объекта в Python, хорошим решением является управление контекстом, где вы должны определить __enter__ и __exit__ в оболочке объекта (либо в pybind11, либо в в самом Python), пусть __exit__ освободит сетевые ресурсы, а затем в клиентском коде Python что-то вроде:
with listener(print_string, 'tcp://127.0.0.1:9001', clientCertPath, serverCertPath, proxyCertPath, desiredSources, 'time_series_data', enableCurve, enableVerbose) as lstnr:
# Run for a minute
cnt = 0
while cnt < 60:
cnt += 1
time.sleep(1)
Спасибо за этот ответ и объяснение @charleslparker. Хотя я до сих пор не решил проблему, потому что изо всех сил пытался найти примеры и документацию о том, как реализовать __enter__ и __exit__ в коде привязки PyBind11.
См. это сообщение: stackoverflow.com/questions/38228170/…