Я пытаюсь добавить python привязок к научному коду C ++ среднего размера c (несколько десятков тысяч LOC). Мне удалось заставить его работать без особых проблем, но теперь я столкнулся с проблемой, которую не могу решить сам. Код организован следующим образом:
- Все классы и структуры данных скомпилированы в библиотеке
libcommon.a
- Исполняемые файлы создаются путем связывания этой библиотеки
- pybind11 используется для создания
core.so
python модуля
Привязки для "основных" частей работают нормально. Действительно, симуляции, запускаемые из автономного кода или из python, дают точно такие же результаты.
Однако этот код также поддерживает подключаемую систему, которая может загружать совместно используемые библиотеки во время выполнения. Эти общие библиотеки содержат классы, которые наследуются от интерфейсов, определенных в основном коде. Оказывается, что если я пытаюсь связать эти общие библиотеки из python, я получаю печально известные ошибки «неопределенный символ». Я проверил, что эти символы находятся в модуле core.so
(используя nm -D
). Фактически, симуляции, которые выполняют динамическое связывание c с автономным кодом, работают отлично (в одной папке и с одним и тем же вводом). Каким-то образом разделяемая библиотека не может найти правильные символы при вызове через python, но у нее нет проблем при загрузке автономным кодом. Я использую CMake для построения системы.
Далее следует MCE. Скопируйте каждый файл в папку, скопируйте (или свяжите) папку pybind11
в том же месте и используйте следующие команды:
mkdir build
cd build
cmake ..
make
, которые сгенерируют двоичный файл standalone
и модуль python , Исполняемый файл standalone
выдаст правильный вывод. Напротив, использование следующих команд в python3 (что, по крайней мере, в моей голове должно быть эквивалентно) приводит к ошибке:
import core
b = core.load_plugin()
main. cpp
#include "Base.h"
#include "plugin_loader.h"
#include <iostream>
int main() {
Base *d = load_plugin();
if(d == NULL) {
std::cerr << "No lib found" << std::endl;
return 1;
}
d->foo();
return 0;
}
Base.h
#ifndef BASE
#define BASE
struct Base {
Base();
virtual ~Base();
virtual void foo();
};
#endif
Base. cpp
#include "Base.h"
#include <iostream>
Base::Base() {}
Base::~Base() {}
void Base::foo() {
std::cout << "Hey, it's Base!" << std::endl;
}
plugin_loader.h
#ifndef LOADER
#define LOADER
#include "Base.h"
Base *load_plugin();
#endif
plugin_loader. cpp
#include "plugin_loader.h"
#include <dlfcn.h>
#include <iostream>
typedef Base* make_base();
Base *load_plugin() {
void *handle = dlopen("./Derived.so", RTLD_LAZY | RTLD_GLOBAL);
const char *dl_error = dlerror();
if(dl_error != nullptr) {
std::cerr << "Caught an error while opening shared library: " << dl_error << std::endl;
return NULL;
}
make_base *entry = (make_base *) dlsym(handle, "make");
return (Base *) entry();
}
Derived.h
#include "Base.h"
struct Derived : public Base {
Derived();
virtual ~Derived();
void foo() override;
};
extern "C" Base *make() {
return new Derived();
}
Производные. cpp
#include "Derived.h"
#include <iostream>
Derived::Derived() {}
Derived::~Derived() {}
void Derived::foo() {
std::cout << "Hey, it's Derived!" << std::endl;
}
привязок. cpp
#include <pybind11/pybind11.h>
#include "Base.h"
#include "plugin_loader.h"
PYBIND11_MODULE(core, m) {
pybind11::class_<Base, std::shared_ptr<Base>> base(m, "Base");
base.def(pybind11::init<>());
base.def("foo", &Base::foo);
m.def("load_plugin", &load_plugin);
}
CMakeLists.txt
PROJECT(foobar)
# compile the library
ADD_LIBRARY(common SHARED Base.cpp plugin_loader.cpp)
TARGET_LINK_LIBRARIES(common ${CMAKE_DL_LIBS})
SET_TARGET_PROPERTIES(common PROPERTIES POSITION_INDEPENDENT_CODE ON)
# compile the standalone code
ADD_EXECUTABLE(standalone main.cpp)
TARGET_LINK_LIBRARIES(standalone common)
# compile the "plugin"
SET(CMAKE_SHARED_LIBRARY_PREFIX "")
ADD_LIBRARY(Derived SHARED Derived.cpp)
# compile the bindings
ADD_SUBDIRECTORY(pybind11)
INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/pybind11/include )
FIND_PACKAGE( PythonLibs 3 REQUIRED )
INCLUDE_DIRECTORIES( ${PYTHON_INCLUDE_DIRS} )
ADD_LIBRARY(_oxpy_lib STATIC bindings.cpp)
TARGET_LINK_LIBRARIES(_oxpy_lib ${PYTHON_LIBRARIES} common)
SET_TARGET_PROPERTIES(_oxpy_lib PROPERTIES POSITION_INDEPENDENT_CODE ON)
pybind11_add_module(core SHARED bindings.cpp)
TARGET_LINK_LIBRARIES(core PRIVATE _oxpy_lib)