Passing a C++ function to a Python functional within C++ with Pybind11 - python

I want to call the Python functional eval from a C++11 code, using a function defined there as the argument of eval.
my_module.py
def eval(foo):
return foo(1)
main.cpp
#include <iostream>
#include <vector>
#include <pybind11/embed.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
double foo (double x) { return x; };
auto foo_py = py::vectorize(foo);
int main()
{
py::scoped_interpreter guard{};
py::module m = py::module::import("my_module");
m.attr("eval")(foo_py);
}
The code compiles but I get the following error, which I do not understand:
terminate called after throwing an instance of 'pybind11::cast_error'
what(): Unable to convert call argument '0' of type 'detail::vectorize_helper (*)(double), double, double>' to Python object

Related

Object created in Python via Pybind11 not recognized as a derived class by C++ functions

The minimum code to reproduce the issue is as follows:
aaa.hpp
#include <string>
#include <vector>
#include <iostream>
#include <stdexcept>
#include <cmath>
#include <cassert>
#include <utility>
template <typename D>
class BaseClass{
/* The base class. */
protected:
bool skip_nan = true;
};
template <typename D>
class DerivedClass : public BaseClass<D>{
public:
explicit DerivedClass(bool skip_nan_){ this->skip_nan = skip_nan_; }
};
aaa_py.cpp
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include "aaa.hpp"
namespace py = pybind11;
template <typename D>
void fff(BaseClass<D>& rs){
}
PYBIND11_MODULE(aaa_py, m) {
m.def("fff_float", &fff<float>);
py::class_<DerivedClass<float>>(m, "DerivedClass_float").def(py::init<bool>(), py::arg("skip_nan")=true);
}
test.py
import aaa_py as rsp
ds = rsp.DerivedClass_float()
rsp.fff_float(ds)
Error:
Traceback (most recent call last):
File "usage_example_python.py", line 8, in <module>
rsp.fff_float(ds)
TypeError: fff_float(): incompatible function arguments. The following argument types are supported:
1. (arg0: BaseClass<float>) -> None
Invoked with: <aaa_py.DerivedClass_float object at 0x000002C9EAF57308>
Basically the error is saying that function fff_float expects somebody from BaseClass<float>, but receives DerivedClass<float>. If I change the function to accept DerivedClass<D>&, then there would be no errors. Or, if I create an instantiation of DerivedClass<float> directly in c++ and pass it to the float_fff, there would also be no issues. So it seems that the issue is that for an object created in Python, somehow the information about its base class is lost. How can I deal with this?
You need to declare BaseClass<float> in Python and tell pybind11 that DerivedClass_float extends it.
PYBIND11_MODULE(MyModule, m)
{
m.def("fff_float", &fff<float>);
// declare base class - this simply expose to Python, it's impossible to
// construct a BaseClass_float in Python since no constructor is provided
py::class_<BaseClass<float>>(m, "BaseClass_float");
// tell pybind11 that DerivedClass<float> extends BaseClass<float>
py::class_<DerivedClass<float>, BaseClass<float>>(m, "DerivedClass_float")
// ^^^^^^^^^^^^^^^^
.def(py::init<bool>(), py::arg("skip_nan") = true);
}

Import error "undefined symbol: _ZNK9FastNoise8GetNoiseEff" when calling C++ extension in Python 3.6

I am currently trying to make some C++ extensions for a python script. In the C++ side of the story, it seems to be compiled just fine and it generates my .so shared library, but when I call it inside my python script it raises an error of undefined symbol. The current code is as follow:
#include <iostream>
#include "FastNoise.h"
#include <string>
#include <time.h>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <boost/python/def.hpp>
#include <boost/python/module.hpp>
using namespace std;
using namespace cv;
namespace bp = boost::python;
int gen(int _size)
{
FastNoise myNoise;
myNoise.SetNoiseType(FastNoise::Simplex);
myNoise.SetSeed((int)(rand() * time(NULL)));
Size img_size(_size, _size);
Mat noise_map(img_size, CV_32FC3);
for (int y = 0; y < _size; y++) {
for (int x = 0; x < _size; x++) {
Vec3f &color = noise_map.at<Vec3f>(Point(x, y));
color.val[0] = (myNoise.GetNoise(x, y) + 1) / 2;
color.val[1] = (myNoise.GetNoise(x, y) + 1) / 2;
color.val[2] = (myNoise.GetNoise(x, y) + 1) / 2;
}
}
imshow("test", noise_map);
waitKey(0);
return 0;
}
BOOST_PYTHON_MODULE(gen) {
bp::def("gen", gen);
}
And here is how I compiled it:
g++ main.cpp -I/opt/opencv/include/opencv -I/usr/include/python3.6m -I/usr/local/include/boost -L/opt/opencv/release/lib -L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu -L/usr/local/lib -lopencv_core -lopencv_highgui -lopencv_imgcodecs -lpython3.6m -lboost_python36 -o NoiseModule.so -shared -fPI
When I import it within python it gives me this error:
>>> import NoiseModule
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: /home/matheus/PycharmProjects/TerrainGenerator/NoiseGenerator/NoiseModule.so: undefined symbol: _ZNK9FastNoise8GetNoiseEff
>>>
Any kind of help in regards of this problem will be really appreciated.
Your shared object doesn't have access to every function you use. You probably have a file like FastNoise.cpp which implements your FastNoise object. Yet you only use main.cpp to compile your dynamic library (.so) file. So make sure all .cpp files are included in the build of your python c++ extension.
Another option might be to make to implement your FastNoise object entirely inside of the header.

Creating instance with methods in C++ and passing it to Python

I'm trying to create an instance of Game, pass it into the main namespace of the test.py as the variable game, then call game.add(e) to run the C++ function that will add Entity e into the std::vector. However, this code produces the error:
unbound method Boost.Python.function object must be called with Game instance as first argument (got Entity instance instead)
(Some context: I'm trying to let Python create instances that will be kept in a container for C++ to run through every tick and update. I thought I had it working a few weeks ago but I came back to it and apparently it wasn't working - I know, source control.)
#include <vector>
class Entity{
public:
Entity(){}
Entity(float x, float y){}
};
class Game{
public:
Game();
void add(Entity* entity);
private:
std::vector<Entity*> objects_;
};
Game::Game(){
}
void Game::add(Entity* entity){
objects_.push_back(entity);
}
main.cpp:
#include <iostream>
#include <boost/python.hpp>
#include "Game.h"
#include "Entity.h"
using namespace boost::python;
BOOST_PYTHON_MODULE(sfgame){
class_<Game>("Game")
.def("add", &Game::add)
;
class_<Entity>("Entity", init<float, float>())
;
}
int main(){
PyImport_AppendInittab("sfgame", &initsfgame);
Py_Initialize();
object main_module = import("__main__");
object main_namespace = main_module.attr("__dict__");
import("sfgame");
Game* game = new Game();
try{
main_namespace["game"] = ptr(game);
exec_file("test.py", main_namespace);
}
catch (const boost::python::error_already_set &){
PyObject *ptype, *pvalue, *ptraceback;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
std::string error;
error = boost::python::extract<std::string>(pvalue);
std::cout << error << std::endl;
}
delete game;
system("PAUSE");
return 0;
}
test.py:
from sfgame import *
e = Entity(5,5)
game.add(e)
You would receive that error if you set the variable name in the main namespace to Game, as it would be the same as the class name.
However, the code posted is correct. You must have compiled the .pyd file using the variable Game, realised your error, then changed it to game and compiled the C++ file to test it without recompiling the .pyd file so the error remained.

LoadLibrary python with dll write in c++

I have write a python script that load a dll writed in c++ .
Script is :
from ctypes import *
mydll = cdll.LoadLibrary('C:\\Users\\gpiscite\\Documents\\Project\\DllXPython\\Debug\\DllXPython.dll')
mydll
mydll.fnDllXPython(956, c_char_p("c_char_p"), c_void_p(0), "std::string")
The c++ code is compiled in VisualStudio as a DLL and it is:
// DllXPython.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include <cstdlib>
#include <string>
#include "DllXPython.h"
// This is an example of an exported variable
DLLXPYTHON_API int nDllXPython=0;
// This is an example of an exported function.
DLLXPYTHON_API int fnDllXPython(int aInt, char* apChar, void* apVoid, std::string aString)
//DLLXPYTHON_API PyObject * fnDllXPython(PyObject *self, PyObject *args)
{
return 0;
}
I had attached debug on python process and i saw thad the exception was raised on return 0 execution . The exception raised is:
Debug Assertion Failed! ..... _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) ....
Any idea ? Thanks.
"std::string" is not a std::string. It is merely a char*

typedef does not work with SWIG (python wrapping C++ code)

Hello and thanks for your help in advance !
I am writing a python wrapper (SWIG 2.0 + Python 2.7) for a C++ code. The C++ code has typedef which I need to access in python wrapper. Unfortunately, I am getting following error when executing my Python code:
tag = CNInt32(0)
NameError: global name 'CNInt32' is not defined
I looked into SWIG documentation section 5.3.5 which explains size_t as typedef but I could not get that working too.
Following is simpler code to reproduce the error:
C++ header:
#ifndef __EXAMPLE_H__
#define __EXAMPLE_H__
/* File: example.h */
#include <stdio.h>
#if defined(API_EXPORT)
#define APIEXPORT __declspec(dllexport)
#else
#define APIEXPORT __declspec(dllimport)
#endif
typedef int CNInt32;
class APIEXPORT ExampleClass {
public:
ExampleClass();
~ExampleClass();
void printFunction (int value);
void updateInt (CNInt32& var);
};
#endif //__EXAMPLE_H__
C++ Source:
/* File : example.cpp */
#include "example.h"
#include <iostream>
using namespace std;
/* I'm a file containing use of typedef variables */
ExampleClass::ExampleClass() {
}
ExampleClass::~ExampleClass() {
}
void ExampleClass::printFunction (int value) {
cout << "Value = "<< value << endl;
}
void ExampleClass::updateInt(CNInt32& var) {
var = 10;
}
Interface file:
/* File : example.i */
%module example
typedef int CNInt32;
%{
#include "example.h"
%}
%include <windows.i>
%include "example.h"
Python Code:
# file: runme.py
from example import *
# Try to set the values of some typedef variables
exampleObj = ExampleClass()
exampleObj.printFunction (20)
var = CNInt32(5)
exampleObj.updateInt (var)
Thanks again for your help.
Santosh
I got it working. I had to use typemaps in the interface file, see below:
- Thanks a lot to "David Froger" on Swig mailing lists.
- Also, thanks to doctorlove for initial hints.
%include typemaps.i
%apply CNInt32& INOUT { CNInt32& };
And then in python file:
var = 5 # Note: old code problematic line: var = CNInt32(5)
print "Python value = ",var
var = exampleObj.updateInt (var) # Note: 1. updated values returned automatically by wrapper function.
# 2. Multiple pass by reference also work.
# 3. It also works if your c++ function is returning some value.
print "Python Updated value var = ",var
Thanks again !
Santosh

Categories