A c++ function has a signature:
void f*(unsigned char* buffer, int buffer_size);
It can be wrapped using pybind11:
m.def("f",&f);
I am now stuck on how I may call f in python.
For example, using ctypes, something like this would work:
buffer = bytearray(size)
cbuf_type = ctypes.c_char*len(buffer)
cbuf = cbuf_type.from_buffer(buffer)
lib.f(cbuf,size)
What would work for pybind11 ?
One simply has to use the interoperability with numpy, here an example:
https://people.duke.edu/~ccc14/cspy/18G_C++_Python_pybind11.html#Using-numpy-arrays-as-function-arguments-and-return-values
Related
I have a boost python application that exports a class to Python, performs a calculation and returns the output back to C++:
import engine # c++ library
import glm # pyglm
class Game(engine.Application):
def __init__(self):
engine.Application.__init__(self, title, fullscreen)
self.shader = engine.Shader();
self.shader.setup("shader.vs", "shader.fs")
self.shader.setMat4("model", glm.mat4(1.0)
def update(self):
...
Amongst other classes, the shader is being wrapped and exported from C++ like this:
struct ShaderWrap : Shader, boost::python::wrapper<Shader>
{
int setup(const char* vertexPath, const char* fragmentPath)
{
return Shader::setup(vertexPath, fragmentPath);
}
int setMat4(std::string name, glm::mat4 mat)
{
return Shader::setMat4(name, mat);
}
};
...
BOOST_PYTHON_MODULE(engine)
{
namespace python = boost::python;
...
python::class_<ShaderWrap, boost::noncopyable>("Shader")
.def("setup", &Shader::setup)
.def("setMat4", &Shader::setMat4)
;
...
}
The application works fine up until the self.shader.setMat4 call and I get the error:
Boost.Python.ArgumentError: Python argument types in
StaticShader.setMat4(StaticShader, str, glm::detail::tmat4x4)
did not match C++ signature:
setMat4(StaticShader {lvalue}, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, glm::mat<4, 4, float, (glm::qualifier)0>)
I've tried to pass the mat4 value as a float* back to C++ with no success.
I've also tried calling value_ptr(mat4) which returns a c_void_p, but the compiler complains saying c_void_p and void* are not the same type (also tried various attempts of casting it with the ctypes library).
this question and this question are the basically the same problem but I got lost in the implementation.
In the PyGLM documentation, the developer says the library is written in C++. So,
What would be a possible way to pass through a glm.mat4 value to C++ to be interpreted as a glm::mat4 value?
I want to pass a glm.mat4 such as the view matrix, being updated in the Python code update function back to C++ to be further used, and was hoping there would be a fairly simple interface between the pyGLM library/GLM library through boost::python. Thanks in advance.
Ended up contacting the developer about this issue and this was there response:
This is what matrix objects look like in C++ code:
template<int C, int R, typename T>
struct mat {
PyObject ob_base;
uint8_t info;
glm::mat<C, R, T> super_type;
};
That is most likely why it's not quite compatible to glm matrices.
If you're using the latest version of PyGLM, value_ptr(mat4) should return a LP_c_float object, i.e. a float*.
I'm sure there is a way to transfer such a pointer over to boost::python, though as I said, I don't know how.
Technically you could convert the pointer to an integer, transfer that and cast it back to a float*.
You can get the address of a pointer by using:
int.from_bytes(ptr, "little")
Alternatively you could simply send over a bytes object.
as_bytes = bytes(mat4)
Or as a tuple:
as_tuple = mat4.to_tuple()
Or as a numpy array (which seems to have it's own boost::python::numpy module):
np_arr = numpy.array(mat4)
I was able to get the right output by passing the memory address as an int* back to C++ which then did some slicing to get it back into a glm::mat4, but I found this solution too convoluted and ended up wrapping the glm::mat4 class manually (see https://stackoverflow.com/a/62672081/9238288 for a basic example of how to achieve this).
I have a structure like this:
struct Person {
std::string name;
int age;
std::string title;
float income;
};
In the python side, I have a dict like this:
person = { 'name':'Alex',
'age':36,
'title':'programmer',
'income':13435.40
}
Now I want to convert the Python dict to the C++ program by using ctypes. There is a stupid way to do this task is that we can write 4 c-style functions to set these four elements independently. How can we do this task in a single function, which can solve different data type (string, int, float)?
ctypes can't handle C++ on its own.
SWIG can though this is equivalent to writing a C wrapper for the C++ code. It requires building native code.
I'm a newbie in Python and embedding it too. And I have one problem:
There is function in my python module that recieves buffer created with ctypes.create_string_buffer(size) and fills it by content from some memory address:
def get_mem(self, address, size, data):
self.mem.read_ram_block(address, size, data)
How should I call this method with using of (char *) buffer? I want fill my C++ buffer with recieved from python.
If you only want to call the Python function ctypes.create_string_buffer(size), you could easily mirror the Python coding on the C++ side:
static PyObject* create_string_buffer(unsigned long size) {
PyObject *ctypes = PyImport_ImportModule("ctypes");
if (!ctypes) return 0;
PyObject *buf = PyObject_CallMethod(ctypes, "create_string_buffer", "k", size);
Py_DECREF(ctypes);
return buf;
}
If you'd like to use another type than unsigned long for the size, you'd need to change the format in PyObject_CallMethod as well. For example O is used for PyObject*. For a complete list of formats see the documentation for Building values.
Basically, it should look like this (in Pseudocode):
C++ file:
void do_stuff(){
printf("Callback", );
}
void main(){
call_python_function(&do_stuff);
}
Python file:
def python_function(ptr):
ptr()
Just calling Python from C++ with some parameters is relatively easy, using Python.h, but how do I do the callback to C++?
Also, I also want to pass around some data, so ideally, it should look something like this in the end:
C++ file:
vector<double> do_stuff(double a, double b){
vector<double> v;
for(int i=0; i<1000; i++) v.push_back(a*sin(b*i));
}
void main(){
call_python_function(&do_stuff);
}
Python file:
def python_function(ptr):
print ptr(1.2,3.4)
Is this possible somehow? I am considering using a message passing interface like ZeroMQ with some threading, but maybe there is a cleaner, and potentially simpler solution which looks more or less like what I have shown here?
May be you can try using swig (http://www.swig.org/) to wrap the C++ functions which can be called from python script.
I have a struct in a dll that only contains function pointers (ie a vtable) that I would like to interact with in python (for test purposes). I am having a bit of trouble working out how to do this using ctypes.
What I have is:
struct ITest
{
virtual char const *__cdecl GetName() = 0;
virtual void __cdecl SetName(char const *name) = 0;
};
/* Factory function to create 'real' Test object */
extern "C" __declspec(dllexport) struct ITest * CALLCONV make_Test(char const * name);
A 'real' Test object will fill in the struct as appropriate. This gets compiled into a DLL (test.dll). I'd like, in python, to be able to call the factory method to get back a pointer to my Test struct and then call the function pointers contained in the struct, but I just can't seem to get my head around how it would work using ctypes. Does anyone have any pointers / examples of doing something similar or should I be using something like SWIG or Boost?
Thanks for any help.
Something like this should be a good starting point (I don't have your DLL compiled to test)
from ctypes import Structure, CFUNCTYPE, POINTER, c_char_p, windll
class ITest(Structure):
_fields_ = [
('GetName', CFUNCTYPE(c_char_p)),
('SetName', CFUNCTYPE(None, c_char_p)
]
test = windll.LoadLibrary('test.dll')
test.make_Test.restype = POINTER(ITest)
After this, you'll need to call make_Test() to get the struct, and try calling the functions. Perhaps with code like this:
itest = test.make_Test().contents
itest.SetName('asdf')
print itest.GetName()
Provide the dll or test and give me your results and I can help more if you still have problems.
The ctypes documentation says that you can create a ctypes.PYFUNCTYPE from an address.
If you get the address of the functions in your structure then you can wrap it as a Python function thanks to ctypes.PYFUNCTYPE and then call it as a regular ctype function.
I didn't test it myself but I think it maybe something to explore in your case
See http://docs.python.org/library/ctypes.html#ctypes.PYFUNCTYPE
I hope it helps