Boost.Python函数指针作为类构造函数参数 [英] Boost.Python function pointers as class constructor argument

查看:754
本文介绍了Boost.Python函数指针作为类构造函数参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个C ++类,在它的构造函数中需要一个函数指针( float(* myfunction)(vector< float> *)

我已经暴露了一些函数指针到Python。

使用这个类的理想方式是这样:

  import mymodule 
mymodule.some_class(mymodule.some_function)

关于此类的Boost如下:

  class_< SomeClass>(some_class,init< float ; *); 

但我得到:

 错误:没有匹配函数调用'register_shared_ptr1(示例(*)(std :: vector< double,std :: allocator< double> *))'


因此,任何人都有任何一个想要如何解决这个错误,而不会失去从函数指针的灵活性(即没有回落到指示哪个函数调用的字符串)?



在C ++中写这个代码的目的是为了速度。所以,如果我仍然能够保持这个好处(函数指针在初始化期间被分配给一个成员变量,并且将在一百万次以后被调用),这将是很好的。

解决方案

确定,所以这是一个相当困难的问题,一般来回答。你的问题的根本原因是,真的没有python类型,它完全相当于一个C函数指针。 Python函数是排序接近的,但它们的接口不匹配的几个原因。



首先,我想提一下从这里包装构造函数的技术:
http://wiki.python。 org / moin / boost.python / HowTo#namedconstructors.2BAC8factories.28asPythoninitializers.29 。这允许您为您的对象编写一个 __ init __ 函数,该函数不直接对应于实际的C ++构造函数。另请注意,您可能必须在 boost :: python :: class _ boost :: python :: no_init >构造,然后 def 一个真实的 __ init __ 函数,如果你的对象不是默认可构造的。 p>

返回问题:
是否只有一小部分函数通常需要传入?在这种情况下,你可以只声明一个特殊的枚举(或专门的类),使你的构造函数重载接受枚举,并使用它来查找真正的函数指针。你不能使用这种方法直接从python直接调用这些函数,但这并不是那么糟糕,性能将与使用真正的函数指针一样。



如果你想提供一个一般的方法,将工作任何python可调用,事情变得更复杂。你必须添加一个构造函数到你的C ++对象接受一般函数,例如。使用 boost :: function std :: tr1 :: function



因此,假设你添加了一个 boost,你可以替换现有的构造函数,因为函数指针会正确转换。 :: function 构造函数到 SomeClass ,你应该将这些函数添加到你的python包装代码:

  struct WrapPythonCallable 
{
typedef float * result_type;
explicit WrapPythonCallable(const boost :: python :: object& wrapped)
:wrapped_(wrapped)
{}
float * operator()(vector< float> * arg )const
{
//做任何你需要做的转换为
// boost :: python :: object here
boost :: python :: object arg_as_python_object = / * ... * /;

//使用对象调用python - 注意,wrapped_
//可以使用operator()重载调用,并返回
// boost :: python: :目的。
//此外,调用可以抛出boost :: python :: error_already_set -
//你可能想在这里处理。
boost :: python :: object result_object = wrapped_(arg_as_python_object);

//你可以使用boost :: python :: extract
float * result = / *来从result_object,
中抽取一个float *。 .. * /;
return result;
}
boost :: python :: object wrapped_;
};

//此函数是您将添加到SomeClass的构造函数包装器。
//更改返回类型以匹配SomeClass的持有人类型,就像使用shared_ptr持有的
//一样。
std :: auto_ptr< SomeClass> CreateSomeClassFromPython(
const boost :: python :: object& callable)
{
return std :: auto_ptr< SomeClass>(
new SomeClass(WrapPythonCallable(callable)))
}


//稍后,当告诉Boost.Python关于SomeClass:
class_< SomeClass>(some_class,no_init)
.def (__init__,make_constructor(& CreateSomeClassFromPython));

我省略了关于如何将指针转换和从python转换的细节 - 因为有对象生命期的问题。



如果你需要调用你将从Python传递给这个函数的函数指针,那么在某些时候你需要使用Boost.Python来 def 这些函数。这第二种方法将与这些def'd函数正常工作,但是调用它们将会很慢,因为每次调用它们时,对象都会被不必要地转换成Python语言。



为了解决这个问题,您可以修改 CreateSomeClassFromPython 来识别已知或常见的函数对象,并用它们的实际函数指针替换它们。你可以使用 object1.ptr()== object2.ptr(),等效于 id(object1)==在C ++中比较python对象的身份



最后,你当然可以结合一般的方法和枚举方法。请注意,这样做,boost :: python的重载规则不同于C ++,这可能会在处理像 CreateSomeClassFromPython 这样的函数时咬你。 Boost.Python按照它们被定义的顺序测试函数,以查看运行时参数是否可以转换为C ++参数类型。所以, CreateSomeClassFromPython 将阻止单参数构造函数def'd晚于它被使用,因为它的参数匹配任何python对象。一定要把它放在其他单参数 __ init __ 函数之后。



如果你发现自己在做这样的事情很多,那么你可能想看看一般boost :: function包装技术(在命名的构造函数技术的同一页上提到): http://wiki.python.org/moin/boost.python/HowTo?action=AttachFile&do=view& ; target = py_boost_function.hpp


I have a C++ class that requires a function pointer in it's constructor (float(*myfunction)(vector<float>*))
I've already exposed some function pointers to Python.
The ideal way to use this class is something like this:

import mymodule
mymodule.some_class(mymodule.some_function)

So I tell Boost about this class like so:

class_<SomeClass>("some_class", init<float(*)(vector<float>*)>);

But I get:

error: no matching function for call to 'register_shared_ptr1(Sample (*)(std::vector<double, std::allocator<double> >*))'

when I try to compile it.

So, does anyone have any ideas on how I can fix the error without losing the flexibility gained from function pointers (ie no falling back to strings that indicate which function to call)?

Also, the main point of writing this code in C++ is for speed. So it would be nice if I was still able to keep that benefit (the function pointer gets assigned to a member variable during initialization and will get called over a million times later on).

解决方案

OK, so this is a fairly difficult question to answer in general. The root cause of your problem is that there really is no python type which is exactly equivalent to a C function pointer. Python functions are sort-of close, but their interface doesn't match for a few reasons.

Firstly, I want to mention the technique for wrapping a constructor from here: http://wiki.python.org/moin/boost.python/HowTo#namedconstructors.2BAC8factories.28asPythoninitializers.29. This lets you write an __init__ function for your object that doesn't directly correspond to an actual C++ constructor. Note also, that you might have to specify boost::python::no_init in the boost::python::class_ construction, and then def a real __init__ function later, if your object isn't default-constructible.

Back to the question: Is there only a small set of functions that you'll usually want to pass in? In that case, you could just declare a special enum (or specialized class), make an overload of your constructor that accepts the enum, and use that to look up the real function pointer. You can't directly call the functions yourself from python using this approach, but it's not that bad, and the performance will be the same as using real function pointers.

If you want to provide a general approach that will work for any python callable, things get more complex. You'll have to add a constructor to your C++ object that accepts a general functor, e.g. using boost::function or std::tr1::function. You could replace the existing constructor if you wanted, because function pointers will convert to this type correctly.

So, assuming you've added a boost::function constructor to SomeClass, you should add these functions to your python wrapping code:

struct WrapPythonCallable
{
    typedef float * result_type;
    explicit WrapPythonCallable(const boost::python::object & wrapped) 
        : wrapped_(wrapped)
    { }
    float * operator()(vector<float>* arg) const
    {
        //Do whatever you need to do to convert into a 
        //boost::python::object here
        boost::python::object arg_as_python_object = /* ... */;

        //Call out to python with the object - note that wrapped_ 
        //is callable using an operator() overload, and returns 
        //a boost::python::object.
        //Also, the call can throw boost::python::error_already_set - 
        //you might want to handle that here.
        boost::python::object result_object = wrapped_(arg_as_python_object);

        //Do whatever you need to do to extract a float * from result_object,
        //maybe using boost::python::extract
        float * result = /* ... */;
        return result;
    }
    boost::python::object wrapped_;
};

//This function is the "constructor wrapper" that you'll add to SomeClass.
//Change the return type to match the holder type for SomeClass, like if it's
//held using a shared_ptr.
std::auto_ptr<SomeClass> CreateSomeClassFromPython(
    const boost::python::object & callable)
{
    return std::auto_ptr<SomeClass>(
        new SomeClass(WrapPythonCallable(callable)));
}


//Later, when telling Boost.Python about SomeClass:
class_<SomeClass>("some_class", no_init)
    .def("__init__", make_constructor(&CreateSomeClassFromPython));

I've left out details on how to convert pointers to and from python - that's obviously something that you'll have to work out, because there are object lifetime issues there.

If you need to call the function pointers that you'll pass in to this function from Python, then you'll need to def these functions using Boost.Python at some point. This second approach will work fine with these def'd functions, but calling them will be slow, because objects will be unnecessarily converted to and from Python every time they're called.

To fix this, you can modify CreateSomeClassFromPython to recognize known or common function objects, and replace them with their real function pointers. You can compare python objects' identity in C++ using object1.ptr() == object2.ptr(), equivalent to id(object1) == id(object2) in python.

Finally, you can of course combine the general approach with the enum approach. Be aware when doing this, that boost::python's overloading rules are different from C++'s, and this can bite you when dealing with functions like CreateSomeClassFromPython. Boost.Python tests functions in the order that they are def'd to see if the runtime arguments can be converted to the C++ argument types. So, CreateSomeClassFromPython will prevent single-argument constructors def'd later than it from being used, because its argument matches any python object. Be sure to put it after other single-argument __init__ functions.

If you find yourself doing this sort of thing a lot, then you might want to look at the general boost::function wrapping technique (mentioned on the same page with the named constructor technique): http://wiki.python.org/moin/boost.python/HowTo?action=AttachFile&do=view&target=py_boost_function.hpp.

这篇关于Boost.Python函数指针作为类构造函数参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆