简体   繁体   中英

boost.python exposing members of members

I have the following (simplified) code:

struct A
{
    int intVal;
    char charVal;
};

struct B
{
    A* someObj;
};

I will want to expose B::A::intVal , but the following dose not work:

class_<B>("someClass")
    .def_readonly("var", &B::A::intVar)
    ;

How can I do that?

I'm assuming you get B::someObj filled in somehow, or accessing a potentially random pointer will crash your code. Given that condition, you must expose both A and B to access A through B. I tested the following code and it compiles and works as expected:

#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>

struct A
{
  int intVal;
  char charVal;
};

struct B
{
  A* someObj;
};

static boost::shared_ptr<B> create_b() {
  boost::shared_ptr<B> retval(new B);
  retval->someObj = new A; //<< WARNING: leakage ahead
  retval->someObj->intVal = 3;
  retval->someObj->charVal = 'a';
  return retval;
}

BOOST_PYTHON_MODULE(test)
{
  using namespace boost::python;

  class_<A, A* >("A")
    .def_readonly("intVal", &A::intVal)
    .def_readonly("charVal", &A::charVal)
    ;

  class_<B, boost::shared_ptr<B> >("B")
    .def("__init__", make_constructor(&create_b))
    .def_readonly("someObj", &B::someObj)
    ;
}

My personal tip: If you have access to the definition of B , change the access pointer from B to A to use a boost::shared_ptr<A> . Boost.Python can handle that gracefully and reference count that properly. In that case, your code would look like this instead:

#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>

struct A
{
  int intVal;
  char charVal;
};

struct B
{
  boost::shared_ptr<A> someObj;
};

static boost::shared_ptr<B> create_b() {
  boost::shared_ptr<B> retval(new B);
  retval->someObj.reset(new A); //< no more leaks
  retval->someObj->intVal = 3;
  retval->someObj->charVal = 'a';
  return retval;
}

BOOST_PYTHON_MODULE(test)
{
  using namespace boost::python;

  class_<A, boost::shared_ptr<A> >("A")
    .def_readonly("intVal", &A::intVal)
    .def_readonly("charVal", &A::charVal)
    ;

  class_<B, boost::shared_ptr<B> >("B")
    .def("__init__", make_constructor(&create_b))
    .def_readonly("someObj", &B::someObj)
    ;
}

Under linux, you can compile and test this module like this:

$ g++ -I/usr/include/python2.7 -shared -fpic test.cc -lboost_python -lpython2.7 -o test.so

Run Python and check your code works:

>>> import test
>>> b = test.B()
>>> b.someObj.intVal
3
>>> b.someObj.charVal
'a'

B::A::intVar does not work because it is an ill-formed qualified-id. I've opted to breaking apart the problem into two parts: qualified-ids and exposing member of members in Boost.Python.


The following code introduces two structs into the same namespace. Thus, the qualified-id of B::A does not exists, as B does not reintroduce A as a nested identifier:

struct A
{
  int intVal;
  char charVal;
};

struct B
{
  A* someObj;
};

To introduce the A identifier in B , B either needs to:

  • Contain the definition of the A struct. (ie A becomes a nested struct).

     struct B { struct A // introduces B::A { int intVal; char charVal; }; A* someObj; }; 
  • Provide a typedef to A .

     struct A { int intVal; char charVal; }; struct B { typedef ::AA; // introduces B::A A* someObj; }; 

To directly expose a member of a member, Boost.Python is going to need a simple helper function, such as:

/// @brief Helper function that returns B.someObj.intVal.
int get_b_intval(const B& self)
{
  return self.someObj->intVal;
}

The helper function can be exposed as the Python B::var readonly property.

namespace python = boost::python;
python::class_<B>("someClass")
  .add_property("var", &get_b_intval)
  ;

Here is a the complete example:

#include <boost/python.hpp>

struct A
{
  int intVal;
  char charVal;
};

struct B
{
  A* someObj;
};


/// @brief Helper function that returns B.someObj.intVal.
int get_b_intval(const B& self)
{
  return self.someObj->intVal;
}

/// @brief Helper function to create a B object.
B make_B()
{
  static A a;
  a.intVal = 42;
  B b;
  b.someObj = &a;
  return b;
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<B>("someClass")
    .add_property("var", &get_b_intval)
    ;

  python::def("make_B", &make_B);
}

And its usage:

>>> import example
>>> b = example.make_B()
>>> print b.var
42
>>> b.var = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM