exposing boost uuid with SWIG - python

I have my existing working code that depends on boost::uuids::uuid. Now I an trying to generate a python module out of it. SWIG is successfully generating all important classes and functions. But I am facing problem with the functions that takes or returns boost uuid.
I want to convert between boost uuid and python uuid. Is there any uuid.i that I can use ? I see there is an uuid python module.I understand I can import that module from an uuid.i with PyImport_ImportModule("uuid").
But how to instantiate and use the python's uuid class inside typemap ?

This is fairly straight forward to do any you're on the right lines with the PyImport_ImportModule call. What you need to do is figure out how you're going to marshal the UUIDs between the two types and then write one typemap for each direction. I ended up going via string representations since that's the simplest portable way to do it in my view. That's using uuid_io.hpp's IO operators in both directions.
When we wrap it like this Python code never see's the boost type at all and vice-versa.
I've assumed you're targeting Python 3.4 here, but everything I've done should be simple adjust to target older Python with too.
First I put together a C++ header file to demonstrate the wrapping I implemented:
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <iostream>
inline boost::uuids::uuid testout() {
static boost::uuids::random_generator gen;
return gen();
}
inline void testin(const boost::uuids::uuid& in) {
std::cout << in << "\n";
}
Nothing clever there, just one function each for each direction of passing the objects.
Next I wrote some Python to exercise the interface I wanted to produce:
import test
import uuid
a=test.testout()
print(type(a))
print(a)
b=uuid.uuid4()
print(type(b))
print(b)
test.testin(a)
test.testin(b)
And finally a SWIG interface that would generate this module, once I've written the actual UUID wrapping.
%module test
%{
#include "test.hh"
%}
%include "boost_uuid.i"
%include "test.hh"
With all this in place we can now write an implementation boost_uuid.i that works for our scenario:
%{
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid.hpp>
#include <sstream>
namespace {
PyObject *py_uuid = nullptr;
}
%}
%init %{
py_uuid = PyImport_ImportModule("uuid"); // Handle error
%}
%typemap(in) const boost::uuids::uuid& (boost::uuids::uuid tmp) {
PyObject *str = PyObject_Str($input);
assert(str); // TODO: check properly
const char *uuid_str = PyUnicode_AsUTF8(str); // Note: Python 3.x, adjust as needed
assert(uuid_str); // TODO: check me
std::istringstream in(uuid_str);
Py_DECREF(str);
in >> tmp; // TODO: Check return!
$1 = &tmp;
}
%typemap(out) boost::uuids::uuid {
// Check this actually works!
static PyObject *uuid_ctor = PyObject_GetAttrString(py_uuid, "UUID");
std::ostringstream out;
out << $1;
PyObject *str = PyUnicode_DecodeUTF8(out.str().c_str(), out.str().size(), NULL);
// Theoretically this string conversion could have just failed
$result = PyObject_CallFunctionObjArgs(uuid_ctor, str, NULL);
Py_DECREF(str);
}
I did contemplate using boost::python to simplify some of this code a little since it's boost we're wrapping. In the end I didn't bother with that though.
You could also use boost's lexical_cast to avoid the stringstream that I've used. That's largely a matter of taste.
None of this is going to be super high performance code, but then when you're passing across language boundaries it also isn't likely to be the bottleneck in your system anyway. You could use the byte-by-byte access that both boost and Python's UUID module give as well and make it just be a direct copy operation, but I avoided that because of the requirement to consider endianness which increased the complexity.
Note:
No proper error handling
No typemap for pass by value on boost UUID input types.
No typemap for pass by non-const reference for input.
All of those should be fairly easy to add as required using this as a starting point.
This compiles and runs as expected:
swig3.0 -c++ -python -py3 -Wall test.i
g++ -g -std=c++1y -shared test_wrap.cxx -o _test.so -I/usr/include/python3.4 -Wall -Wextra -lpython3.4m
python3.4 run.py
<class 'uuid.UUID'>
b37c9285-f055-4f08-b4e9-4a238be3b09d
<class 'uuid.UUID'>
cf1071e6-2e7f-45af-8920-a68290ee61d4
b37c9285-f055-4f08-b4e9-4a238be3b09d
cf1071e6-2e7f-45af-8920-a68290ee61d4

Related

SWIG C++/Python binding and support of conditional members with std::enable_if

Sorry for the long title, here is what I'm trying to achieve: I have a small C++ class with a bool template parameter which, when true, disables its setter methods using std::enable_if. Here is a simplified example:
template< bool IS_CONST >
class Handle
{
public:
Handle(void) : m_value(0) { }
Handle(int value) : m_value(value) { }
template < bool T = IS_CONST, typename COMPILED = typename std::enable_if< T == false >::type >
void set(int value)
{
m_value = value;
}
int get(void) const
{
return m_value;
}
private:
int m_value;
};
This code compiles an work as expected: Handle< true > doesn't have the set method, Handle< false > has it.
Now I'm trying to bind this to Python using SWIG. I'm using the following file to generate the binding:
%module core
%include "Handle.h"
%template(NonConstHandle) Handle< false >;
%template(ConstHandle) Handle< false >;
%{
#include "Test.h"
%}
SWIG generates the module without complaining, it compiles fine, but the set method is never bound, even in the specialized NonConstHandle. e.g. the following Python test fails with AttributeError: 'NonConstHandle' object has no attribute 'set' :
import core
handle = core.NonConstHandle()
assert(handle.get() == 0)
handle.set(1)
assert(handle.get() == 1)
const_handle = core.ConstHandle()
assert(const_handle .get() == 0)
try:
const_handle .set(1)
print("this should not print")
except:
pass
print("all good")
When I searched on the subject, I found lots of things related to enable_if and SWIG which leads me to think that it's supported, but I can't figure out why set is not generated, although there's no error / warning emitted by SWIG...
Any help appreciated !
Regards
The problem here is that every time you create a template in C++ you need at least one %template directive in your SWIG interface in order to make it have any effect on the generated wrapper.
When people vaguely hint that std::enable_if works they typically mean two things. Firstly that it parses ok and secondly that %template works for them. Both things are true here.
Since you've used SFINAE inside your template class with a template function you need one %template for each. Otherwise the set member is ignored totally, as you've seen. Side stepping the SFINAE/enable_if bit of your question an example of template functions inside template classes is a good place to start.
So we can change your .i file to look something like this:
%module test
%{
#include "test.h"
%}
%include "test.h"
// Be explicit about what 'versions' of set to instantiate in our wrapper
%template(set) Handle::set<false, void>;
%template(NonConstHandle) Handle<false>;
%template(ConstHandle) Handle<true>;
The problem is that (having fixed a few minor bugs in it) your test python now hits "this should not print", because we've generated a (totally legal) set() function even in the const case by explicitly spelling out the template parameters instead of getting them deduced.
So we've generated code to call:
Handle<true>::set<false, void>(int);
Which uh works in this instance, because it can compile just not in the intuitive way.
I'm not aware of a way of making the deduction happen here (which is a shame, because they're defaulted so it should be possible right? - Maybe one for a patch into SWIG trunk, although doing both the defaulting and the SFINAE is going to be tricky)
Fortunately there is a simple workaround using %ignore to drop the version we don't want too:
%module test
%{
#include "test.h"
%}
%include "test.h"
%template(set) Handle::set<false, void>;
%ignore Handle<true>::set;
%template(NonConstHandle) Handle<false>;
%template(ConstHandle) Handle<true>;
Which does then generate the code you expected.
It's worth noting that often it's simpler to explicitly spell out the way you want complex templated code to work when generating wrappers - you typically need extra helpers or tweaks to the interface to get it to work in Python in the way you hope for. So you could also solve your example by doing something like this:
%module test
%{
#include "test.h"
%}
template <bool>
class Handle {
public:
Handle(void);
Handle(int value);
int get(void) const;
};
template<>
class Handle<false>
{
public:
Handle(void);
Handle(int value);
void set(int value);
int get(void) const;
};
%template(NonConstHandle) Handle<false>;
%template(ConstHandle) Handle<true>;
Or a similar trick:
%module test
%{
#include "test.h"
typedef Handle<true> ConstHandle;
typedef Handle<false> NonConstHandle;
%}
struct ConstHandle {
ConstHandle(void);
ConstHandle(int value);
int get(void) const;
};
struct NonConstHandle
{
NonConstHandle(void);
NonConstHandle(int value);
void set(int value);
int get(void) const;
};
Although note that in this last case you'll need to use %apply as well if you want to use the templates as arguments in/out of functions.

Passing a C++ object to Python

This question is about how to pass a C++ object to a python function that is called in a (C++) embedded Python interpreter.
The following C++ class (MyClass.h) is designed for testing:
#ifndef MyClassH
#define MyClassH
#include <string>
using std::string;
class MyClass
{
public:
MyClass(const string& lbl): label(lbl) {}
~MyClass(){}
string getLabel() {return label;}
private:
string label;
};
#endif
A python module, exposing the C++ class, can be generated by the following Swig interface file:
%module passmetopython
%{ #include "MyClass.h" %}
%include "std_string.i"
//Expose to Python
%include "MyClass.h"
Below is a Python script using the python module
import passmetopython as pmtp
def execute(obj):
#This function is to be called from C/C++, with a
#MyClass object as an argument
print ("Entering execute function")
lbl = obj.getLabel();
print ("Printing from within python execute function. Object label is: " + lbl)
return True
def main():
c = pmtp.MyClass("Test 1")
retValue = execute(c)
print("Return value: " + str(retValue))
#Test function from within python
if __name__ == '__main__':
main()
This question is about how to get the python execute() function working, when called from c++, with a C++ object as an argument.
The following C++ program was written to test the functions (minimum amount of error checking):
#include "Python.h"
#include <iostream>
#include <sstream>
#include "MyClass.h"
using namespace std;
int main()
{
MyClass obj("In C++");
cout << "Object label: \"" << obj.getLabel() << "\"" << endl;
//Setup the Python interpreter and eventually call the execute function in the
//demo python script
Py_Initialize();
//Load python Demo script, "passmetopythonDemo.py"
string PyModule("passmetopythonDemo");
PyObject* pm = PyUnicode_DecodeFSDefault(PyModule.c_str());
PyRun_SimpleString("import sys");
stringstream cmd;
cmd << "sys.path.append(\"" << "." << "\")";
PyRun_SimpleString(cmd.str().c_str());
PyObject* PyModuleP = PyImport_Import(pm);
Py_DECREF(pm);
//Now create PyObjects for the Python functions that we want to call
PyObject* pFunc = PyObject_GetAttrString(PyModuleP, "execute");
if(pFunc)
{
//Setup argument
PyObject* pArgs = PyTuple_New(1);
//Construct a PyObject* from long
PyObject* pObj(NULL);
/* My current attempt to create avalid argument to Python */
pObj = PyLong_FromLong((long) &obj);
PyTuple_SetItem(pArgs, 0, pObj);
/***** Calling python here *****/
cout<<endl<<"Calling function with an MyClass argument\n\n";
PyObject* res = PyObject_CallObject(pFunc, pArgs);
if(!res)
{
cerr << "Failed calling function..";
}
}
return 0;
}
When running the above code, the execute() python function, with a MyClass object as an argument, fails and returns NULL. However, the Python function is entered, as I can see the output (Entering execute function) in the console output, indicating that the object passed is not, indeed, a valid MyClass object.
There are a lot of examples on how to pass simple types, like ints, doubles or string types to Python from C/C++. But there are very few example showing how to pass a C/C++ object/ pointer, which is kind of puzzling.
The above code, with a CMake file, can be checked out from github:
https://github.com/TotteKarlsson/miniprojects/tree/master/passMeToPython
This code is not to use any boost python or other API's. Cython sounds interesting though, and if it can be used to simplify on the C++ side, it could be acceptable.
This is a partial answer to my own question. I'm saying partial, because I do believe there is a better way.
Building on this post http://swig.10945.n7.nabble.com/Pass-a-Swig-wrapped-C-class-to-embedded-Python-code-td8812.html
I generated the swig runtime header, as described here, section 15.4: http://www.swig.org/Doc2.0/Modules.html#Modules_external_run_time
Including the generated header in the C++ code above, allow the following code to be written:
PyObject* pObj = SWIG_NewPointerObj((void*)&obj, SWIG_TypeQuery("_p_MyClass"), 0 );
This code is using information from the Swig python wrap source files, namely the "swig" name of the type MyClass, i.e. _p_MyClass.
With the above PyObject* as an argument to the PyObject_CallObject function, the python execute() function in the code above executes fine, and the Python code, using the generated python module, do have proper access to the MyClass objects internal data. This is great.
Although the above code illustrate how to pass, and retrieve data between C++ and Python in a quite simple fashion, its not ideal, in my opinion.
The usage of the swig header file in the C++ code is really not that pretty, and in addition, it requires a user to "manually" look into swig generated wrapper code in order to find the "_p_MyClass" code.
There must be a better way!? Perhaps something should be added to the swig interface file in order to get this looking nicer?
PyObject *pValue;
pValue = PyObject_CallMethod(pInstance, "add","(i)",x);
if (pValue)
Py_DECREF(pValue);
else
PyErr_Print();

Disable generation of implicit type checking code by swig for python-C++ interface

For the following simple function:
#include <iostream>
void Echo(int no) {
std::cout << "no: " << no << std::endl;
}
I have the following swig interface file:
%module example
%{
#include "example.h"
%}
%include "example.h"
I can generate a wrapper using swig and test it as:
from example import Echo
import numpy as np
no = 2
Echo( np.int(no) ) # OK
Echo( np.int32(no) ) # Error
Echo( np.int64(no) ) # Error
swig generates type checking wrapper code which results in error in 2nd and 3rd call. This is nice but is there any way to override/disable generation of this type checking code for the type casting which are legally ok?
This can be controlled by passing castmode or nocastmode option to swig when generating wrappers. The default is nocastmode. This does not require to define any typemaps and is also independent of python2/python3.
Example: To allow the type castings in above example, one can generate the wrapper as:
swig -c++ -python -castmode example.i
You can write a typemap that has the semantics you want using SWIG. The reason the default int typemap doesn't work for you is that it explicitly calls PyLong_Check, which requires that your input be an int, or a subtype of it. That's not the case for the numpy types, so it gets rejected, however if we skip that check and jump straight to PyLong_AsLong then it'll end up implicitly calling the __int__ method to make the conversion possible.
So changing your SWIG module to be this:
%module example
%{
#include "example.h"
%}
%typemap(in) int %{
// Skips PyLong_Check and calls PyLong_AsLong, which calls __int__ as needed
$1 = PyLong_AsLong($input);
// But be aware of overflow semantics here. And note py3/py2 differences in PyLong/PyInt
//
%}
%include "example.h"
Is sufficient to make your examples pass.
Note that you could also have achieved the same thing in SWIG by using its overload resolution mechanisms.

Creating variable of typedefd type in SWIG

I have a project with SWIG set up generating python code. I have a typedef of std::string to Message and a say(Message) function. I am able to call say with a string in python. I want to be able to make a variable of the type Message and the Message type is exported to the library, but not the python wrapper. Here are my files:
test.h
#include <string>
#include <iostream>
typedef std::string Message
void say(Message s);
test.cpp
#include "test.h"
void say(Message s)
{
std::cout << s << std::endl;
}
test.i
%module test
%{
#include "test.h"
%}
typedef std::string Message;
%include "std_string.i"
%include "test.h"
Python example
import test
test.say('a')
# >>> a
# What I want to be able to do
msg = test.Message('a')
# >>> Traceback (most recent call last):
# >>> File "<stdin>", line 1, in <module>
# >>> AttributeError: module 'test' has no attribute 'Message'
My actual use case also involves typedefs to other types (primarily enums) and I'm curious if those cases take any different handling. I believe I could wrap the objects in a class for the SWIG bindings and then modify the SWIG-generated classes (or maybe use a SWIG typemap), but I feel that that's a bit of a roundabout solution to what I would think is a common situation.
I thought this may be an issue with having access to the code in the string header, but I run into the same issue if I try to typedef something like an int.
My best approach so far has to been a wrapper template:
template<typename T>
class Wrapper
{
public:
Wrapper(T x) : data(x){};
T data;
T operator()() { return data; };
};
And a corresponding %template directive in test.i:
%template(Message) Wrapper<std::string>;
Unfortunately, this seems to have a few drawbacks so far:
You have to actually call operator(), ie, test.Message('a')() needs to be called
You need to either use some conditional compilation or name the wrapper something different from the typedef; otherwise, test.say won't accept the wrapper or a string and thus it is not able to be used at all.
It doesn't seem to work with enums with an error on construction.
I also thought that I may be clever and change operator* to just return what was being wrapped, but it looks like SWIG wrapped what was returned anyway.
In general in SWIG typedefs should "just work". (There's one exception where I know they regularly don't behave as expected and that's when instantiating templates, but this isn't an issue here.)
In your example I think that your problem is simply the visibility of the typedef relative to the definition of std::string. If you change your .i file to be:
%module test
%{
#include "test.h"
%}
%include "std_string.i"
typedef std::string Message;
%include "test.h"
Or
%module test
%{
#include "test.h"
%}
%include "std_string.i"
%include "test.h"
Then I'd expect your example code to work.
In terms of std::string vs const char* there should be very little observable behavior difference for Python users. Python's native string type will get converted correctly and automatically for either, so the rule I'd stick to is that if you're using C++ then use the C++ type unless there's an overriding reason not to. POD-ness (or lack thereof) is not likely to be the expensive part of your interface and even less likely to be the bottleneck in your performance.

calling c functions from python [duplicate]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
Improve this question
What would be the quickest way to construct a Python binding to a C or C++ library?
(I am using Windows if this matters.)
ctypes module is part of the standard library, and therefore is more stable and widely available than swig, which always tended to give me problems.
With ctypes, you need to satisfy any compile time dependency on python, and your binding will work on any python that has ctypes, not just the one it was compiled against.
Suppose you have a simple C++ example class you want to talk to in a file called foo.cpp:
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Since ctypes can only talk to C functions, you need to provide those declaring them as extern "C"
extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo){ foo->bar(); }
}
Next you have to compile this to a shared library
g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
And finally you have to write your python wrapper (e.g. in fooWrapper.py)
from ctypes import cdll
lib = cdll.LoadLibrary('./libfoo.so')
class Foo(object):
def __init__(self):
self.obj = lib.Foo_new()
def bar(self):
lib.Foo_bar(self.obj)
Once you have that you can call it like
f = Foo()
f.bar() #and you will see "Hello" on the screen
You should have a look at Boost.Python. Here is the short introduction taken from their website:
The Boost Python Library is a framework for interfacing Python and
C++. It allows you to quickly and seamlessly expose C++ classes
functions and objects to Python, and vice-versa, using no special
tools -- just your C++ compiler. It is designed to wrap C++ interfaces
non-intrusively, so that you should not have to change the C++ code at
all in order to wrap it, making Boost.Python ideal for exposing
3rd-party libraries to Python. The library's use of advanced
metaprogramming techniques simplifies its syntax for users, so that
wrapping code takes on the look of a kind of declarative interface
definition language (IDL).
There is also pybind11, which is like a lightweight version of Boost.Python and compatible with all modern C++ compilers:
https://pybind11.readthedocs.io/en/latest/
The quickest way to do this is using SWIG.
Example from SWIG tutorial:
/* File : example.c */
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
Interface file:
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}
extern int fact(int n);
Building a Python module on Unix:
swig -python example.i
gcc -fPIC -c example.c example_wrap.c -I/usr/local/include/python2.7
gcc -shared example.o example_wrap.o -o _example.so
Usage:
>>> import example
>>> example.fact(5)
120
Note that you have to have python-dev. Also in some systems python header files will be in /usr/include/python2.7 based on the way you have installed it.
From the tutorial:
SWIG is a fairly complete C++ compiler with support for nearly every language feature. This includes preprocessing, pointers, classes, inheritance, and even C++ templates. SWIG can also be used to package structures and classes into proxy classes in the target language — exposing the underlying functionality in a very natural manner.
I started my journey in the Python <-> C++ binding from this page, with the objective of linking high level data types (multidimensional STL vectors with Python lists) :-)
Having tried the solutions based on both ctypes and boost.python (and not being a software engineer) I have found them complex when high level datatypes binding is required, while I have found SWIG much more simple for such cases.
This example uses therefore SWIG, and it has been tested in Linux (but SWIG is available and is widely used in Windows too).
The objective is to make a C++ function available to Python that takes a matrix in form of a 2D STL vector and returns an average of each row (as a 1D STL vector).
The code in C++ ("code.cpp") is as follow:
#include <vector>
#include "code.h"
using namespace std;
vector<double> average (vector< vector<double> > i_matrix) {
// Compute average of each row..
vector <double> averages;
for (int r = 0; r < i_matrix.size(); r++){
double rsum = 0.0;
double ncols= i_matrix[r].size();
for (int c = 0; c< i_matrix[r].size(); c++){
rsum += i_matrix[r][c];
}
averages.push_back(rsum/ncols);
}
return averages;
}
The equivalent header ("code.h") is:
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
We first compile the C++ code to create an object file:
g++ -c -fPIC code.cpp
We then define a SWIG interface definition file ("code.i") for our C++ functions.
%module code
%{
#include "code.h"
%}
%include "std_vector.i"
namespace std {
/* On a side note, the names VecDouble and VecVecdouble can be changed, but the order of first the inner vector matters! */
%template(VecDouble) vector<double>;
%template(VecVecdouble) vector< vector<double> >;
}
%include "code.h"
Using SWIG, we generate a C++ interface source code from the SWIG interface definition file..
swig -c++ -python code.i
We finally compile the generated C++ interface source file and link everything together to generate a shared library that is directly importable by Python (the "_" matters):
g++ -c -fPIC code_wrap.cxx -I/usr/include/python2.7 -I/usr/lib/python2.7
g++ -shared -Wl,-soname,_code.so -o _code.so code.o code_wrap.o
We can now use the function in Python scripts:
#!/usr/bin/env python
import code
a= [[3,5,7],[8,10,12]]
print a
b = code.average(a)
print "Assignment done"
print a
print b
For modern C++, use cppyy:
http://cppyy.readthedocs.io/en/latest/
It's based on Cling, the C++ interpreter for Clang/LLVM. Bindings are at run-time and no additional intermediate language is necessary. Thanks to Clang, it supports C++17.
Install it using pip:
$ pip install cppyy
For small projects, simply load the relevant library and the headers that you are interested in. E.g. take the code from the ctypes example is this thread, but split in header and code sections:
$ cat foo.h
class Foo {
public:
void bar();
};
$ cat foo.cpp
#include "foo.h"
#include <iostream>
void Foo::bar() { std::cout << "Hello" << std::endl; }
Compile it:
$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
and use it:
$ python
>>> import cppyy
>>> cppyy.include("foo.h")
>>> cppyy.load_library("foo")
>>> from cppyy.gbl import Foo
>>> f = Foo()
>>> f.bar()
Hello
>>>
Large projects are supported with auto-loading of prepared reflection information and the cmake fragments to create them, so that users of installed packages can simply run:
$ python
>>> import cppyy
>>> f = cppyy.gbl.Foo()
>>> f.bar()
Hello
>>>
Thanks to LLVM, advanced features are possible, such as automatic template instantiation. To continue the example:
>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
>>> v.push_back(f)
>>> len(v)
1
>>> v[0].bar()
Hello
>>>
Note: I'm the author of cppyy.
pybind11 minimal runnable example
pybind11 was previously mentioned at https://stackoverflow.com/a/38542539/895245 but I would like to give here a concrete usage example and some further discussion about implementation.
All and all, I highly recommend pybind11 because it is really easy to use: you just include a header and then pybind11 uses template magic to inspect the C++ class you want to expose to Python and does that transparently.
The downside of this template magic is that it slows down compilation immediately adding a few seconds to any file that uses pybind11, see for example the investigation done on this issue. PyTorch agrees. A proposal to remediate this problem has been made at: https://github.com/pybind/pybind11/pull/2445
Here is a minimal runnable example to give you a feel of how awesome pybind11 is:
class_test.cpp
#include <string>
#include <pybind11/pybind11.h>
struct ClassTest {
ClassTest(const std::string &name, int i) : name(name), i(i) { }
void setName(const std::string &name_) { name = name_; }
const std::string getName() const { return name + "z"; }
void setI(const int i) { this->i = i; }
const int getI() const { return i + 1; }
std::string name;
int i;
};
namespace py = pybind11;
PYBIND11_PLUGIN(class_test) {
py::module m("my_module", "pybind11 example plugin");
py::class_<ClassTest>(m, "ClassTest")
.def(py::init<const std::string &, int>())
.def("setName", &ClassTest::setName)
.def("getName", &ClassTest::getName)
.def_readwrite("name", &ClassTest::name)
.def("setI", &ClassTest::setI)
.def("getI", &ClassTest::getI)
.def_readwrite("i", &ClassTest::i);
return m.ptr();
}
class_test_main.py
#!/usr/bin/env python3
import class_test
my_class_test = class_test.ClassTest("abc", 1);
print(my_class_test.getName())
print(my_class_test.getI())
my_class_test.setName("012")
my_class_test.setI(2)
print(my_class_test.getName())
print(my_class_test.getI())
assert(my_class_test.getName() == "012z")
assert(my_class_test.getI() == 3)
Compile and run:
#!/usr/bin/env bash
set -eux
sudo apt install pybind11-dev
g++ `python3-config --cflags` -shared -std=c++11 -fPIC class_test.cpp \
-o class_test`python3-config --extension-suffix` `python3-config --libs`
./class_test_main.py
Stdout output:
abcz
2
012z
3
If we tried to use a wrong type as in:
my_class_test.setI("abc")
it blows up as expected:
Traceback (most recent call last):
File "/home/ciro/test/./class_test_main.py", line 9, in <module>
my_class_test.setI("abc")
TypeError: setI(): incompatible function arguments. The following argument types are supported:
1. (self: my_module.ClassTest, arg0: int) -> None
Invoked with: <my_module.ClassTest object at 0x7f2980254fb0>, 'abc'
This example shows how pybind11 allows you to effortlessly expose the ClassTest C++ class to Python!
Notably, Pybind11 automatically understands from the C++ code that name is an std::string, and therefore should be mapped to a Python str object.
Compilation produces a file named class_test.cpython-36m-x86_64-linux-gnu.so which class_test_main.py automatically picks up as the definition point for the class_test natively defined module.
Perhaps the realization of how awesome this is only sinks in if you try to do the same thing by hand with the native Python API, see for example this example of doing that, which has about 10x more code: https://github.com/cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.c On that example you can see how the C code has to painfully and explicitly define the Python class bit by bit with all the information it contains (members, methods, further metadata...). See also:
Can python-C++ extension get a C++ object and call its member function?
Exposing a C++ class instance to a python embedded interpreter
A full and minimal example for a class (not method) with Python C Extension?
Embedding Python in C++ and calling methods from the C++ code with Boost.Python
Inheritance in Python C++ extension
pybind11 claims to be similar to Boost.Python which was mentioned at https://stackoverflow.com/a/145436/895245 but more minimal because it is freed from the bloat of being inside the Boost project:
pybind11 is a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing C++ code. Its goals and syntax are similar to the excellent Boost.Python library by David Abrahams: to minimize boilerplate code in traditional extension modules by inferring type information using compile-time introspection.
The main issue with Boost.Python—and the reason for creating such a similar project—is Boost. Boost is an enormously large and complex suite of utility libraries that works with almost every C++ compiler in existence. This compatibility has its cost: arcane template tricks and workarounds are necessary to support the oldest and buggiest of compiler specimens. Now that C++11-compatible compilers are widely available, this heavy machinery has become an excessively large and unnecessary dependency.
Think of this library as a tiny self-contained version of Boost.Python with everything stripped away that isn't relevant for binding generation. Without comments, the core header files only require ~4K lines of code and depend on Python (2.7 or 3.x, or PyPy2.7 >= 5.7) and the C++ standard library. This compact implementation was possible thanks to some of the new C++11 language features (specifically: tuples, lambda functions and variadic templates). Since its creation, this library has grown beyond Boost.Python in many ways, leading to dramatically simpler binding code in many common situations.
pybind11 is also the only non-native alternative hightlighted by the current Microsoft Python C binding documentation at: https://learn.microsoft.com/en-us/visualstudio/python/working-with-c-cpp-python-in-visual-studio?view=vs-2019 (archive).
Tested on Ubuntu 18.04, pybind11 2.0.1, Python 3.6.8, GCC 7.4.0.
I think cffi for python can be an option.
The goal is to call C code from Python. You should be able to do so
without learning a 3rd language: every alternative requires you to
learn their own language (Cython, SWIG) or API (ctypes). So we tried
to assume that you know Python and C and minimize the extra bits of
API that you need to learn.
http://cffi.readthedocs.org/en/release-0.7/
I love cppyy, it makes it very easy to extend Python with C++ code, dramatically increasing performance when needed.
It is powerful and frankly very simple to use,
here it is an example of how you can create a numpy array and pass it to a class member function in C++.
cppyy_test.py
import cppyy
import numpy as np
cppyy.include('Buffer.h')
s = cppyy.gbl.Buffer()
numpy_array = np.empty(32000, np.float64)
s.get_numpy_array(numpy_array.data, numpy_array.size)
print(numpy_array[:20])
Buffer.h
struct Buffer {
void get_numpy_array(double *ad, int size) {
for( long i=0; i < size; i++)
ad[i]=i;
}
};
You can also create a Python module very easily (with CMake), this way you will avoid recompile the C++ code all the times.
The question is how to call a C function from Python, if I understood correctly. Then the best bet are Ctypes (BTW portable across all variants of Python).
>>> from ctypes import *
>>> libc = cdll.msvcrt
>>> print libc.time(None)
1438069008
>>> printf = libc.printf
>>> printf("Hello, %s\n", "World!")
Hello, World!
14
>>> printf("%d bottles of beer\n", 42)
42 bottles of beer
19
For a detailed guide you may want to refer to my blog article.
Cython is definitely the way to go, unless you anticipate writing Java wrappers, in which case SWIG may be preferable.
I recommend using the runcython command line utility, it makes the process of using Cython extremely easy. If you need to pass structured data to C++, take a look at Google's protobuf library, it's very convenient.
Here is a minimal examples I made that uses both tools:
https://github.com/nicodjimenez/python2cpp
Hope it can be a useful starting point.
First you should decide what is your particular purpose. The official Python documentation on extending and embedding the Python interpreter was mentioned above, I can add a good overview of binary extensions. The use cases can be divided into 3 categories:
accelerator modules: to run faster than the equivalent pure Python code runs in CPython.
wrapper modules: to expose existing C interfaces to Python code.
low level system access: to access lower level features of the CPython runtime, the operating system, or the underlying hardware.
In order to give some broader perspective for other interested and since your initial question is a bit vague ("to a C or C++ library") I think this information might be interesting to you. On the link above you can read on disadvantages of using binary extensions and its alternatives.
Apart from the other answers suggested, if you want an accelerator module, you can try Numba. It works "by generating optimized machine code using the LLVM compiler infrastructure at import time, runtime, or statically (using the included pycc tool)".

Categories

Resources