Reputation: 397
I have a function that takes an int-pointer and exposed it via boost::python. How can I call this function from python?
in C++ with boost::python:
void foo(int* i);
...
def("foo", foo);
in python:
import foo_ext
i = 12
foo_ext.foo(i)
results in
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
foo(int)
did not match C++ signature:
foo(int* i)
So how to pass a pointer?
Upvotes: 6
Views: 7819
Reputation: 1451
In most cases you can avoid raw pointer passing to the function, but when it's really required you can make Python object for the C++ pointer to the original object using adapter in such way:
template<typename PtrT>
struct PtrAdapter {
auto& get(PtrT ptr) { return *ptr; }
};
then define mapping of the pointer type to Python object and allow implicit conversion:
class_<Cluster<LinksT>*, noncopyable>(typpedName<LinksT>("ClusterPtr", true, true)
, "Raw hierarchy cluster pointer\n")
.def("__call__", &PtrAdapter<Cluster<LinksT>*>::get,
return_internal_reference<>(),
"referenced cluster")
;
register_ptr_to_python<Cluster<LinksT>*>();
Note that original object type also should have mapping to the Python object (in this case Cluster<LinksT>
).
Then for such C++ code:
Cluster<LinksT>* cl = clusters.head();
process(cl);
Id cid = cl->id();
You can use similar Python code:
cl = clusters.head()
process(cl)
cid = cl.id()
Upvotes: 1
Reputation: 15551
Short answer is: You can't. Python does not have pointers
Long answer is: There are assorted workarounds depending on use-case.
I notice that you are using an int and an int* in your example. Int (along with float, str, and bool) is a special case because it is immutable in python.
Have a wrapper function that takes the argument as a reference, takes the address and passes it on to the actual function. This will work seamlessly in python.
Ok, so say it really was an int. Now you have a problem. You can not change the int you passed in. If you try the same solution, boost::python will complain about l-values at runtime. There are still several options.
Your wrapper should now take the int by value or by const reference. Everything else is the same.
Your wrapper function will now take no arguments, and will pass the address of a local int to the actual function. It will return that value. If you function already has a return value it should now return a tuple.
Combine the two above. The wrapper takes one int by value and returns a different int.
There is no real good solution. You can create and expose an object in c++ that contains a c++ int. The wrapper will take that object by reference, extract the address of the contained int and pass it on to the actual function. Keeping the object alive in python (and safe from the garbage collector) until the library is done with it is now the python writer's problem, and if he goofs the data is corrupt or the interpretor crashes.
Upvotes: 4
Reputation: 28036
From python.org's boost.python HowTo
Perhaps you'd like the resulting Python object to contain a raw pointer to the argument? In that case, the caveat is that if the lifetime of the C++ object ends before that of the Python object, that pointer will dangle and using the Python object may cause a crash.
Here's how to expose mutable C++ object during module initialisation:
scope().attr("a") = object(ptr(&class_instance));
Upvotes: 1