Boost(6):Boost.Python 如何转换 C++ 的参数和返回值类型

概述

在使用 Boost.Python 转换 C++ 程序到 Python 接口的过程中,我们经常需要处理 C++ 参数和返回类型,但是不同编程语言,支持的数据类型会很不一样。虽然对于 int/string 等简单的数据类型,C++ 和 Python 之间不需要任何处理就可以直接使用,但是如 C++ 中的引用、类、指针和 STL 等在 Python 中没有的数据类型,就需要 Boost.Python 进行额外处理才能在 python 程序中使用。

比如,封装以下 c++ 函数:

struct Y
{
    X x; Z* z;
    int z_value() { return z->value(); }
};

X& f(Y& y, Z* z)
{
    y.z = z;
    return y.x;
}

在 Python 程序中不能直接调用 X的方法,虽然可以将结果拷贝到一个新的 Python 对象中,如:

>>> f(y, z).set(42) # Result disappears
>>> y.x.get()       # No crash, but still bad
3.14

但是这样违背了 Boost.Python 希望尽可能还原 C++ 接口的初衷,且如果 C++ 函数更复杂一点,这种方法又将失效。

为了处理这些复杂的情况,Boost.Python 设计了三种处理模型对应不同的目标和情况。

Boost.Python 的策略

Boost.Python 的三种策略如下:

  • CallPolicies 策略: 定义 Boost.Python 转换 C++ 对象生成的 Python 对象的行为。
  • ResultConverter 策略: 定义如何将 C++ 的返回值转换成 _python。
  • ResultConverterGenerator 策略:这是 ResultConverter 模型的生成器

每个模型下定义了多种模型(以 class 的形式实现),用户在使用时并不直接调用上述策略,而是使用策略下的模型。不同的模型指定的功能和目标不同,常用模型如下:

  • with_custodian_and_ward: Ties lifetimes of the arguments
  • with_custodian_and_ward_postcall: Ties lifetimes of the arguments and results
  • return_internal_reference: Ties lifetime of one argument to that of result
  • return_value_policy with T one of:
  • reference_existing_object: naive (dangerous) approach
    • copy_const_reference: Boost.Python v1 approach
    • copy_non_const_reference:
    • manage_new_object: Adopt a pointer and hold the instance

注:在 Boost.Python 官方文档中的描述有一个小的混淆,即模型和策略没有分得很清楚,有的地方用策略,有的地方用模型。这里为了明确这一点,将三个大类定义为策略,而其具体实现称为模型。


如何使用 Boost.Python 模型

以上面的 f() 函数为例,我们在定义成 Python 接口时可以:

def("f", f,
    return_internal_reference<1,
        with_custodian_and_ward<1, 2> >());
  • return_internal_reference<1 :返回第一个参数 Y& y 拥有的内部引用 X&。
  • with_custodian_and_ward<1, 2> : 通知 Boost.Python ward 指示的参数的生命周期(即第二个参数:Z* z)取决于 custodian 指示的参数的生命周期(即第一个参数:Y& y)。

上述示例中我们定义了两个策略,如果需要定义多个策略,可以采用嵌套的方式:

policy1<args...,
    policy2<args...,
        policy3<args...> > >


CallPolicies 策略

CallPolicies 策略指定了 Boost.Python 将 C++ 对象转换成 Python 对象后,后者的行为。也就是说这个策略及其下定义的模型主要用于封装 C++ 类。

在模型中定义该 Python 对象的四种行为:

  • precall - Python 对象被调用前进行进行 Python 参数元组管理
  • result_converter - 如何处理 C++ 返回值
  • postcall - 在调用 Python 对象后进行的参数元组和结果管理
  • extract_return_type - 用于从给定类型序列中提取返回类型的元函数

即所有 CallPolicies 策略下的模型都要实现下表中的功能:

ExpressionTypeResult/Semantics
x.precall(a)convertible to boolreturns false and PyErr_Occurred() != 0 upon failure, true otherwise.
P::result_converterA model of ResultConverterGenerator.An MPL unary Metafunction Class used produce the “preliminary” result object.
x.postcall(a, r)convertible to PyObject*0 and PyErr_Occurred() != 0 upon failure. Must “conserve references” even in the event of an exception. In other words, if r is not returned, its reference count must be decremented; if another existing object is returned, its reference count must be incremented.
P::extract_return_typeA model of Metafunction.An MPL unary Metafunction used extract the return type from a given signature. By default it is derived from mpl::front.

为了能在同一个可调用对象中使用多个 CallPolicies 模型,Boost.Python 的 CallPolicies 类模板提供了一个链接接口,允许递归地组合他们。此接口采用可选模板参数 Base 的形式,默认为 default_call_policies

通常,Base 的 precall 函数在外部模板的 precall 函数之后调用, postcall 函数在外部模板的 postcall 函数之前调用, 如果外部模板提供了 result_converter 函数,它将替换 Base 提供的任何 result_converter函数。

在 Boost.Python 中 包含多个 CallPolicies 策略:default_call_policies, return_arg.hpp, return_internal_referencewith_custodian_and_ward

default_call_policies 模型

default_call_policies 是 CallPolicies 的一个模型,该模型没有调用前(precall)或调用后(postcall)的行为,并且有一个处理按值返回的 result_converter。默认情况下封装的 C++ 函数和成员函数会使用该模型,如果有需要,用户可以指定其他的模型或派生此类。

部分实现代码如下:

// boost/python/default_call_policies.hpp

struct default_call_policies
{
    // Ownership of this argument tuple will ultimately be adopted by
    // the caller.
    template <class ArgumentPackage>
    static bool precall(ArgumentPackage const&)
    {
        return true;
    }

    // Pass the result through
    template <class ArgumentPackage>
    static PyObject* postcall(ArgumentPackage const&, PyObject* result)
    {
        return result;
    }

    typedef default_result_converter result_converter;
    typedef PyObject* argument_package;

    template <class Sig> 
    struct extract_return_type : mpl::front<Sig>
    {
    };

};

struct default_result_converter
{
    template <class R>
    struct apply
    {
        typedef typename mpl::if_<
            mpl::or_<detail::is_pointer<R>, detail::is_reference<R> >
          , detail::specify_a_return_value_policy_to_wrap_functions_returning<R>
          , boost::python::to_python_value<
                typename detail::value_arg<R>::type
            >
        >::type type;
    };
};

// Exceptions for c strings an PyObject*s
template <>
struct default_result_converter::apply<char const*>
{
    typedef boost::python::to_python_value<char const*const&> type;
};

template <>
struct default_result_converter::apply<PyObject*>
{
    typedef boost::python::to_python_value<PyObject*const&> type;
};

可以看到,default_result_converter 类下的 precall 和 postcall 函数均为空,即不对这两个行为做处理。default_result_converter 类是 ResultConverterGenerator 的模型,可用于包装 C++ 函数,按值返回非指针类型、char const* 和 PyObject*。

这个模型也是其他模型的基类。

使用示例
在 Boost 的源代码中提供了一个默认的 CallPolicies 的结构体,即前文所述。默认情况下,在 Boost 内部也是使用该结构体,但是如果用户想要有其他不同的功能实现,也可以参数上面的示例,构建自己的 CallPolicies。使用方式与普通结构体一样,可以作为元素实例包在其他结构体中(这类结构体在Boost中有大量应用)如:

template <class Class, class CallPolicies = boost::python::default_call_policies>
struct def_init
{
  def_init(Class& cl, CallPolicies call_policies = CallPolicies())
    : cl(cl)
    , call_policies(call_policies)
  {}
  
  ...
  
  Class& cl;
  CallPolicies call_policies;
};

也可以在转换成python接口时直接指定,如:

// default.cpp
BOOST_PYTHON_MODULE(defaults_ext)
{
    ...
    class_<X>("X",no_init)

        .def(init<optional<int, char, std::string, double> >("doc of init", args("self", "a", "b", "c", "d")))
        .def(init<std::string, bool>(args("self", "s", "b"))[default_call_policies()]) // what's a good policy here?
        ...
        ;
}


return_arg 和 return_self 模型

return_arg 及其衍生类 return_self 的实例是基于 default_call_policies 的模型,使用这个模型的封装函数会返回指定的参数(return_arg 是指定参数,而 return_self 通常是 *this)。

// boost/python/return_arg.hpp
// class return_arg
namespace boost { namespace python
{
   template <size_t arg_pos=1, class Base = default_call_policies>
   struct return_arg : Base
   {
      static PyObject* postcall(PyObject*, PyObject* result);
      struct result_converter{ template <class T> struct apply; };
      template <class Sig> struct extract_return_type : mpl::at_c<Sig, arg_pos>{};

   };
}}

// class return_self
namespace boost { namespace python
{
   template <class Base = default_call_policies>
   struct return_self
     : return_arg<1,Base>
   {};
}}

其中 arg_pos=1 表示需要返回的参数位置。

使用示例
C++ code:

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/return_arg.hpp>

struct Widget
{
   Widget() :sensitive_(true){}
   bool get_sensitive() const { return sensitive_; }
   void set_sensitive(bool s) { this->sensitive_ = s; }
 private:
   bool sensitive_;
};

struct Label : Widget
{
   Label() {}

   std::string  get_label() const { return label_; }
   void set_label(const std::string &l){ label_ = l; }

 private:
   std::string label_;
};

using namespace boost::python;
BOOST_PYTHON_MODULE(return_self_ext)
{
   class_<widget>("Widget")
      .def("sensitive", &Widget::get_sensitive)
      .def("sensitive", &Widget::set_sensitive, return_self<>())
      ;

   class_<Label, bases<Widget> >("Label")
      .def("label", &Label::get_label)
      .def("label", &Label::set_label, return_self<>())
      ;
}

Python code:

>>> from return_self_ext import *
>>> l1 = Label().label("foo").sensitive(false)
>>> l2 = Label().sensitive(false).label("foo")

使用 return_self 模型之后,虽然 C++ 中,set_label() 函数并没有返回值,但是 Python 中 label(“foo”) 返回了一个可调用的 Python 对象(Label 对象本身)。

return_internal_reference 模型

这个类可以在不拷贝的情况下,直接返回封装函数的指针或引用。围绕指向 C++ 结果对象的指针构建一个 Python 对象,如果 Python 结果是有效的,则应用一些生命周期管理来保持 “self” 对象的活动。NULL 指针返回 None。

其部分实现代码如下:

// boost/python/return_internal_reference.hpp
namespace boost { namespace python
{
   template <std::size_t owner_arg = 1, class Base = default_call_policies>
   struct return_internal_reference : Base
   {
      static PyObject* postcall(PyObject*, PyObject* result);
      typedef reference_existing_object result_converter;
   };
}}

owner_arg=1表示要返回引用或指针对象的参数索引。如果用于封装成员函数,则参数1是目标对象(*this)。需要注意的是,如果目标 Python 对象类型不支持弱引用,则在调用被封装的函数时将引发 Python TypeError 异常。

Base = default_call_policies 表示其父类为 default_call_policies。

使用示例
C++ code:

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/return_internal_reference.hpp>

class Bar
{
 public:
   Bar(int x) : x(x) {}
   int get_x() const { return x; }
   void set_x(int x) { this->x = x; }
 private:
   int x;
};

class Foo
{
 public:
   Foo(int x) : b(x) {}

   // Returns an internal reference
   Bar const& get_bar() const { return b; }

 private:
   Bar b;
};

using namespace boost::python;
BOOST_PYTHON_MODULE(internal_refs)
{
   class_<Bar>("Bar", init<int>())
      .def("get_x", &Bar::get_x)
      .def("set_x", &Bar::set_x)
      ;

   class_<Foo>("Foo", init<int>())
      .def("get_bar", &Foo::get_bar
          , return_internal_reference<>())
      ;
}

Python code:

>>> from internal_refs import *
>>> f = Foo(3)
>>> b1 = f.get_bar()
>>> b2 = f.get_bar()
>>> b1.get_x()
3
>>> b2.get_x()
3
>>> b1.set_x(42)
>>> b2.get_x()
42

C++ 中 Foo() 的返回值是一个类 Bar, 在 Python 程序中, Foo() 返回的对象是根据 Bar 类指针构建的 Python

return_value_policy 模型

return_value_policy 实例化是 CallPolicies 的简单模型,由 ResultConverterGenerator 和可选的 Base CallPolicies 组成。表示产生 Python 返回值的策略。

其部分定义如下:

// boost/python/return_value_policy.hpp
namespace boost { namespace python
{
  template <class ResultConverterGenerator, class Base = default_call_policies>
  struct return_value_policy : Base
  {
      typedef ResultConverterGenerator result_converter;
  };
}}
ParameterRequirementsDefault
ResultConverterGeneratorResultConverterGenerator 的模型
BaseCallPolicies 模型default_call_policies

使用示例
使用 return_value_policy 模型还需要额外指定所用的 ResultConverterGenerator 模型。具体见 ResultConverterGenerator 模型。
C++ code:

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/return_value_policy.hpp>

// classes to wrap
struct Bar { int x; }

struct Foo {
   Foo(int x) : { b.x = x; }
   Bar const& get_bar() const { return b; }
 private:
   Bar b;
};

// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
   class_<Bar>("Bar");

   class_<Foo>("Foo", init<int>())
      .def("get_bar", &Foo::get_bar
          , return_value_policy<copy_const_reference>())
      ;
}

这里的 copy_const_reference 表示封装函数 get_bar() 返回一个 const 修饰的引用。

Python code:

>>> from my_module import *
>>> f = Foo(3)         # create a Foo object
>>> b = f.get_bar()    # make a copy of the internal Bar object


with_custodian_and_ward 和 with_custodian_and_ward_postcall 模型

这两个类是在函数的两个 Python 参数或结果对象之间建立生命周期依赖关系的工具。只要 Python 对象支持弱引用,则这两个类指定的参数或结果在该对象被销毁之前将一直有效。

为了避免无意中创建空指针,默认情况下在调用底层 C++ 对象之前进行生命周期绑定。但是在调用之前结果对象不可用,因此提供了 with_custodian_and_ward_postcall 来绑定调用后的生命周期。

Note: 这两个类需要目标 Python 对象支持弱引用,否则将触发 Python TypeError 异常。

部分实现代码如下:

// boost/python/with_custodian_and_ward.hpp

namespace boost { namespace python
{
   template <std::size_t custodian, std::size_t ward, class Base = default_call_policies>
   
   struct with_custodian_and_ward : Base
   {
      static bool precall(PyObject* args);
   };
   
   struct with_custodian_and_ward_postcall : Base
   {
      static PyObject* postcall(PyObject* args, PyObject* result);
   };
}}
  • custodian : 表示开始绑定生命周期的参数索引;1表示第一个参数;如果用于封装成员函数,则参数1表示目标对象(*this)。对于 with_custodian_and_ward_postcall, 0表示结果对象;
  • ward : 表示绑定声明周期的最大的参数索引。
  • Base : CallPolicies 模型,默认是 default_call_policies

使用示例
with_custodian_and_ward
例如,容器操作 append 通常使用 with_custodian_and_ward<1,2>,这意味着在容器本身处于活动状态时保持参数处于活动状态。

// c++
void reg(Creator* c) { mC = c; }

// python wrapper
.def("reg", &Factory::reg, with_custodian_and_ward<1,2>())

with_custodian_and_ward_postcall
// c++
request_with_value
communicator_irecv_content(const communicator& comm, int source, int tag,
                           content& c)
{
  request_with_value req(comm.irecv(source, tag, c.base()));
  req.m_external_value = &c.object;
  return req;
}

// python wrapper
.def("irecv", communicator_irecv_content,
     (arg("source") = any_source, arg("tag") = any_tag, arg("buffer")),
     with_custodian_and_ward_postcall<0, 4>()
     );


ResultConverter 策略

ResultConverter 策略指定如何将 C++ 的返回值转换成 _python 返回值。
从文档上看,所有 ResultConverter 下的模型都要实现下表中的功能:

ExpressionTypeSemantics
C c构造实例c
c.convertible()convertible to bool如果无法从任何 R 值转换为 Python 对象,则为 false。
c (r)convertible to PyObject*指向对应于 r 的 Python 对象的指针。如果 r 无法转换为 _python,此时出发 PyErr_Occurred。
c.get_pytype()PyTypeObject const *指向对应于转换结果的 Python Type 对象的指针。如果用于生成文档,则该值为0.

注:上表来自 Boost.Python 的官方文档,但是我在源代码中没有找到上述 convertible() 函数的实现。


to_python_indirect 模型

<boost/python/to_python_indirect.hpp> 提供了一种方法来构造新的 Python 对象,这些对象通过指针或智能指针保存包装的 C++ 类实例。

模板类 to_python_indirect 使用第二个参数提供的所有权策略将第一个参数类型的对象转换为 python 对象。
部分实现代码如下:

namespace boost { namespace python {

template <class T, class MakeHolder>
struct to_python_indirect
{
    template <class U>
    inline PyObject* operator()(U const& ref) const;
    inline PyTypeObject const* get_pytype()const;
    
 private:
    template <class U>
    inline PyObject* execute(U* ptr, detail::true_) const;
    
    template <class U>
    inline PyObject* execute(U const& x, detail::false_) const;
};
}}
  • T 表示要转换成 Python 类型的 C++ 类。
  • operator() 函数会创建一个适当类型的 Boost.Python 扩展类实例,使用 MakeHolder 从 x 创建一个 instance_holder,将 instance_holder 安装在新的扩展类实例中,并返回一个指向它的指针。

注:这部分内容从源代码中拷贝出来,与文档有出入。

使用示例
这个示例复制了 reference_existing_object 的功能,但没有一些编译时错误检查。

struct make_reference_holder
{
   typedef boost::python::objects::instance_holder* result_type;
   template <class T>
   static result_type execute(T* p)
   {
      return new boost::python::objects::pointer_holder<T*, T>(p);
   }
};

struct reference_existing_object
{
   // metafunction returning the ResultConverter
   template <class T>
   struct apply
   {
      typedef boost::python::to_python_indirect<T,make_reference_holder> type;
   };
};


to_python_value 模型

to_python_value 是 ResultConverter 的一个模型,它将 C++ 参数复制到一个新的 Python 对象中。

template <class T>
struct to_python_value
    : mpl::if_<
          detail::value_is_shared_ptr<T>
        , detail::shared_ptr_to_python_value<T>
        , typename mpl::if_<
              mpl::or_<
                  converter::is_object_manager<T>
                , converter::is_reference_to_object_manager<T>
              >
            , detail::object_manager_to_python_value<T>
            , detail::registry_to_python_value<T>
          >::type
      >::type
{
};

同样地,这里的代码跟文档也有很大出入。

注:其使用方式更多的是作为其他类的模板类,在实际开发 Boost.Python 转换程序时还没看到有什么具体应用使用这个策略。


ResultConverterGenerator 策略

ResultConverterGenerator 是一个 MPL 一元元函数类,对给定 C++ 函数的返回类型 T,该类返回该类型 T 的 ResultConverter。 Boost.Python 中的 ResultConverters 通常检查库的转换器注册表以找到合适的转换器,但不使用注册表的转换器也是可以的。

在下表中,G 表示 ResultConverterGenerator 类型,R 表示可能的 C++ 函数返回类型。

ExpressionRequirements
G::apply::typeA ResultConverter type for R.

如前文所说, ResultConverterGenerator 下的模型需要与 CallPolicies 的 return_value_policy 结合使用。即 return_value_policy 中的 T,
表示产生 Python 返回值的策略。

该策略包含的模型有:

  • reference_existing_object : 返回一个已存在实例的Python对象
  • copy_non_const_reference :返回一个没有用 const 修饰的对象,C++ 对象会复制到 Python 对象中
  • copy_const_reference : 返回一个用 const 修饰的对象,C++ 对象会复制到 Python 对象中
  • manage_new_object : 返回一个从堆中新分配的对象,需要在 Python 程序中释放该对象内存空间
  • return_by_value : 封装函数的返回值是简单的数据类型的引用,如 int, string 等, 这些值会被复制到 Python 中。
  • return_opaqe_pointer : 用于封装返回值是未定义类型的指针的 C++ 函数,会将该值复制到新的 Python 对象中

reference_existing_object 模型

这个模型用于修饰返回值是 C++ 对象引用或指针的函数,转换后的 Python 函数返回值是指向该 C++ 对象的指针。调用封装函数时,不会复制其返回值所引用的值。而是创建一个新的 Python 对象,该对象包含一个 unowned U* 指针,该指针指向封装函数返回值所指向的对象。

这个策略在 return_internal_reference 有应用。 NULL 指针也返回 None。

使用示例

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/reference_existing_object.hpp>
#include <boost/python/return_value_policy.hpp>
#include <utility>

// classes to wrap
struct Singleton
{
   Singleton() : x(0) {}

   int exchange(int n)  // set x and return the old value
   {
        std::swap(n, x);
        return n;
   }

   int x;
};

Singleton& get_it()
{
   static Singleton just_one;
   return just_one;
}

// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(singleton)
{
    def("get_it", get_it,
        return_value_policy<reference_existing_object>());

    class_<Singleton>("Singleton")
       .def("exchange", &Singleton::exchange)
       ;
}

Python 中 get_it() 函数会返回 Singleton 实例的指针对象。

>>> import singleton
>>> s1 = singleton.get_it()
>>> s2 = singleton.get_it()
>>> id(s1) == id(s2)  # s1 and s2 are not the same object
0
>>> s1.exchange(42)   # but they reference the same C++ Singleton
0
>>> s2.exchange(99)
42


copy_non_const_reference 模型

用于封装返回非 const 引用的 C++ 函数,以便将引用的值复制到新的 Python 对象中。

使用示例

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/copy_non_const_reference.hpp>
#include <boost/python/return_value_policy.hpp>

// classes to wrap
struct Bar { int x; }

struct Foo {
   Foo(int x) : { b.x = x; }
   Bar& get_bar() { return b; }
 private:
   Bar b;
};

// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
    class_<Bar>("Bar");

     class_<Foo>("Foo", init<int>())
        .def("get_bar", &Foo::get_bar
            , return_value_policy<copy_non_const_reference>())
       ;
}
>>> from my_module import *
>>> f = Foo(3)         # create a Foo object
>>> b = f.get_bar()    # make a copy of the internal Bar object


copy_const_reference 模型

用于封装返回一个引用到常量类型的 C++ 函数,以便将引用的值复制到新的 Python 对象中。

使用示例

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/return_value_policy.hpp>

// classes to wrap
struct Bar { int x; }

struct Foo {
   Foo(int x) : { b.x = x; }
   Bar const& get_bar() const { return b; }
 private:
   Bar b;
};

// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
    class_<Bar>("Bar");

     class_<Foo>("Foo", init<int>())
        .def("get_bar", &Foo::get_bar
            , return_value_policy<copy_const_reference>())
       ;
}
>>> from my_module import *
>>> f = Foo(3)         # create a Foo object
>>> b = f.get_bar()    # make a copy of the internal Bar object


manage_new_object 模型

这个模型用于返回封装函数的新分配的对象,如 new 语句的对象。需要注意的是,像 c++ 中的操作一样,用户需要手动从堆中删除该对象。

使用示例

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/manage_new_object.hpp>
#include <boost/python/return_value_policy.hpp>


struct Foo {
   Foo(int x) : x(x){}
   int get_x() { return x; }
   int x;
};

Foo* make_foo(int x) { return new Foo(x); }

// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
    def("make_foo", make_foo, return_value_policy<manage_new_object>())
    class_<Foo>("Foo")
        .def("get_x", &Foo::get_x)
        ;
}
>>> from my_module import *
>>> f = make_foo(3)     # create a Foo object
>>> f.get_x()
3


return_by_value 模型

这个模型用于封装函数的返回值是任何类型的引用或值。返回值被复制到一个新的 Python 对象中。这里的引用或值是指普通的数据类型,如 string, int 等 c++ 和 python 共有的基本数据类型。

使用示例

#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/return_by_value.hpp>
#include <boost/python/return_value_policy.hpp>

// classes to wrap
struct Bar { };

Bar global_bar;

// functions to wrap:
Bar b1();
Bar& b2();
Bar const& b3();

// Wrapper code
using namespace boost::python;
template <class R>
void def_void_function(char const* name, R (*f)())
{
   def(name, f, return_value_policy<return_by_value>());
}

BOOST_PYTHON_MODULE(my_module)
{
    class_<Bar>("Bar");
    def_void_function("b1", b1);
    def_void_function("b2", b2);
    def_void_function("b3", b3);
}
>>> from my_module import *
>>> b = b1() # each of these calls
>>> b = b2() # creates a brand
>>> b = b3() # new Bar object


return_opaqe_pointer 模型

此模型用于封装返回值是未定义类型的指针的 C++ 函数,会将该值复制到新的 Python 对象中。

除了指定 return_opaque_pointer 策略之外,还必须使用 BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID 宏来为返回的指针指向的类型定义 type_id 函数的特化。

使用示例

# include <boost/python/return_opaque_pointer.hpp>
# include <boost/python/def.hpp>
# include <boost/python/module.hpp>
# include <boost/python/return_value_policy.hpp>

typedef struct opaque_ *opaque;

opaque the_op   = ((opaque) 0x47110815);

opaque get () { return the_op; }
void use (opaque op) {
    if (op != the_op)
	throw std::runtime_error (std::string ("failed"));
}

void failuse (opaque op) {
    if (op == the_op)
	throw std::runtime_error (std::string ("success"));
}

BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID(opaque_)

namespace bpl = boost::python;

BOOST_PYTHON_MODULE(opaque_ext)
{
    bpl::def (
        "get", &::get, bpl::return_value_policy<bpl::return_opaque_pointer>());
    bpl::def ("use", &::use);
    bpl::def ("failuse", &::failuse);
}
"""
>>> from opaque_ext import *
>>> #
>>> # Check for correct conversion
>>> use(get())
>>> failuse(get())
Traceback (most recent call last):
        ...
RuntimeError: success
>>> #
>>> # Check that there is no conversion from integers ...
>>> use(0)
Traceback (most recent call last):
        ...
TypeError: bad argument type for built-in operation
>>> #
>>> # ... and from strings to opaque objects
>>> use("")
Traceback (most recent call last):
        ...
TypeError: bad argument type for built-in operation
"""
def run(args = None):
    import sys
    import doctest

    if args is not None:
        sys.argv = args
    return doctest.testmod(sys.modules.get(__name__))

if __name__ == '__main__':
    print "running..."
    import sys
    sys.exit(run()[0])


参考资料

boost.python/CallPolicy
Boost.Python - Chapter 1. Concepts
Boost Call Policies
Models of CallPolicies
ResultConverter
Models of ResultConverterGenerator
Models of ResultConverter

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用Boost::Python可以非常方便地实现C++调用Python接口。以下是简单的步骤: 1. 安装Boost库和Python解释器。 2. 编写一个Python模块,在其中定义一些函数或类,这些函数或类是你希望C++调用的接口。 3. 用Boost::Python库Python模块导出C++中,使得C++可以调用Python模块中的函数和类。 4. 在C++代码中调用Python模块中的函数或类。 下面是一个简单的示例代码,演示了如何使用Boost::Python实现C++调用Python接口: ```cpp #include <boost/python.hpp> #include <iostream> // 定义一个Python函数 int add(int x, int y) { return x + y; } // 导出Python函数到C++ BOOST_PYTHON_MODULE(example) { using namespace boost::python; def("add", add); } int main() { // 初始化Python解释器 Py_Initialize(); // 导入Python模块 boost::python::object example_module = boost::python::import("example"); // 调用Python函数 boost::python::object result = example_module.attr("add")(1, 2); // 将Python返回值转换C++类型 int sum = boost::python::extract<int>(result); // 输出结果 std::cout << "1 + 2 = " << sum << std::endl; // 释放Python解释器 Py_Finalize(); } ``` 在这个例子中,我们定义了一个名为`add`的Python函数,在C++中通过`def`函数将其导出。然后,在C++代码中导入了Python模块,调用了Python函数,并将其返回值转换C++类型。最后输出了结果。 这只是一个简单的例子,Boost::Python还支持更复杂的数据类型和类的导出。如果你想深入了解Boost::Python使用,可以参考官方文档和示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翔底

您的鼓励将是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值