I'm trying to bind a registerHandler() defined in a given class, which I can't modify.
The class:
class FG{
public:
//return DType, no input argument
using Handler = std::function<DType(void)>;
void RegisterHandler(Handler h);
}
class DType{
public:
DType() noexcept = default;
DType(DType const &) = delete;
DType(DType&& other) noexcept = default;
~DType() noexcept
{...
}
DType& operator=(DType const&) = delete;
DType& operator=(DType&& other) & noexcept = default;
...
}
If I bind it like below, it gives an error message saying "error: use of deleted function 'DType(const DType&)'.
#include <pybind/pybind11>
#include <pybind/stl_bind11>
#include <pybind/functional.h>
#include <pybind/stl.h>
using Handler = std::function<DType>(void);
void bind_FG(py::module& m)
{
py::class_<FG> pyClass(m, "FG", py::module_local());
pyClass.def("RegisterHandler" &RegisterHandler);
//or
// pyClass.def("RegisterHandler", [](FG& self, Handler h)
// {
// self.RegisterHandler(h);
// }
// );
}
I googled std::function, and it says it requires a copy ctor, does it mean that there is no way to bind the RegisterHandler() for my case?
At the same time, I'm confused we have implemented the handler function in the C++ side, it seems to be fine (build and work). Why doesn't it require a copy ctor for C++ code?
Related
I have a virtual object that I subclass and implement in python using pybind11, and that object then is used in C++ code but the python object goes out of scope when the python script finished and python garbage collector destroys my object before I can use it in C++ side.
To avoid the destruction I have added a "keep" method to the binding code that increases the ref_count, so that python garbage collector doesn't destroy it.
C++ code for the bindings
VNCS::Real VNCS::py::BlendingField::blending(const VNCS::Point_3 &p) const
{
std::array<double, 3> pyP{p[0], p[1], p[2]};
PYBIND11_OVERLOAD_PURE(VNCS::Real, /* Return type */
VNCS::BlendingField, /* Parent class */
blending, /* Name of function in C++ (must match Python name) */
pyP /* Argument(s) */
);
}
void VNCS::py::module::blendingField(pybind11::module &m)
{
pybind11::class_<VNCS::BlendingField, VNCS::py::BlendingField, std::shared_ptr<VNCS::BlendingField>>(
m, "BlendingField")
.def(pybind11::init(
[](pybind11::args &args, pybind11::kwargs &kwargs) { return std::make_shared<VNCS::py::BlendingField>(); }))
.def("keep", [](pybind11::handle handle) { handle.inc_ref(); });
}
Python code
class BlendingField(PyVNCS.BlendingField):
def __init__(self, *args, **kwargs):
PyVNCS.BlendingField.__init__(self, *args, **kwargs)
def __del__(self):
print("dtor from python")
def blending(self, point):
return point[0]
blendingField = BlendingField()
blendingField.keep()
simCreator = initObject.addObject("SimCreator",
name="simCreator",
coarseMesh="#lowResTopology",
detailMesh="#highResTopology")
simCreator.setBlendingField(blendingField)
This doesn't look very nice, as the keep method there feels like a terrible hack. What would be the correct way to implement this?
I don't think messing with the ref count is the best solution. If you want to use an object in C++-land, use C++ lifetime management tools.
Pybind integrates lifetime management into the language bindings, you just need to change it's behavior. To quote the docs:
The binding generator for classes, class_, can be passed a template type that denotes a special holder type that is used to manage references to the object. If no such holder type template argument is given, the default for a type named Type is std::unique_ptr<Type>, which means that the object is deallocated when Python’s reference count goes to zero.
It is possible to switch to other types of reference counting wrappers or smart pointers, which is useful in codebases that rely on them. For instance, the following snippet causes std::shared_ptr to be used instead.
Here's an example lifted from the tests:
// Object managed by a std::shared_ptr<>
class MyObject2 {
public:
MyObject2(const MyObject2 &) = default;
MyObject2(int value) : value(value) { print_created(this, toString()); }
std::string toString() const { return "MyObject2[" + std::to_string(value) + "]"; }
virtual ~MyObject2() { print_destroyed(this); }
private:
int value;
};
py::class_<MyObject2, std::shared_ptr<MyObject2>>(m, "MyObject2")
.def(py::init<int>());
m.def("make_myobject2_1", []() { return new MyObject2(6); });
m.def("make_myobject2_2", []() { return std::make_shared<MyObject2>(7); });
m.def("print_myobject2_1", [](const MyObject2 *obj) { py::print(obj->toString()); });
m.def("print_myobject2_2", [](std::shared_ptr<MyObject2> obj) { py::print(obj->toString()); });
m.def("print_myobject2_3", [](const std::shared_ptr<MyObject2> &obj) { py::print(obj->toString()); });
m.def("print_myobject2_4", [](const std::shared_ptr<MyObject2> *obj) { py::print((*obj)->toString()); });
If you do go that route, which I would recommend, this question would be a duplicate of this one.
Note that there are some minor pitfalls here especially if youre codebase uses raw pointers around python code. The docs point to a possible double-free if a C++ method returning a raw pointer to an shared object is called from python. Again stealing from the docs:
class Child { };
class Parent {
public:
Parent() : child(std::make_shared<Child>()) { }
Child *get_child() { return child.get(); } /* Hint: ** DON'T DO THIS ** */
private:
std::shared_ptr<Child> child;
};
PYBIND11_MODULE(example, m) {
py::class_<Child, std::shared_ptr<Child>>(m, "Child");
py::class_<Parent, std::shared_ptr<Parent>>(m, "Parent")
.def(py::init<>())
.def("get_child", &Parent::get_child);
}
from example import Parent
print(Parent().get_child()) // UB or Segfault
Though these issue don't seem to me to be unique to shared_ptr over unique_ptr.
Motivation
I am currently trying to use a custom class both in python and in c++ with pybind11. The motivation behind this is to use a python-trained classifier within c++.
There are some working examples, e.g. in the official documentation https://pybind11.readthedocs.io/en/stable/advanced/cast/custom.html or by the nice examples from tdegeus https://github.com/tdegeus/pybind11_examples/blob/master/09_numpy_cpp-custom-matrix/pybind_matrix.h
However, i still have problems to transfer this to a simple example with a custom class.
Below is a minimal working example with a wrapper function for pybind11 in c++ and the use of it in python.
#include <vector>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
//A custom data class
class class_DATA{
public:
std::vector<int> a;
};
//the actual function
class_DATA func_test(class_DATA data){
std::vector<int> a = {1,2};
data.a = a;
return data;
}
//the wrapper function that communicates with python
py::object wrapper(py::object data_py){
class_DATA data_cpp = class_DATA();
py::object a = data_py.attr("a");
data_cpp.a = a.cast<std::vector<int>>();
class_DATA data_result_cpp = func_test(data_cpp);
py::object data_result_py = data_py;
data_result_py.attr("a") = py::cast(data_result_cpp.a);
return data_result_py;
}
//defining the modules
PYBIND11_MODULE(TEST,m){
m.doc() = "pybind11 example plugin";
//the test function
m.def("func_test", &wrapper);
//the custom class
py::class_<class_DATA>(m, "class_DATA", py::dynamic_attr())
.def(py::init<>())
.def_readwrite("a", &class_DATA::a);
}
from build.Debug.TEST import func_test
//the custom data class in python
class class_DATA_py:
def __init__(self):
self.a = [0]
test_py = class_DATA_py()
data_return = func_test(test_py)
The output input is data.a = 0 and the output data_return.a = [1,2] as desired.
Question
How can I replace the wrapper function call with a custom caster? Most likely with the general shape outlined below (?)
namespace pybind11 { namespace detail {
template <> struct type_caster<class_DATA> : public type_caster_base<class_DATA> {
using base = type_caster_base<class_DATA>;
public:
PYBIND11_TYPE_CASTER(class_DATA, _("class_DATA"));
// Conversion part 1 (Python->C++):
bool load(py::handle src, bool convert){
PyObject *source = src.ptr();
//what to do here?
return true;
}
// Conversion part 2 (C++ -> Python):
static py::handle cast(class_DATA src, py::return_value_policy policy, py::handle parent){
//what to do here?
return base::cast(src, policy, parent);
}
};
}}
c++ is not my forte and python crashes repeatedly without any error messages when I meddle with this part, so any help would be really appreciated.
I'm using pybind11 to access some functions which I wrote in C++ from python. I wish to return an initialized struct instance to python(from a class member function) so that I can access the instance values in python. I have provided a simplified version of my source file. One can reproduce the error with the following.
This is the c++ wrapper
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "sum.h"
namespace py = pybind11;
PYBIND11_PLUGIN(GetSum) {
py::module mod("GetSum", "wrapping caltri");
mod.def("get_instance", &GetSum::get_instance, py::return_value_policy::copy, "get instance of out");
py::class_<GetSum, std::shared_ptr<GetSum>>(mod, "GetSum")
.def(py::init<>())
.def("input", &GetSum::input);
return mod.ptr();
}
This is a part of the class header sum.h
extern "C" {
#include "mesh.h"
}
#include<iostream>
#include<vector>
class GetSum {
struct A out;
public:
GetSum();
void input(std::vector<float>);
struct A get_instance() {
return out; };
~GetSum();
};
This is class definition sum.cpp
#include "sum.h"
GetSum::GetSum() {
out.pointlist = nullptr;
}
void GetSum::input(std::vector<float> num){
out.pointlist = new float[num.size()];
for(size_t i = 0; i < num.size(); i++)
*(out.pointlist+i) = num[i];
}
GetSum::~GetSum(){
delete [] out.pointlist;
}
Struct definition: mesh.h
#include <stdio.h>
struct A {
float *pointlist;
};
and this is how I was calling it in python.
import GetSum
m = GetSum.GetSum()
m.input([1.0,2.0])
GetSum.s = GetSum.get_instance()
After which I get the error: get_instance(): incompatible function arguments. The following argument types are supported:
1. (arg0: GetSum) -> A
Can someone help me figure out where I might be going wrong?
Thanks!
You need to declare struct A to pybind11 before it can be used as a return type by adding at minimum:
py::class_<A>(mod, "A")
.def(py::init<>());
to your wrapper code. Further, since get_instance is not static, you need to call it as follows in your example python code:
GetSum.s = GetSum.get_instance(m)
In some places in exposing C++ code to python, I need to use a PyObject*. If I have an instance of a boost::python::class_ object, I can invoke ptr() on it. But what if I just have the type?
Basically, given the type list boost::python::bases<A, B, C>, I want to convert this to a boost::python::tuple of instances that I can pass into something like PyErr_NewExceptionWithDoc(). Is this possible?
Given a C++ type T, one can create a boost::python::type_id object, and then query into the Boost.Python registry for registration information. If an entry is found in the registry, then one can use it to obtain a handle to the Python class created for type T:
/// #brief Get the class object for a wrapped type that has been exposed
/// through Boost.Python.
template <typename T>
boost::python::object get_instance_class()
{
// Query into the registry for type T.
namespace python = boost::python;
python::type_info type = python::type_id<T>();
const python::converter::registration* registration =
python::converter::registry::query(type);
// If the class is not registered, return None.
if (!registration) return python::object();
python::handle<PyTypeObject> handle(python::borrowed(
registration->get_class_object()));
return python::object(handle);
}
Here is a complete example demonstrating locating a Python class object in the Boost.Python registry:
#include <boost/python.hpp>
#include <iostream>
/// #brief Get the class object for a wrapped type that has been exposed
/// through Boost.Python.
template <typename T>
boost::python::object get_instance_class()
{
// Query into the registry for type T.
namespace python = boost::python;
python::type_info type = python::type_id<T>();
const python::converter::registration* registration =
python::converter::registry::query(type);
// If the class is not registered, return None.
if (!registration) return python::object();
python::handle<PyTypeObject> handle(python::borrowed(
registration->get_class_object()));
return python::object(handle);
}
struct spam {};
int main()
{
Py_Initialize();
namespace python = boost::python;
try
{
// Create the __main__ module.
python::object main_module = python::import("__main__");
python::object main_namespace = main_module.attr("__dict__");
// Create `Spam` class.
// >>> class Spam: pass
auto spam_class_object = python::class_<spam>("Spam", python::no_init);
// >>> print Spam
main_module.attr("__builtins__").attr("print")(get_instance_class<spam>());
// >>> assert(spam is spam)
assert(spam_class_object.ptr() == get_instance_class<spam>().ptr());
}
catch (python::error_already_set&)
{
PyErr_Print();
return 1;
}
}
Output:
<class 'Spam'>
For more type related functionality, such as accepting type objects, is, and issubclass, see this answer.
I'm using Boost.Python to create a wrapper for my C++ library, and I'm having some troubles, googling all the day did not produc any results. For example, I have the following code:
class Base
{
public:
virtual void func() = 0;
};
class Derived : public Base
{
public:
virtual void func()
{
cout << "Derived::func()"<< endl;
}
};
// wrapper for Base
struct BaseWrapper : Base, python::wrapper<Base>
{
virtual void func()
{
this->get_override("func");
}
};
Base* makeDerived()
{
return new Derived;
}
vector<Base*>* makeDerivedVec()
{
vector<Base*> *v = new vector<Base*>;
v->push_back(new Derived);
v->push_back(new Derived);
v->push_back(new Derived);
return v;
}
BOOST_PYTHON_MODULE(mylib)
{
// export Base
class_<BaseWrapper, noncopyable>("Base")
.def("func", pure_virtual(&Base::func));
class_<vector<Base*> >("BasePtrVec")
.def(vector_indexing_suite<vector<Base*> >());
// export Derived
class_<Derived, bases<Base> >("Derived")
.def("func", &Derived::func);
// export makeDerived()
def("makeDerived", &makeDerived, return_value_policy<manage_new_object>());
// export makeDerivedVec()
def("makeDerivedVec", &makeDerivedVec, return_value_policy<manage_new_object>());
}
So, I compile it, import in python and try this:
b = mylib.Base() b.func()
d = mylib.makeDerived() d.func()
The first line, as expected, throws an exception saying that b.func() is pure virtual, and the second line prints out
Derived::func()
And that's ok.
But the code
dlist = mylib.makeDerivedVec()
for d in dlist:
d.func()
does not work, and Python throws an exception:
TypeError: No to_python (by-value) converter found for C++ type: Base*
Why it handled correctly the Base* returned by makeDerived() and refuses to work with Base* contained in std::vector? How can I make it work?
You can fix this by registering Base* as a type that can be used to point to a BaseWrapper*:
class_<BaseWrapper, noncopyable, Base*>("Base")
.def("func", pure_virtual(&Base::func));
But it seems that this means that Base can't have a pure virtual function...