I have an in-house library written in C++ that I'm currently working on extending into Python. I started this task with Boost.Python in mind, but I'm open to alternatives.
Currently I have a C++ function that needs to accept a Python class instance, and then use the methods of this object to perform certain tasks. The idea is for the Python user to never need to deal with C++. They're expected to create this Python object from a Python template/example class that I will provide, with preset method names which I can assume to be there in my C++ library.
The interface exposed to the Python user looks like:
class Foo(object):
def __init__(self, args):
"""create an instance of this class with instance-specific attributes"""
def Bar1(self, a, b, c):
"""do something with the given integers a, b and c"""
pass
def Bar2(self, a, b, c):
"""do something else with the given integers a, b and c"""
pass
import mylib
cheese = mylib.Wine()
cheese.do_something(Foo)
In C++, the corresponding code looks like:
#include <boost/python.h>
#include <Python.h>
class Wine {
public:
Wine() {};
~Wine() {};
static void do_something(boost::python::object *Foo) {
int a = 1;
int b = 2;
int c = 3;
Foo->attr("Bar1")(a, b, c);
Foo->attr("Bar2")(a, b, c);
};
};
BOOST_PYTHON_MODULE(mylib)
{
using namespace boost::python;
class_<Wine>("Wine")
.def("do_something", &Wine::do_something);
};
I have successfully compiled this code and verified that the C++ class called Wine is really exposed to Python and I can access its member functions. If I write a member function called "greet()" that only returns "Hello, world!", it works perfectly.
I need to stress here the importance of passing an instance of Foo. I do not have the luxury to simply import the Foo module into C++ code and create an instance of Foo in C++. The object that I want to receive from the Python user has attributes I need to use that is specific to the instance, and not to the class itself.
Problem is that I can't figure out how to pass a Python instance into do_something, such that it will appear in C++ as a callable boost::python::object. The above code returns the following C++ signature mismatch error:
Boost.Python.ArgumentError: Python argument types in
Wine.do_something(Wine, Foo)
did not match C++ signature:
do_something(boost::python::api::object*)
Two days of perusing the internet for answers has yielded no progress. There seems to be a wealth of information on how to pass C++ classes into Python, but I was unable to find information on doing this in the opposite direction. Would really appreciate some guidance here.
Thanks!
A PyObject is in fact just a Python object at the C level. And since integers in Python are objects, they are also PyObject s. It doesn't matter whether it was written in Python or in C, it is a PyObject at the C level regardless.
A function can take multiple arguments, these arguments can be objects, variables(of same or different data types) and functions.
Functions can be passed around in Python. In fact there are functions built into Python that expect functions to be given as one or more of their arguments so that they can then call them later.
The __module__ property is intended for retrieving the module where the function was defined, either to read the source code or sometimes to re-import it in a script.
There are two errors in the initial code:
self
and an instance of Foo
) to the static Wine::do_something()
C++ function that only accepts one argument. To resolve this, when exposing the Wine
class, the Python Wine.do_something()
member function needs to be set as static via the boost::python::class_::staticmethod()
member function. When exposed as a static method, Boost.Python will no longer pass the self
instance argument.PyObject*
), Boost.Python provides a higher-level notation boost::python::object
class that is often passed around by value or reference. Internally, this class interacts with a boost::python::handle
that performs smart pointer management for PyObject
.Here is a complete Python extension based on the original code:
#include <boost/python.hpp>
class Wine
{
public:
static void do_something(boost::python::object object)
{
int a = 1;
int b = 2;
int c = 3;
object.attr("Bar1")(a, b, c);
object.attr("Bar2")(a, b, c);
};
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<Wine>("Wine")
.def("do_something", &Wine::do_something)
.staticmethod("do_something")
;
};
Interactive usage:
>>> class Foo(object):
... def Bar1(self, a, b, c):
... print "Bar1", locals()
... def Bar2(self, a, b, c):
... print "Bar2", locals()
...
>>> import example
>>> cheese = example.Wine()
>>> cheese.do_something(Foo())
Bar1 {'a': 1, 'c': 3, 'b': 2, 'self': <__main__.Foo object at 0xb6b0f2ac>}
Bar2 {'a': 1, 'c': 3, 'b': 2, 'self': <__main__.Foo object at 0xb6b0f2ac>}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With