pybind11 data type isn't matched for map, as an input argument for function - pybind11

Data type:
std::map<platform::String, platform::String> ssmap;
Binding code:
dt.cpp (-> import dt)
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include "./ssmap.h"
PYBIND11_MAKE_OPAQUE(std::map<platform::String, platform::String>>)
void bind_string(py::module &m)
{
py::classplatform::String pyClass(m, "String);
pyClass.def(py::init<const std::String&>(), py::return_value_policy::move);
...
}
void bind_ssmap(py::module &m)
{
py::bind_map<platform::String, platform::String>>(m, "ssmap");
}
...
Then in another module, I'll use ssmap:
vp.cpp (import vp)
void bind_vp(py::module &m)
{
py::class pyClass(m, "vp", py::module_local());
pyClass.def("reset", &vp::reset, py::arg("val")); ---> val is type of ssmap
}
// test script:
import dt
import vp
val = dt.ssmap()
print("type of val is ", type(val))
str = dt.string("Hi")
val[str]=str
vp.reset(val) --> gave error
Errors: TypeError: reset(): incompatible function arguments. The following argument types are supported:
(self: vp, value: Dict[std::String, std::String]) -> None
If I checked the type of val, it shows:
type of val is : <class 'dt.ssmap'>
So why the input argument (val) is taken as Dict, but the ssmap is taken as class of ssmap (but not Dict), What is the right way to deal with this case?
Thanks for any suggestions!

You have to put PYBIND11_MAKE_OPAQUE(std::map<platform::String, platform::String>>) in vp.cpp as well, or at least in a common header. Otherwise when you bind reset the bindings will not be aware that this type is opaque.

Related

How to bind a void array using pybind11

I have a structure as follows
struct _name{
void* person[3];
}name;
I tried to copy elements into a python array but it didn't work.
How do you want to use the bound result? There is no such thing a a void* in Python, unless perhaps if you want to work with capsules, so the easiest to work with is a properly sized integer type, which can subsequently be handed to ctypes, rebound, etc.
In cppyy, I do the exactly that. Example:
import cppyy
cppyy.cppdef("""
typedef struct _name{
void* person[3];
} name;
""")
n = cppyy.gbl.name()
print(n.person[0])
which prints whatever the first pointer points to (it's uninitialized in this example) as an integer address, and cppyy.bind_object() allows rebinding of the pointer to a proxy.
To do the same in pybind11, you can for example do:
#include <pybind11/pybind11.h>
#include <pybind11/pytypes.h>
#include <pybind11/numpy.h>
typedef struct _name {
void* person[3];
} name;
PYBIND11_MODULE(name, m)
{
pybind11::class_<name>(m, "name", pybind11::buffer_protocol())
.def(pybind11::init<>())
.def_property("person", [](name &n) -> pybind11::array {
auto dtype = pybind11::dtype(pybind11::format_descriptor<uintptr_t>::format());
auto base = pybind11::array(dtype, {3}, {sizeof(uintptr_t)});
return pybind11::array(
dtype, {3}, {sizeof(uintptr_t)}, n.person, base);
}, [](name& n) {});
}
which gives you a numpy array of the proper integer type and length. Example use:
>>> import name
>>> n = name.name()
>>> n.person
array([0, 0, 0], dtype=uint64)
>>>
where again the values happen to be what they are, as they're uninitialized w/o a constructor for the struct _name.

Unable to bind rvalue reference arguments in member function

I'm using pybind11 to create python bindings for a C++ library whose source I cannot change. It contains a class that defines member functions with rvalue reference arguments (eg T &&val). I am unable to create a binding to a member function with rvalue reference arguments but binding to a non-member function with identical arguments works as expected.
A simplified example looks like this:
struct Foo {
// Cannot create a pybinding for this method.
void print_ref(int &&v) const {
std::cout << "Foo::print_ref(" << to_string(v) << ")" <<std::endl;
}
};
// Pybinding for standalone function works as expected.
void print_ref(int&& val) {
std::cout << "print_ref(" << to_string(val) << ")" << std::endl;
};
The pybind11 code looks like this:
PYBIND11_MODULE(refref, m) {
py::class_<Foo>(m, "Foo")
// Both of these attempts to create a pybinding FAILs with same error.
.def("print_ref", &Foo::print_ref)
.def("print_ref", (void (Foo::*) (int&&)) &Foo::print_ref);
// This pybinding of standalone function is SUCCESSful.
m.def("print_ref", &print_ref);
}
The compilation error on the first binding attempt is:
pybind11/bin/../include/site/python3.4/pybind11/pybind11.h:79:80: error: rvalue reference to type 'int' cannot bind to lvalue of type 'int'
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
^~~~
pybind11/bin/../include/site/python3.4/pybind11/pybind11.h:1085:22: note: in instantiation of function template specialization 'pybind11::cpp_function::cpp_function<void, Foo, int &&,
pybind11::name, pybind11::is_method, pybind11::sibling>' requested here
cpp_function cf(method_adaptor<type>(std::forward<Func>(f)), name(name_), is_method(*this),
^
refref.cpp:31:3: note: in instantiation of function template specialization 'pybind11::class_<Foo>::def<void (Foo::*)(int &&) const>' requested here
.def("print_ref", &Foo::print_ref);
Any ideas on what I may be doing wrong? Since it works fine with non-member functions, I'm inclined to suspect a pybind11 issue but thought I would check here first.
Indeed, the problem comes from rvalues. I learned quite a few things from this SO answer and this blog post.
There's a nice workaround : you can create a wrapper class that will redirect calls to the C++ library you cannot change, taking care of the rvalue with the std::move semantic.
#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
struct Foo {
void print_ref(int&& v) const {
std::cout << "Foo::print_ref(" << +v << ")" <<std::endl;
}
};
// This class will interface between PyBind and your library
class Foo_wrap{
private:
Foo _mimu;
public:
void print_ref(int v) const{
_mimu.print_ref(std::move(v));
}
};
PYBIND11_MODULE(example, m) {
py::class_<Foo_wrap>(m, "Foo_wrap")
.def(py::init())
.def("print_ref", &Foo_wrap::print_ref);
}
That you can call in Python with
import example as fo
wr = fo.Foo_wrap()
wr.print_ref(2)

Return_value_policy for method with void return type and optional parameter

I have class with void method and optional argument looking like this:
class A
{
public:
void method(int par1, bool par2 = false) { ... }
};
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(method, A::method, 1, 2)
class_<A>("A")
.def("method", &A::method, return_value_policy<reference_existing_object>(),method())
;
What is correct return_value_policy in this case? I've tried to avoid return policy completely however I've received following compile error then.
'boost::mpl::vector17<RT,most_derived<Target,ClassT>::type&,T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14> boost::python::detail::get_signature(RT (__cdecl ClassT::* )(T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14) volatile const,Target *)' : expects 2 arguments - 1 provided
Any suggestion appreciated.
I've scrambled few things together. Bur I realized I do not need to use BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS but rather named arguments something like:
def("method", &A::method, (arg("par1"), arg("par2") = false))

Class with Arduino, use SoftwareSerial as variable

Trying to use a class in Arduino 1.0 and setting SoftwareSerial as a variable with no success.
class SensorGPS: public Thread
{
private:
SoftwareSerial* serialGPS; // RX, TX
public:
SensorGPS()
{
serialGPS = new SoftwareSerial(10,11);
serialGPS.begin(4800);
}
}
serialGPS.begin returns the error
arduino_sketch.ino: In constructor 'SensorGPS::SensorGPS()':
arduino_sketch:31: error: request for member 'begin' in '((SensorGPS*)this)->SensorGPS::serialGPS', which is of non-class type 'SoftwareSerial*'
arduino_sketch.ino: In member function 'virtual void SensorGPS::run()':
arduino_sketch:37: error: request for member 'read' in '((SensorGPS*)this)->SensorGPS::serialGPS', which is of non-class type 'SoftwareSerial*'
arduino_sketch:44: error: request for member 'write' in '((SensorGPS*)this)->SensorGPS::serialGPS', which is of non-class type 'SoftwareSerial*'
If remove * when setting the variable
SoftwareSerial serialGPS(10,11); // RX, TX
result error on the variable
arduino_sketch:21: error: expected identifier before numeric constant
arduino_sketch:21: error: expected ',' or '...' before numeric constant
This issue is on all kind of classes that needs values as initialisations. Another example with Dht11 module(1); generates same error.
Notice the same issue with example Dht11 library. So I changed the constructor (library https://github.com/adalton/arduino/tree/master/projects/Dht11_Library).
Example Dht11 open the Dht11.h
From
Dht11(uint8_t newPin) : humidity(-1), temperature(-1), pin(newPin) { }
To (with added function setPin)
Dht11() : humidity(-1), temperature(-1), pin(-1) { }
void setPin(uint8_t newPin) {
pin = newPin;
}
So now in Arduino sketch I can set the pin before usage. This solution is possible to do for the majority of libraries.
class SensorTemperatureHumidity: public Thread
{
private:
static const int Pin = 3;
Dht11 module;
public:
SensorTemperatureHumidity() {
module.setPin(Pin); // Set my pin usage
}
}

extending an exposed class using boost python

I want to extend a class which has already exposed to python. For example:
snippet 1:
class_<DataValueContainer, DataValueContainer::Pointer>( "DataValueContainer" )
.def( "__len__", &DataValueContainer::Size )
.def( VariableIndexingPython<DataValueContainer, Variable<std::string> >() )
.def( VariableIndexingPython<DataValueContainer, Variable<int> >() )
.def( VariableIndexingPython<DataValueContainer, Variable<double> >() )
.def( self_ns::str( self ) )
;
Now in a different place I want to extend python class DataValueContainer, such as:
snippet 2:
class_<DataValueContainer, DataValueContainer::Pointer>
.def( VariableIndexingPython<DataValueContainer, Variable<MyObject> >() )
Is it possible to do it using boost.python? The reason I want to do that since the snippet 1 is in the kernel of an existing code and I hesitate to modify it.
Best regards
As far as I know, this is not possible without making some minor modifications to snippet 1, or reimplementing parts of boost::python::class_. When an object of type boost::python::class_ is instantiated, type initialization and registration occurs within the internals of Boost.Python. Instantiating class_ with the same type and arguments will effectively overwrite the previous class object.
In Python, the problem is akin to a type being redefined. In the example below, the spam identifier is used for one type, and then associated with a different type:
>>> class spam:
... def action1(self): pass
...
>>> # Redefines spam type.
... class spam:
... def action2(self): pass
...
>>> print hasattr(s, "action1")
False
>>> print hasattr(s, "action2")
True
Rather than the spam type being extended via its identifier:
>>> class spam:
... def action1(self): pass
...
>>> # Extend spam type.
... def action2(s): pass
...
>>> spam.action2 = action2
>>> s = spam()
>>> print hasattr(s, "action1")
True
>>> print hasattr(s, "action2")
True
The Boost.Python examples are fairly equivalent to the Python examples. In this snippet, the spam identifier is modified to point to a new type, as a new class_ instance was instantiated.
#include <boost/python.hpp>
class spam {};
void action1(spam& self) {}
void action2(spam& self) {}
BOOST_PYTHON_MODULE(example)
{
typedef boost::python::class_<spam> spam_class_type;
spam_class_type("spam")
.def("action1", &action1)
;
// Redefines spam type.
spam_class_type("spam")
.def("action2", &action1)
;
}
And its usage:
>>> import example
__main__:1: RuntimeWarning: to-Python converter for spam already
registered; second conversion method ignored.
>>> s = example.spam()
>>> print hasattr(s, "action1")
False
>>> print hasattr(s, "action2")
True
To extend the type in Boost.Python, one simply operates on the same class_ instance:
#include <boost/python.hpp>
class spam {};
void action1(spam& self) {}
void action2(spam& self) {}
BOOST_PYTHON_MODULE(example)
{
typedef boost::python::class_<spam> spam_class_type;
spam_class_type spam_ = spam_class_type("spam");
spam_
.def("action1", &action1)
;
// Extend spam type.
spam_
.def("action2", &action2)
;
}
And its usage:
>>> import example
>>> s = example.spam()
>>> print hasattr(s, "action1")
True
>>> print hasattr(s, "action2")
True