ImportError when accessing static variables from class in same namespace [C++/pybind11]

Viewed 136

First of all, I am relatively new to C++ programming and pybind11. The following example should explain my problem:

a.h:

namespace test {

class A {
    public:
    static int something;
};

void setSomething(int input);

}

a.cpp:

#include <pybind11/pybind11.h>
#include "a.h"

int test::A::something;

void test::setSomething(int input) {
    A::something = input;
}

PYBIND11_MODULE(a, handle) {
  handle.doc() = "I'm a docstring hehe";
  handle.def("setSomething", &test::setSomething);
}

b.h:

namespace test {

class B {
    public:
    B();
    int getSomething() const;
};
}

b.cpp:

#include <pybind11/pybind11.h>
#include "a.h"
#include "b.h"

namespace py = pybind11;

// int test::A::something;

test::B::B(){}

int test::B::getSomething() const {
    return A::something;
}

PYBIND11_MODULE(b, handle) {
  handle.doc() = "I'm a docstring hehe";
  py::class_<test::B>(handle, "B")
    .def(py::init())
    .def("getSomething", &test::B::getSomething);
}

So I have two classes A and B, which are defined in a.cpp and b.cpp that both have header files a.h and b.h. Basically, I am trying to access the static variable from class A in class B. Now if I compile them using CMake and try to run a test.py file I get an ImportError telling me undefined symbol: _ZN4test1A9somethingE. I hope this is not a stupid question. Thanks for your help in advance!

Edit: If I define the variable within the class, it doesn't acquire the value set before or after outside the class.

1 Answers

Presumably you have used the above code to build two modules. Something like a.so and b.so that you would import into Python with separate import a and import b statements.

That's not going to work: You need to access the static variables through the same binary object. Even if you got the linking to work right, ::test::A::something would have a different addresses in a.so and in b.so. (And if you are using the build system helpers from pybind, you won't even notice, because the "default hidden symbols" will prevent linking errors when you import the two modules.)

You'll want to make sure that both a.cpp and b.cpp are linked into the same library object.

In pybind, the first argument to the PYBIND11_MODULE is equated to the shared object library that you are building. If you want to arrange your software modularly like this, I would suggest defining the pybind module in a separate source file, and then passing the module object (which you have named handle) to functions that are responsible for adding class bindings.

Example:

module.cpp

PYBIND11_MODULE(m, handle) {
  handle.doc() = "I'm a docstring hehe";
  bind_a(handle);
  bind_b(handle);
}

a.cpp:

void bind_a(py::module& m) {
  m.def("setSomething", &test::setSomething);
}

b.cpp:

void bind_b(py::module& m) {
  py::class_<test::B>(m, "B")
    .def(py::init())
    .def("getSomething", &test::B::getSomething);
}
Related