Reputation: 5815
We are trying to embed several Python procedures in our C++ code using and it fails with an error
TypeError: No to_python (by_value) converter found for C++ type: boost::python::detail::kwds_proxy
We honestly studied all the examples we managed to find over the network (this and this), but still we have no any clear solution for passing the ****kwargs** variable from C++ to Python. This failure seems to be very rare.
Python function we are trying to call recieves a string value and a dictionary:
from ipalib import api
user_kw = dict(givenname=u'Test', sn=u'User')
api.Command.user_add.(u'Pythonist', **user_kw)
This is its C++ implementation:
//Importing modules
bp::import("__main__");
ipalib = bp::import("ipalib");
ipalib_namespace = ipalib.attr("__dict__");
api = ipalib.attr("api");
... //Starting Python environment
//Construct args
std::string name = "Pythonist";
bp::dict new_user;
new_user["givenname"] = "Test";
new_user["sn"] = "User";
//Calling the function
bp::object user_add_wrapper = api.attr("Command").attr("user_add");
user_add_wrapper(name, new_user);
And on the last line Boost throws an exception. What are we doing wrong? Thank you.
Upvotes: 1
Views: 1153
Reputation: 51961
user_add_wrapper(name, new_user)
tries to pass new_user
as a dictionary to user_add_wrapper()
, rather than passing the unpacked contents of new_user
. The new_user
dictionary needs to be unpacked. Additionally, calling the python::object
with an unpacked dictionary requires the first argument to be an unpacked boost::python::tuple
. To account for these requirements, invoke user_add_wrapper()
as follows:
user_add_wrapper(*bp::make_tuple(name), **new_user);
This behavior is part of the seamless interoperability provided by Boost.Python, but it is rather obscure and I only recall noticing it mentioned indirectly in the change log rather than the tutorial or reference.
Below is a complete minimal example. Given example.py
:
def user_add(name, givenname, sn):
print locals()
The following program invokes user_add()
by unpacking a dictionary:
#include <boost/python.hpp>
int main()
{
Py_Initialize(); // Start interpreter.
// Create the __main__ module.
namespace python = boost::python;
python::object main = python::import("__main__");
python::object main_namespace = main.attr("__dict__");
try
{
python::object example = python::import("example");
python::object example_namespace = example.attr("__dict__");
// Construct args.
std::string name = "Pythonist";
python::dict new_user;
new_user["givenname"] = "Test";
new_user["sn"] = "User";
// Invoke user_add by unpacking the new_user dictionary.
example_namespace["user_add"](*python::make_tuple(name), **new_user);
}
catch (const python::error_already_set&)
{
PyErr_Print();
}
}
And produces the following output:
{'givenname': 'Test', 'sn': 'User', 'name': 'Pythonist'}
Upvotes: 3