Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to pass a C++ object to another C++ object with Boost.Python
    primarykey
    data
    text
    <p>I have some C++ code that defines two classes, A and B. B takes an instance of A during construction. I have wrapped A with Boost.Python so that Python can create instances of A, as well as subclasses. I want to do the same with B.</p> <pre class="lang-cpp prettyprint-override"><code>class A { public: A(long n, long x, long y) : _n(n), _x(x), _y(y) {}; long get_n() { return _n; } long get_x() { return _x; } long get_y() { return _y; } private: long _n, _x, _y; }; class B { public: B(A a) : _a(a) {}; doSomething() { ... }; private: A _a; }; </code></pre> <p>While wrapping B, I needed to work out how to pass an instance of A to B's constructor. I did some digging and the <a href="http://misspent.wordpress.com/2009/09/27/how-to-write-boost-python-converters/">solution</a> I found was to write a "converter" class:</p> <pre class="lang-cpp prettyprint-override"><code>struct A_from_python_A { static void * convertible(PyObject* obj_ptr) { // assume it is, for now... return obj_ptr; } // Convert obj_ptr into an A instance static void construct(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { // extract 'n': PyObject * n_ptr = PyObject_CallMethod(obj_ptr, (char*)"get_n", (char*)"()"); long n_val = 0; if (n_ptr == NULL) { cout &lt;&lt; "... an exception occurred (get_n) ..." &lt;&lt; endl; } else { n_val = PyInt_AsLong(n_ptr); Py_DECREF(n_ptr); } // [snip] - also do the same for x, y // Grab pointer to memory into which to construct the new A void* storage = ( (boost::python::converter::rvalue_from_python_storage&lt;A&gt;*) data)-&gt;storage.bytes; // in-place construct the new A using the data // extracted from the python object new (storage) A(n_val, x_val, y_val); // Stash the memory chunk pointer for later use by boost.python data-&gt;convertible = storage; } // register converter functions A_from_python_A() { boost::python::converter::registry::push_back( &amp;convertible, &amp;construct, boost::python::type_id&lt;A&gt;()); } }; </code></pre> <p>Then I register this with:</p> <pre class="lang-cpp prettyprint-override"><code>BOOST_PYTHON_MODULE(interpolation_ext) { // register the from-python converter for A A_from_python_A(); class_&lt;A&gt;("A", init&lt;long, long, long&gt;()) ; class_&lt;B&gt;("B", init&lt;object&gt;()) ; } </code></pre> <p>Convertible and construct are methods that answer the "is this convertible?" and "how to convert?" questions respectively. I have observed that the construct() method is non-trivial - it has to reach into A's PyObject*, extract all relevant fields, then rebuild a C++ instance that it then passes to B's constructor. Because A contains some private fields, it has to do this via public access mechanisms (whereas with a pure Python object it wouldn't have to, right?). This seems to work.</p> <p>However, <em>is the field extraction in the 'construct' function really necessary?</em> It seems laborious. If A is a compound object, it could get very complicated, and possibly require one converter to invoke another. I perhaps understand the requirement if A is a Python class, but if the A instance originated from the C++ side, is there a way to determine that this is the case, and then simply get a handle (e.g. pointer) to this 'native' object, as a shortcut?</p> <p>Here's the associated python code:</p> <pre class="lang-python prettyprint-override"><code>from my_ext import A, B a = A(1,2,3) b = B(a) b.doSomething() </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload