How can I wrap a c++ library (.h & .lib files) using Python? - python

I have a c++ library containing only .h and .lib files (no cpp files) for communicating with a piece of hardware and I need to use this library from Python. I don't have much experience with c/++, so it's all a little alien to me.
The .h files look something like this:
#define MSG_DLL_VERSION 10
typedef struct {
ULONG ulDLLVersion;
// vipmsg variables
PMSGACCOUNTS pMsgAccounts;
PMSGSEGMENT pMsgSegment;
USHORT usMsgSegmentNum;
} MSGSTATICDATA, *PMSGSTATICDATA;
VOID msgGetDLLRedirections ( PMSGSTATICDATA *pData );
VOID msgSetDLLRedirections ( PMSGSTATICDATA pData );
Looking around, here's what I've found:
Cython is primarily for C. It can do c++, but requires the .cpp files
Boost.Python also requires .cpp files
cffi is C only
ctypes is C only
So, what would be the best approach?

Boost.Python also requires .cpp files
No it does not. You can wrap your library with Boost.Python. The way you expose C++ code using Boost.Python is to write a shared library using the various macros provided by Boost.Python.
The documentation for Boost.Python demonstrates wrapping this C++ type:
struct World
{
void set(std::string msg) { this->msg = msg; }
std::string greet() { return msg; }
std::string msg;
};
The wrapper looks like this:
#include <boost/python.hpp>
using namespace boost::python;
BOOST_PYTHON_MODULE(hello)
{
class_<World>("World")
.def("greet", &World::greet)
.def("set", &World::set)
;
}
You can do that with your library. You do need to write the wrapper. But the fact that the class and methods being wrapped are defined in a .lib library file rather than a .cpp source file is irrelevant.
Update
Looking at the sample code from the header file this seems much more like a C style library than C++. You could certainly use Boost.Python for that. SWIG would also be an option. Or ctypes.

Related

pybind11: return c++ class (with an existing python binding) to python

I am trying to create a wrapper for a c++ method that returns a c++ class(vtkPolyData) which comes from an external c++ library (vtk). The same library has python binding available which is already installed in my python environment. How do you tell pybind that the c++ class (vtkPolydata) and its python variant are the same?
I tried to use this custom type caster macro. but I get TypeError: Unable to convert function return value to a Python type! The signature was : (self: Versa3dLib.skeletonizer, offset distance: float) -> vtkPolyData
which is confusing since it looks like the conversion maps to the correct type but python is unable to interpret it. So I am not sure what's wrong since I don't see anything wrong with the macro either. I noticed that in python vtkPolyData has type vtkCommonDataModelPython.vtkPolyData. is that why the conversion is not done correctly?
#include "skeletonizer.h"
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "PybindVTKTypeCaster.h"
#include <vtkSmartPointer.h>
namespace py = pybind11;
PYBIND11_VTK_TYPECASTER(vtkPolyData)
PYBIND11_DECLARE_HOLDER_TYPE(T, vtkSmartPointer<T>);
namespace pybind11 { namespace detail {
template <typename T>
struct holder_helper<vtkSmartPointer<T>> { // <-- specialization
static const T *get(const vtkSmartPointer<T> &p) { return p.GetPointer(); }
};
}}
PYBIND11_MODULE(Versa3dLib, m)
{
py::class_<skeletonizer>(m, "skeletonizer")
.def(py::init<vtkPolyData *>())
.def("get_offset", &skeletonizer::get_offset,
"return vtkPolyData offset",
py::arg("offset distance"));
}
Skeletonizer
#ifndef SKELETONIZER_H
#define SKELETONIZER_H
#include <vtkPolyData.h>
#include <vector>
#include <vtkSmartPointer.h>
using namespace std;
class skeletonizer
{
public:
skeletonizer(vtkPolyData* data);
vtkSmartPointer<vtkPolyData> get_offset(double dist);
};
#endif
skeletonizer cpp
#include "skeletonizer.h"
skeletonizer::skeletonizer(vtkPolyData* data)
{
};
vtkSmartPointer<vtkPolyData> skeletonizer::get_offset(double dist)
{
vtkSmartPointer<vtkPolyData> offsets = vtkSmartPointer<vtkPolyData>::New();
return offsets;
};
I think this should be a more general solution (hopefully easier to use?):
vtk_pybind (README)
vtk_pybind.h
example binding code
example python test using bindings
I believe this should be an improvement on the VTK code by:
Generalizing the type casters using SFINAE (rather than requiring explicit instantiations...).
Permit direct casting of vtkSmartPointer and vtkNew (assuming the types inside these are VTK types).
Made the code kind-of follow Drake's C++ + Python binding conventions.
For the above solution you had, I think it was close leveraging the SMTK code, but the holder type instantation was incorrect - you'd need type_caster specializations for the smart pointers (which the vtk_pybind code I posted would provide).
I'll see if I can post an issue on SMTK to see if they want to improve / simplify their binding code (esp. if people refer to it!).
EDIT: Posted issue here: https://gitlab.kitware.com/cmb/smtk/issues/228

Dealing with opaque pointers in pybind11

I am moving a Python module written in C++ from Boost.Python to Pybind11. My C++ code relies on a C library that has opaque pointers. With Boost.Python, I used the documentation here to deal with these opaque pointers: https://www.boost.org/doc/libs/1_66_0/libs/python/doc/html/reference/to_from_python_type_conversion/boost_python_opaque_pointer_conv.html
I can't find the equivalent code for Pybind11. For reference, here is a C header that I want to expose to Python using Pybind11:
typedef struct mytruct* mystruct_t;
void myfunction1(mystruct_t x);
mystruct_t myfunction2();
This can be exposed with Boost.Python as follows:
BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID(mystruct)
namespace bpl = boost::python;
BOOST_PYTHON_MODULE(mymodule) {
bpl::opaque<mystruct>();
bpl::def("f1", &myfunction1);
bpl::def("f2", &myfunction2, bpl::return_value_policy<bpl::return_opaque_pointer>());
}
If your goal is to simply permit passing pointers of a C++ object to Python without exposing information, you should just be able to register the class:
PYBIND_MODULE(mymodule, m) {
py::class_<mystruct>(m, "mystruct");
m.def("f1", &myfunction1);
m.def("f2", &myfunction2);
}
If you wish to avoid conflict with other pybind11 modules that might declare types on this third-party type, consider using py::module_local():
https://pybind11.readthedocs.io/en/stable/advanced/classes.html#module-local-class-bindings

Cython write a python wrapper to third-party shared library

I need help in writing a wrapper to call some functions and get JSON responce (a unicode string) from a third-party shared library. The library's headers file is shown below:
#include <string>
#include <ExportLib.h>
// some code ignored here
typedef std::string UString;
using namespace std;
namespace ns1{
class DLL_PUBLIC S_API {
public:
static UString function1();
static UString function2();
// some code ignored here
};
}
The problem is I'm not so good in C/C++, so I have no idea what to do with Cython. I would be very grateful if someone would point me in right direction. I wrote a .pyx file like so:
from libcpp.string cimport string
cdef extern from "libName.h" namespace "ns1":
cdef cppclass S_API:
string function1;
string function2;
This compiles fine and I do get a .so file, which I can import in Pyhton. But I am still unable to access function1() or any other function inside the module.

How can I expose C++ classes in a Python extension? (Without resorting to boost.python or swig or cython)

I've followed this video tutorial on setting up a very basic C++ extension for python using Visual Studio. It's very straightforward and works quite nicely.
https://www.youtube.com/watch?v=y_eh00oE5rI
Here's what my code looks like.
#ifdef _DEBUG
#define _DEBUG_WAS_DEFINED
#undef _DEBUG
#endif
#include <Python.h>
#define MODULE_NAME "DummyExt"
PyObject* greet(PyObject* self, PyObject* args)
{
return PyString_FromString("Hello world!");
}
PyMethodDef DummyExtMethods[] =
{
{"greet", greet, METH_NOARGS, nullptr}
};
PyMODINIT_FUNC
initDummyExt(void)
{
PyObject* m;
m = Py_InitModule(MODULE_NAME, DummyExtMethods);
if (m == nullptr)
{
return;
}
}
#ifdef _DEBUG_WAS_DEFINED
#define _DEBUG 1
#endif
How can I use python's APIs to expose C++ classes from an extension? I want to be able to define my class and its logic in C++ and be able to instantiate it from python and call its member functions and whatnot. As the title says I don't want to use any 3rd party tools and such, only the headers and libs that come with python.
Any help would be appreciated :)

Best method to call a C function from python code

I have some C code which has some basic functions. I want to be able to call these C functions from my python code. There seem to be a lot f methods to do this as I search online, but they look a bit complicated. Can anyone suggest which is the simplest and best method to call C functions from python without any issues ?
Cffi library is a fairly modern approach to this. It also works across python and pypy.
Provided you have your functions contained in a shared library, you can just import them as python methods. Have a look at examples here: http://cffi.readthedocs.org/en/latest/overview.html
When you want to extend Python with some C functions you can check out the following example. What you need is a module which registers wrapper functions for your own functions.
For more information check out the Python docs.
#include <Python.h>
static PyObject *
yourfunction(PyObject *self, PyObject *args, PyObject *keywds)
{
int voltage;
char *state = "a stiff";
char *action = "voom";
char *type = "Norwegian Blue";
static char *kwlist[] = {"voltage", "state", "action", "type", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,
&voltage, &state, &action, &type))
return NULL;
printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",
action, voltage);
printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef keywdarg_methods[] = {
{"yourfunction", (PyCFunction)yourfunction, METH_VARARGS | METH_KEYWORDS,
"the doc of your function"},
{NULL, NULL, 0, NULL} /* sentinel */
};
// in Python 2.x the function name initmodulename is executed when imported
void initkeywdarg(void)
{
/* Create the module and add the functions */
Py_InitModule("keywdarg", keywdarg_methods);
}
To compile the file you can use clang. Keep in mind that you have to correct the include path if the Python headers are located somewhere else. The created binary keywdarg.so can be created by using import keywdarg.
clang++ -shared -I/usr/include/python2.7 -fPIC keywdarg.cpp -o keywdarg.so -lpython
You can use a shared library using the C compiler and make the calls of the functions that are defined in the library, to this, you can use the module CPython.
Let's say your c code sends email, and the code accept args like this
./sendemail email title body
Then from python , you can do something like:
from subprocess import call
call(["./sendemail", "email#email.com", "subject", "body"])

Categories

Resources