在boost python中公开C ++接口

时间:2013-11-18 18:15:04

标签: c++ python boost-python

示例代码说明:

struct Base
{
  virtual int foo() = 0;
};

struct Derived : public Base
{
  virtual int foo()
  {
    return 42;
  }
};

Base* get_base()
{
  return new Derived;
}

BOOST_PYTHON_MODULE(libTestMod)
{
  py::class_<Base>("Base", py::no_init)
    .def("foo", py::pure_virtual(&Base::foo));

  py::def("get_base", get_base, py::return_internal_reference<>()); //ignore mem leak
}
  • 不会在python
  • 中覆盖Base :: foo
  • Base:foo将在c ++中实现,但不应该暴露给python

尝试了上面的代码,但无法编译。

更新 编译错误:

/path/to/boostlib/boost/1.53.0-0/common/include/boost/python/object/value_holder.hpp:66:11: error: cannot declare field 'boost_1_53_0::python::objects::value_holder<Base>::m_held' to be of abstract type 'Base'
Main.C:59:8: note:   because the following virtual functions are pure within 'Base':
Main.C:61:15: note:         virtual int Base::foo()

3 个答案:

答案 0 :(得分:9)

抽象C ++类不能以这种方式暴露给Boost.Python。 Boost.Python tutorial给出了如何公开纯虚函数的示例。简而言之,在使用boost::python::pure_virtual装饰方法时,需要创建一个包装类型以允许C ++多态解析虚函数,并且虚函数实现将委托在Python对象的层次结构中以多态方式解析函数。

struct BaseWrap : Base, boost::python::wrapper<Base>
{
  int foo()
  {
    return this->get_override("foo")();
  }
};

...

boost::python::class_<BaseWrap>("Base", ...)
  .def("foo", boost::python::pure_virtual(&Base::foo))
  ;

有关详细信息,当通过boost::python::class_公开类型时,HeldType默认为公开的类型,HeldType是在Python对象中构造的。 class_文档声明:

  

模板参数:

     
      
  • T:被包裹的课程
  •   
  • HeldType:指定实际嵌入在包裹T实例[...]的Python对象中的类型。默认为T
  •   

因此,boost::python::class_<Base>将失败,因为T = BaseHeldType = Base,并且Boost.Python将尝试将HeldType的对象实例化为表示Base的实例。此实例化将失败,因为Base是一个抽象类。


这是一个完整的示例,显示了BaseWrap类的使用。

#include <boost/python.hpp>

struct Base
{
  virtual int foo() = 0;
  virtual ~Base() {}
};

struct Derived : public Base
{
  virtual int foo()
  {
    return 42;
  }
};

Base* get_base()
{
  return new Derived;
}

namespace python = boost::python;

/// @brief Wrapper that will provide a non-abstract type for Base.
struct BaseWrap : Base, python::wrapper<Base>
{
  BaseWrap() {}

  BaseWrap(const Base& rhs)
    : Base(rhs)
  {}

  int foo()
  {
    return this->get_override("foo")();
  }
};

BOOST_PYTHON_MODULE(example)
{
  python::class_<BaseWrap>("Base")
    .def("foo", python::pure_virtual(&Base::foo));
    ;

  python::def("get_base", &get_base,
              python::return_value_policy<python::manage_new_object>());
}

及其用法:

>>> import example
>>> class Spam(example.Base):
...     pass
... 
>>> Spam().foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: Pure virtual function called
>>> class Egg(example.Base):
...     def foo(self):
...         return 100
... 
>>> e = Egg()
>>> e.foo()
100
>>> d = example.get_base()
>>> d.foo()
42

可以通过在没有默认初始值设定项(boost::python::no_init)和不可复制(boost::noncopyable)的情况下公开抽象类来在Boost.Python中公开它。缺少初始化程序会阻止Python类型从中有效地防止覆盖。此外,由Base::foo()在C ++中实现Derived的实现细节是无关紧要的。如果Python根本不了解foo()方法,则省略通过def()公开它。

#include <boost/python.hpp>

struct Base
{
  virtual int foo() = 0;
  virtual ~Base() {}
};

struct Derived
  : public Base
{
  virtual int foo()
  {
    return 42;
  }
};

struct OtherDerived
  : public Base
{
  virtual int foo()
  {
    return 24;
  }
};

Base* get_base()
{
  return new Derived;
}

Base* get_other_base()
{
  return new OtherDerived;
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<Base, boost::noncopyable>("Base", python::no_init)
    ;

  python::class_<Derived, python::bases<Base> >("Derived", python::no_init)
    .def("foo", &Base::foo)
    ;

  python::class_<OtherDerived, python::bases<Base> >(
      "OtherDerived", python::no_init)
    ;

  python::def("get_base", &get_base,
              python::return_value_policy<python::manage_new_object>());

  python::def("get_other_base", &get_other_base,
              python::return_value_policy<python::manage_new_object>());
}

交互式使用:

>>> import example
>>> b = example.get_base()
>>> b.foo()
42
>>> b = example.get_other_base()
>>> b.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'OtherDerived' object has no attribute 'foo'

答案 1 :(得分:6)

抽象类实际上 CAN 在没有包装器的情况下暴露给Boost.Python。 诀窍是使用 boost :: noncopyable 定义您的类,并避免使用 pure_virtual 方法包装器。

这是更正的代码(使用Boost.Python 1.47.0和Python 2.7.6测试):

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

struct Base
{
  virtual int foo() = 0;
};

struct Derived : public Base
{
  virtual int foo()
  {
    return 42;
  }
};

Base* get_base()
{
  return new Derived;
}

BOOST_PYTHON_MODULE(libTestMod)
{
    namespace py = boost::python;

    py::class_<Base, boost::noncopyable>("Base", py::no_init)
        .def("foo", &Base::foo);

    py::def("get_base", get_base,
        py::return_value_policy<py::reference_existing_object>()); //ignore mem leak
}

测试:

$ python
Python 2.7.6 (default, Mar 31 2014, 16:04:58) 
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import libTestMod
>>> base = libTestMod.get_base()
>>> print base.foo()
42

答案 2 :(得分:0)

接受的答案是正确的,但Visual Studio 2015在编译器中包含bug,会导致接受的答案出现以下链接错误:

  

错误LNK2001未解析的外部符号“struct Base const volatile *   __cdecl boost :: get_pointer(struct Base const volatile *)“   (?? $ get_pointer @ $$ CDUBase @@@ boost @@ YAPDUBase @@ PDU1 @@ Z)test_boost_class

以下是已接受答案的VS2015编译器错误解决方法:

namespace boost {
    template <>
    inline Base const volatile * get_pointer(struct Base const volatile *ptr) {
        return ptr;
    }
}