pybind / pybind11

Seamless operability between C++11 and Python
https://pybind11.readthedocs.io/
Other
15.85k stars 2.12k forks source link

Undefined symbol : typeinfo for class #1736

Closed krokoko closed 5 years ago

krokoko commented 5 years ago

Hello guys,

I have an interface class in c++ which contains pure virtual functions (simplified version):

class IFoo {
public:
  IFoo(){};
  virtual ~IFoo(){};
  virtual std::string getName() = 0;
protected:
  virtual void setName(std::string newName) = 0;

}

I am embedding the Python interpreter in my C++ code, which contains as member a pointer to this interface object. I would like to pass this pointer to my Python :

...
int result = m_pythonLib.attr("Initialize")(m_IFoo).cast<int>();
...

I don't want to provide implementation for IFoo as it's deduced at runtime. I just want to expose the headers files to Python so then I can call IFoo's methods :

from example import IFoo
def Initialize(self, pIFoo):
    a = pIFoo.getName()

Is this scenario supported by pybind11 ? I did try the following :

class IFooTrampoline : IFoo{

public:
    using IFoo::IFoo;

    std::string getName() override {
        PYBIND11_OVERLOAD_PURE(
            std::string, /* Return type */
            IFoo,      /* Parent class */
            getName,          /* Name of function in C++ (must match Python name) */
                  /* Argument(s) */
        );
    }

    void setName(std::string newName) override {
        PYBIND11_OVERLOAD_PURE(
            void, /* Return type */
            IFoo,      /* Parent class */
            setName,          /* Name of function in C++ (must match Python name) */
            newName     /* Argument(s) */
        );
    }

};

class IFooPublicist : IFoo{ // helper type for exposing protected functions
public:
   using IFoo::setName;

};

PYBIND11_MODULE(example, m) 
{
    py::class_<IFoo, IFooTrampoline >(m, "IFoo")
        .def(py::init<>())
        .def("getName", &IFoo::getName)
    .def("setName", &IFooPublicist ::setName);
}

This compiles fine, but when i try to pass a pointer to IFoo as parameter in my Python call from C++, I have the following runtime error (demangled) :

typeinfo for IFoo

I did check if there was a mix between no-rtti/rtti while linking my libs but everything seems to be ok (built with gcc). Another option might be the fact that a virtual function is declared but not defined, which is not the case here (default implementation for constructor/destructor), and pure virtual functions for getName/setName. Am i doing something wrong ?

Thank you !

krokoko commented 5 years ago

Just to add some information on how I build it (with cmake) :

Python binary module CMakeLists.txt

cmake_minimum_required(VERSION 2.8.12)

project(PythonModule)

add_subdirectory(../../../../../../deps/pybind11 build)

set(PYTHON_BINDINGS_SOURCES
    pyEntryPoint.cpp
)

pybind11_add_module(${PROJECT_NAME} ${PYTHON_BINDINGS_SOURCES})

target_link_libraries(${PROJECT_NAME} PRIVATE pybind11::embed)

set(PYBIND11_HEADERS
    ${CMAKE_SOURCE_DIR}/../../deps/pybind11/include/pybind11
)

file(COPY ${PYBIND11_HEADERS} DESTINATION ${CMAKE_SOURCE_DIR}/build/install/${PLATFORM_NAME}/include)
...

and my cpp shared lib's CMakeLists.txt (which is just linking to pybind11 embed to access the python interpreter):

cmake_minimum_required(VERSION 2.8)

project(PythonAppManager)
set(PYTHON_APP_MANAGER_INSTALL_HEADERS
    ${CMAKE_SOURCE_DIR}/inc/PythonAppManager.hpp
)

set(PYTHON_APP_MANAGER_SOURCE
    PythonAppManager.cpp
)

add_library(${PROJECT_NAME} SHARED ${PYTHON_APP_MANAGER_SOURCE})
target_link_libraries(${PROJECT_NAME} PRIVATE pybind11::embed)
...
krokoko commented 5 years ago

Ok bug fixed, in my real code the implementation of constructor/destructor was in the .cpp file. After moving it to the header file, everything is called correctly.

tianyudwang commented 5 years ago

Why does it not work when functions are written in the .cpp file and not in the header?

zeng-hello-world commented 4 years ago

I encountered the same question. It's very strange. I just change the boost.python to pybind11, while boost.python works well with functions in .cpp file.

PhilippThoelke commented 4 years ago

For me adding the .cpp file as an additional argument in the pybind11_add_module(...) call in CMakeLists.txt fixed the problem and it found the implementation. I guess it's similar to the add_executable(...) call which also needs all .cpp source files.

vegardjervell commented 3 months ago

For anyone else showing up here: I got this error (ImportError: dlopen(<...>): symbol not found in flat namespace (typeinfo <my_mangled_type>)) when my class had a virtual method that was never defined. Note that the error is different from what I'm used to getting if I have a non-virtual method that is not defined, which usually gives ImportError: dlopen(<...>): symbol not found in flat namespace (<my_mangled_class_method>).