<p>当一个函数通过Boost.Python被调用,Boost.Python将查询注册表,根据所需的C++类型,为每个调用方的参数找到一个合适的Python转换器。如果找到一个转换器,它知道如何将Python对象转换为C++对象,那么它将使用转换器构造C++对象。如果没有找到合适的转换器,则Boost.Python将引发<code>ArgumentError</code>异常。在</p>
<p>已注册from Python转换器:</p>
<ul>
<li>对于支持的类型自动Boost.Python,例如<code>int</code>和{<cd3>}</li>
<li>隐式地用于<a href="http://www.boost.org/doc/libs/1_60_0/libs/python/doc/html/reference/high_level_components.html#high_level_components.boost_python_class_hpp.class_template_class_t_bases_hel" rel="nofollow">^{<cd4>}</a>公开的类型。默认情况下,生成的Python类将持有一个嵌入的实例,该实例是^ {< CD5> } C++对象,并使用嵌入实例登记到Python和Python转换器,用于Python类和类型^ {< CD5> }。在</li>
<li>显式地通过<code>boost::python::converter::registry::push_back()</code></li>
</ul>
<p>测试可兑换性和构造对象的步骤分为两个不同的步骤。由于没有从Python converter为<code>boost::function<void(int)></code>注册,Boost.Python将引发<code>ArgumentError</code>异常。Boost.Python不会尝试构造<code>boost::function<void(int)></code>对象,尽管<code>boost::function<void(int)></code>可以从<code>boost::python::object</code>构造。在</p>
<hr/>
<p>要解决此问题,请考虑使用一个shim函数来延迟<code>boost::function<void(int)></code>的构造,直到<code>boost::python::object</code>通过Boost.Python图层:</p>
<pre class="lang-cpp prettyprint-override"><code>void http_manager_get_async_aux(
http_manager& self, std::string url, boost::python::object on_response)
{
return self.get_async(url, on_response);
}
...
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<http_manager>("HttpManager", python::no_init)
.def("get_async", &http_manager_get_async_aux);
...
}
</code></pre>
<p>这是一个完整的例子<a href="http://coliru.stacked-crooked.com/a/3ecd7bc2f6f1c322" rel="nofollow">demonstrating</a>这种方法:</p>
^{pr2}$
<p>交互式使用:</p>
<pre class="lang-python prettyprint-override"><code>>>> import example
>>> result = 0
>>> def f(r):
... global result
... result = r
...
>>> assert(result == 0)
>>> example.http.get_async('www.google.com', f)
>>> assert(result == 42)
>>> try:
... example.http.get_async('www.google.com', 42)
... assert(False)
... except TypeError:
... pass
...
</code></pre>
<p>另一种方法是显式地为<code>boost::function<void(int)></code>注册from Python转换器。这样做的好处是<em>所有</em>函数都通过Boost.Python可以使用转换器(例如,不需要为每个功能编写垫片)。但是,需要为每个C++类型注册一个转换。下面是一个示例<a href="http://coliru.stacked-crooked.com/a/208a77ccf2175f6a" rel="nofollow">demonstrating</a>显式注册<code>boost::function<void(int)></code>和<code>boost::function<void(std::string)></code>的自定义转换器:</p>
<pre class="lang-cpp prettyprint-override"><code>#include <boost/python.hpp>
#include <boost/function.hpp>
struct http_manager
{
void get_async(std::string url, boost::function<void(int)> on_response)
{
if (on_response)
{
on_response(42);
}
}
} http;
/// @brief Type that allows for registration of conversions from
/// python iterable types.
struct function_converter
{
/// @note Registers converter from a python callable type to the
/// provided type.
template <typename FunctionSig>
function_converter&
from_python()
{
boost::python::converter::registry::push_back(
&function_converter::convertible,
&function_converter::construct<FunctionSig>,
boost::python::type_id<boost::function<FunctionSig>>());
// Support chaining.
return *this;
}
/// @brief Check if PyObject is callable.
static void* convertible(PyObject* object)
{
return PyCallable_Check(object) ? object : NULL;
}
/// @brief Convert callable PyObject to a C++ boost::function.
template <typename FunctionSig>
static void construct(
PyObject* object,
boost::python::converter::rvalue_from_python_stage1_data* data)
{
namespace python = boost::python;
// Object is a borrowed reference, so create a handle indicting it is
// borrowed for proper reference counting.
python::handle<> handle(python::borrowed(object));
// Obtain a handle to the memory block that the converter has allocated
// for the C++ type.
typedef boost::function<FunctionSig> functor_type;
typedef python::converter::rvalue_from_python_storage<functor_type>
storage_type;
void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;
// Allocate the C++ type into the converter's memory block, and assign
// its handle to the converter's convertible variable.
new (storage) functor_type(python::object(handle));
data->convertible = storage;
}
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<http_manager>("HttpManager", python::no_init)
.def("get_async", &http_manager::get_async);
python::scope().attr("http") = boost::ref(http);
// Enable conversions for boost::function.
function_converter()
.from_python<void(int)>()
// Chaining is supported, so the following would enable
// another conversion.
.from_python<void(std::string)>()
;
}
</code></pre>