简体   繁体   English

使用 boost::python 将虚拟成员函数从 C++ 暴露给 Python

[英]Exposing virtual member functions from C++ to Python using boost::python

I try to expose two different classes to python, but I don't get it to compile.我尝试向 python 公开两个不同的类,但我无法编译它。 I tried to follow the boost::python example, which works quite well.我尝试遵循 boost::python 示例,该示例效果很好。 But if I try to write the wrapper classes for my classes it doesn't work.但是,如果我尝试为我的类编写包装类,则它不起作用。 I have provided two minimal examples below:我在下面提供了两个最小的例子:

struct Base
{
    virtual ~Base() {}
    virtual std::unique_ptr<std::string> f() = 0;
};

struct BaseWrap : Base, python::wrapper<Base>
{
    std::unique_ptr<std::string> f()
    {
        return this->get_override("f")();
    }
};

and

struct Base
{
    virtual ~Base() {}
    virtual void f() = 0;
};

struct BaseWrap : Base, python::wrapper<Base>
{
    void f()
    {
        return this->get_override("f")();
    }
};

The first one does not compile because of the unique pointer(I think boost::python does not use unique pointers?) and the second example complains about the return statement inside the void function.第一个由于唯一指针而无法编译(我认为 boost::python 不使用唯一指针?),第二个示例抱怨 void 函数内的 return 语句。 Can someone help me how to solve this problems?有人可以帮我解决这个问题吗?

The examples are failing to compile because:这些示例无法编译,因为:

  • The first example attempts to convert an unspecified type (the return type of override::operator() ) to an incompatible type.第一个示例尝试将未指定的类型( override::operator()的返回类型)转换为不兼容的类型。 In particular, Boost.Python does not currently support std::unique_ptr , and hence will not convert to it.特别是,Boost.Python 当前不支持std::unique_ptr ,因此不会转换为它。
  • The second example attempts to return the unspecified type mentioned above when the calling function declares that it returns void .第二个示例尝试在调用函数声明它返回void时返回上面提到的未指定类型。

From a Python perspective, strings are immutable, and attempting to transferring ownership of a string from Python to C++ violates semantics.从 Python 的角度来看,字符串是不可变的,尝试将字符串的所有权从 Python 转移到 C++ 违反了语义。 However, one could create a copy of a string within C++, and pass ownership of the copied string to C++.但是,可以在 C++ 中创建字符串的副本,并将复制的字符串的所有权传递给 C++。 For example:例如:

std::unique_ptr<std::string> BaseWrap::f()
{
  // This could throw if the Python method throws or the Python
  // method returns a value that is not convertible to std::string.
  std::string result = this->get_override("f")();

  // Adapt the result to the return type.
  return std::unique_ptr<std::string>(new std::string(result));
}

The object returned from this->get_override("f")() has an unspecified type, but can be used to convert to C++ types.this->get_override("f")()返回的对象具有未指定的类型,但可用于转换为 C++ 类型。 The invocation of the override will throw if Python throws, and the conversion to the C++ type will throw if the object returned from Python is not convertible to the C++ type.如果 Python 抛出,则覆盖的调用将抛出,如果从 Python 返回的对象不可转换为 C++ 类型,则向 C++ 类型的转换将抛出。


Here is a complete example demonstrating two ways to adapt the returned Python object to a C++ object.这是一个完整的示例,演示了将返回的 Python 对象调整为 C++ 对象的两种方法。 As mentioned above, the override conversion can be used.如上所述,可以使用override转换。 Alternatively, one can use boost::python::extract<> , allowing one to check if the conversion will fail before performing the conversion:或者,可以使用boost::python::extract<> ,允许在执行转换之前检查转换是否会失败:

#include <memory> // std::unique_ptr
#include <boost/algorithm/string.hpp> // boost::to_upper_copy
#include <boost/python.hpp>

struct base
{
  virtual ~base() {}
  virtual std::unique_ptr<std::string> perform() = 0;
};

struct base_wrap : base, boost::python::wrapper<base>
{
  std::unique_ptr<std::string> perform()
  {
    namespace python = boost::python;
    // This could throw if the Python method throws or the Python
    // method returns a value that is not convertible to std::string.
    std::string result = this->get_override("perform")();

    // Alternatively, an extract could be used to defer extracting the
    // result.
    python::object method(this->get_override("perform"));
    python::extract<std::string> extractor(method());

    // Check that extractor contains a std::string without throwing.
    assert(extractor.check());

    // extractor() would throw if it did not contain a std::string.
    assert(result == extractor());

    // Adapt the result to the return type.
    return std::unique_ptr<std::string>(new std::string(result));
  }
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<base_wrap, boost::noncopyable>("Base", python::init<>())
    .def("perform", python::pure_virtual(&base::perform))
    ;

  python::def("make_upper", +[](base* object) {
    auto result = object->perform(); // Force dispatch through base_wrap.
    assert(result);
    return boost::to_upper_copy(*result);
  });
}

Interactive usage:互动使用:

>>> import example
>>> class Derived(example.Base):
...     def perform(self):
...         return "abc"
...        
>>> derived = Derived()
>>> assert("ABC" == example.make_upper(derived))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM