6

i want to embed a function written in python into c++ code.
My python code is:test.py

def func(x=None, y=None, z=None):  
  print x,y,z  

My c++ code is:

module = import("test");  
namespace = module.attr("__dict__");  

//then i want to know how to pass value 'y' only.  
module.attr("func")("y=1") // is that right?
Your Boss
  • 85
  • 7
yelo
  • 271
  • 2
  • 10

2 Answers2

1

I'm not sure Boost.Python implements the ** dereference operator as claimed, but you can still use the Python C-API to execute the method you are intested on, as described here.

Here is a prototype of the solution:

//I'm starting from where you should change
boost::python::object callable = module.attr("func");

//Build your keyword argument dictionary using boost.python
boost::python::dict kw;
kw["x"] = 1;
kw["y"] = 3.14;
kw["z"] = "hello, world!";

//Note: This will return a **new** reference
PyObject* c_retval = PyObject_Call(callable.ptr(), NULL, kw.ptr());

//Converts a new (C) reference to a formal boost::python::object
boost::python::object retval(boost::python::handle<>(c_retval));

After you have converted the return value from PyObject_Call to a formal boost::python::object, you can either return it from your function or you can just forget it and the new reference returned by PyObject_Call will be auto-deleted.

For more information about wrapping PyObject* as boost::python::object, have a look at the Boost.Python tutorial. More precisely, at this link, end of the page.

André Anjos
  • 4,641
  • 2
  • 27
  • 34
0

a theoretical answer (no time to try myself :-| ):

boost::python::dict kw;
kw["y"]=1;
module.attr("func")(**kw); 
eudoxos
  • 18,545
  • 10
  • 61
  • 110
  • It seems not ok. i can't understand what the '**kw' really means. but i have solved this problem by a little trick by calling like this 'module.attr("func")('',"1")'. thanks anyway! – yelo Jul 08 '11 at 09:27
  • boost::python imitates the ** operator on dictionary which unpacks dictionary into function arguments (see [tutorial](http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists)) The problem with your solution is that you depend on position of that particular argument, and need to have both parts of code in sync. – eudoxos Jul 10 '11 at 17:13
  • @eudoxos Can you point to the boost-python doc which mentions this? I couldn't find at http://www.boost.org/doc/libs/1_54_0/libs/python/doc/v2/dict.html – balki Aug 30 '13 at 23:24
  • 1
    They don't seem to be documented, though you find `object_core::operator*` returning `args_proxy`, and `args_proxy::operator*` returning `kwds_proxy` in `boost/python/object_core.hpp`. The call operator is then defined as `object operator()(detail::args_proxy const &args) const;` and `object operator()(detail::args_proxy const &args, detail::kwds_proxy const &kwds) const;`, which corresponds to `func(*args)` and func(*args,**kw) in python respectively. (Whoever downvoted my correct answer, can I get some explanation? I can improve it.) – eudoxos Sep 01 '13 at 19:38
  • 3
    PS the credit for me discovering `*` and `**` in boost::python goes to http://stackoverflow.com/a/6526185/761090. – eudoxos Sep 01 '13 at 19:41
  • You also need to pass an unpacked list as the first argument, like this: `module.attr("func")(*boost::python::make_tuple(), **kw);` – lav Dec 18 '18 at 07:44
  • Doesn't work for me. I get `TypeError: No to_python (by-value) converter found for C++ type: boost::python::detail::kwds_proxy` – ababak Jan 11 '19 at 17:28
  • Got it working by wrapping my positional arguments in *boost::python::make_tuple() – ababak Jan 11 '19 at 17:35