I want to know if there is any way to expose a C++ class to Python but without building an intermediate shared library.
Here is my desirable scenario. For example I have following C++ class:
class toto
{
public:
toto(int iValue1_, int iValue2_): iValue1(iValue1_), iValue2(iValue2_) {}
int Addition(void) const {if (!this) return 0; return iValue1 + iValue2;}
private:
int iValue1;
int iValue2;
};
I would like to convert somehow this class (or its intance) to a PyObject* in order to send it as paremter (args) to for example PyObject_CallObject:
PyObject* PyObject_CallObject(PyObject* wrapperFunction, PyObject* args)
In the other hand in my python side, I'll have a wrapperFunction which gets the pointer on my C++ class (or its instance) as parameter and it calls its methods or uses its properties:
def wrapper_function(cPlusPlusClass):
instance = cPlusPlusClass(4, 5)
result = instance.Addition()
As you can see, I don't really need/want to have a separate shared library or build a module by boost python. All that I need is to find a way to convert a C++ code to PyObject and send it to python. I cannot find a way to do that by C python libraries, boost or SWIG.
Do you have any idea?
Thanks for your help.
As far as I know, there is no easy way to accomplish this.
To extend Python with C++ with neither a module nor an intermediate library, it would require dynamically loading a library, then importing the functions. This approach is used by the ctypes
module. To accomplish the same with C++, one would need to write a ctypes
-like library that understood the C++ ABI for the target compiler(s).
To extend Python without introducing a module, an intermediate library could be created that provided a C API that wraps the C++ library. This intermediate library could then be used in Python through ctypes
. While it does not provide the exact calling syntax and does introduce an intermediate library, it would likely be less effort than building a ctypes
-like library that could interface directly with C++.
However, if an intermediate library is going to be introduced, it may be worthwhile to use Boost.Python, SWIG, or some other C++/Python language binding tool. While many of these tools will introduce the extension via a module, they often provide cleaner calling conventions, better error checking in the binding process, and may be easier to maintain.
I found my answer. Actually what I was searching was pretty similar to this answer (thanks moooeeeep for his comment):
Exposing a C++ class instance to a python embedded interpreter
Following C++ class (Attention! default constructor is mandatory):
class TwoValues
{
public:
TwoValues(void): iValue1(0), iValue2(0) {}
TwoValues(int iValue1, int iValue2): iValue1(iValue1_), iValue2(iValue2_) {}
int Addition(void) const {if (!this) return 0; return iValue1 + iValue2;}
public:
int iValue1;
int iValue2;
};
could be exposed by boost by following macro:
BOOST_PYTHON_MODULE(ModuleTestBoost)
{
class_<TwoValues>("TwoValues")
.def("Addition", &TWOVALUES::Addition)
.add_property("Value1", &TWOVALUES::iValue1)
.add_property("Value2", &TWOVALUES::iValue2);
};
In the other hand I have a python function defined in python_script.py
which takes an instance of this class and do something. For example:
def wrapper_function(instance):
result = instance.Addition()
myfile = open(r"C:\...\testboostexample.txt", "w")
output = 'First variable is {0}, second variable is {1} and finally the addition is {2}'.format(instance.Value1, instance.Value2, result)
myfile .write(output)
myfile .close()
Then in C++ side, I can call this function by sending at the same time the instance of my class, like this:
Py_Initialize();
try
{
TwoValues instance(5, 10);
initModuleTestBoost();
object python_script = import("python_script");
object wrapper_function = python_script.attr("wrapper_function");
wrapper_function(&instance);
}
catch (error_already_set)
{
PyErr_Print();
}
Py_Finalize();
Advantages:
- I don't need build any shared library or binary
- As I'm using Boost, I don't need to be worry about memory management
& reference counting
- I don't use shared boost pointer (boost::shared_ptr) to point to the
instance of my class