Boost.Python函数指针作为类构造函数参数

时间:2010-09-04 07:39:33

标签: python performance compiler-errors function-pointers boost-python

我有一个C ++类,需要在其构造函数(float(*myfunction)(vector<float>*))中使用函数指针 我已经公开了一些Python的函数指针 使用这个类的理想方法是这样的:

import mymodule
mymodule.some_class(mymodule.some_function)

所以我告诉Boost这个类是这样的:

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

但我明白了:

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

当我尝试编译它时。

那么,有没有人对如何修复错误而不失去从函数指针获得的灵活性有任何想法(即没有回退到指示调用哪个函数的字符串)?

此外,在C ++中编写此代码的要点是速度。因此,如果我仍然能够保持这种好处(函数指针在初始化期间被分配给成员变量并且稍后将被调用超过一百万次),那将是很好的。

1 个答案:

答案 0 :(得分:2)

好的,所以这是一个相当难以回答的问题。你的问题的根本原因是确实没有python类型,它完全等同于C函数指针。 Python函数很接近,但由于一些原因,它们的接口不匹配。

首先,我想提一下从这里包装构造函数的技巧: http://wiki.python.org/moin/boost.python/HowTo#namedconstructors.2BAC8factories.28asPythoninitializers.29。这使您可以为对象编写一个__init__函数,该函数与实际的C ++构造函数不直接对应。另请注意,如果您的对象不是默认构造的,那么您可能必须在boost::python::no_init构造中指定boost::python::class_,然后在def指定真正的__init__函数。

回到问题: 您通常只想传递一小部分功能吗?在这种情况下,您可以声明一个特殊的枚举(或专门的类),使您的构造函数重载接受枚举,并使用它来查找实际的函数指针。你不能使用这种方法直接从python中调用函数,但它并没有那么糟糕,性能与使用实际函数指针相同。

如果你想提供一个适用于任何python可调用的通用方法,事情会变得更复杂。您必须向C ++对象添加一个构造函数,该对象接受一般的仿函数,例如:使用boost::functionstd::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
    {
        //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));

我遗漏了如何将指针转换为python和从python转换指针的细节 - 这显然是你必须解决的问题,因为那里存在对象生命周期问题。

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

要解决此问题,您可以修改CreateSomeClassFromPython以识别已知或常用的函数对象,并将其替换为实际的函数指针。您可以使用object1.ptr() == object2.ptr()在C ++中比较python对象的身份,相当于python中的id(object1) == id(object2)

最后,您当然可以将一般方法与枚举方法结合起来。请注意,在执行此操作时,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。< / p>