好的,所以总的来说,这是一个相当难以回答的问题。问题的根本原因是确实没有与 C 函数指针完全等效的 python 类型。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::function
or 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
{
//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 传递给此函数的函数指针,那么您将需要def
在某些时候使用 Boost.Python 来调用这些函数。第二种方法可以很好地处理这些 def'd 函数,但调用它们会很慢,因为每次调用对象时都会不必要地与 Python 相互转换。
要解决此问题,您可以修改CreateSomeClassFromPython
以识别已知或常见的函数对象,并将它们替换为它们的真实函数指针。您可以在 C++ 中使用 比较 python 对象的身份object1.ptr() == object2.ptr()
,相当于id(object1) == id(object2)
在 python 中。
最后,您当然可以将通用方法与枚举方法结合起来。执行此操作时请注意,boost::python 的重载规则与 C++ 的不同,在处理诸如CreateSomeClassFromPython
. Boost.Python 按照定义的顺序测试函数,以查看运行时参数是否可以转换为 C++ 参数类型。因此,CreateSomeClassFromPython
将阻止使用比它更晚定义的单参数构造函数,因为它的参数匹配任何 python 对象。请务必将其放在其他单参数__init__
函数之后。
如果你发现自己经常做这种事情,那么你可能想看看一般的 boost::function 包装技术(在同一页面上提到了命名构造函数技术):http ://wiki.python.org/ moin/boost.python/HowTo?action=AttachFile&do=view&target=py_boost_function.hpp。