开发者

boost::python: howto call a function that expects a pointer?

I have a function that takes an int-pointer and exposed it via boost开发者_Python百科::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?


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.

Lets say that the object that you are passing in is not really an int.

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.

Let's say that you do not need to see what the int looks like after the function exits and you know that the function will not squirrel away the pointer to dereference after the function returns:

Your wrapper should now take the int by value or by const reference. Everything else is the same.


Maybe you ONLY need to see the after state (the int is an OUT perimeter):

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.


Both the input and the output are important and you know that the function will not squirrel away the pointer to dereference after the function returns:

Combine the two above. The wrapper takes one int by value and returns a different int.


The function expects to squirrel away the pointer to dereference after the function returns:

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.


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));


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()
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜