У меня есть следующий класс (это было упрощено):
class Value
{
public:
Value();
~Value();
void setValue(int value);
void setValue(double value);
}
У меня есть модуль Python Boost:
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
BOOST_PYTHON_MODULE(py_classes)
{
typedef std::vector<Value> VectorClass;
boost::python::type_info infoVectorValue = boost::python::type_id<VectorClass>();
const boost::python::converter::registration* regVectorValue = boost::python::converter::registry::query(infoVectorValue);
if (regVectorValue == NULL || (*regVectorValue).m_to_python == NULL) {
class_<VectorClass>("std_vector_value")
.def(vector_indexing_suite<VectorClass>());
}
}
Сценарий Python вызывается из библиотеки dll и использует контейнер из pyd. py_classes
, Когда dll-библиотека вызывается впервые std_vector_value
Тип используется без проблем.
Когда я перезагружаю dll-библиотеку внутри исполняемого файла, я получаю следующее предупреждение:
RuntimeWarning: to-Python converter for class
boost::python::detail::container_element<class std::vector<class Value,class std::allocator<class Value> >,
unsigned __int64,class boost::python::detail::final_vector_derived_policies
<class std::vector<class Value,
class std::allocator<class Value> >,0> >
already registered; second conversion method ignored.
return f(*args, **kwds)
Итак, это значит:
std_vector_value
тип.FreeLibrary
а также LoadLibrary
функции), конвертер to-python, пытающийся зарегистрироваться еще раз и проверяющий, зарегистрирован ли он, говорит, что он уже зарегистрирован, но я не могу использовать std_vector_value
тип из питона.И эта ситуация появляется только для контейнерных классов (если я использую std::vector
или же std::map
— как правило, если я использую vector_indexing_suite
или же map_indexing_suite
), для класса Value это предупреждение не появляется.
Что я делаю неправильно?
Проблема была следующая: boost python загружался из одного exe-процесса, но разными DLL-библиотеками. Это означает, что у меня было 3 DLL-библиотеки, которые выполняли скрипты Python.
Типы повышения Python работали без проблем до тех пор, пока DLL-библиотеки не были перезагружены (вызывая FreeLibrary и LoadLibrary внутри процесса без перезапуска процесса).
Python boost имеет статическую переменную register
(register — это имя этой переменной) хранит ссылки на типы надстроек Python.
Когда исполняемый файл запускается, ссылки на типы добавляются в статическую переменную register
,
Итак, эта проверка работает нормально:
boost::python::type_info infoVectorValue = boost::python::type_id<VectorClass>();
const boost::python::converter::registration* regVectorValue = boost::python::converter::registry::query(infoVectorValue);
if (regVectorValue == NULL || (*regVectorValue).m_to_python == NULL) {
Но когда DLL-библиотеки выгружаются, ссылки на эти типы форсирования Python остаются внутри статической переменной. register
и они приводят к адресам библиотек DLL, которые были освобождены. Это означает, что когда библиотека загружается снова, проверка типов получает ссылку на нужный тип (VectorClass
в моем примере) без проблем, но эта ссылка уже сломана.
Итак, решение этой проблемы состоит в том, чтобы статически связать библиотеку boost python с каждой DLL-библиотекой — каждая DLL-библиотека имеет свою статическую переменную register
и повысить типы Python
ссылки создаются для каждой DLL-библиотеки и, когда DLL-библиотека выгружается, register
Переменная уничтожена для этой DLL.
Других решений пока нет …