Boost.Python函数指针作为类构造函数参数 [英] Boost.Python function pointers as class constructor argument
问题描述
我有一个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_initdef
一个真实的__ 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 specifyboost::python::no_init
in theboost::python::class_
construction, and thendef
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
orstd::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 toSomeClass
, 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++ usingobject1.ptr() == object2.ptr()
, equivalent toid(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屋!