簡體   English   中英

使用 boost::python 將虛擬成員函數從 C++ 暴露給 Python

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

我嘗試向 python 公開兩個不同的類,但我無法編譯它。 我嘗試遵循 boost::python 示例,該示例效果很好。 但是,如果我嘗試為我的類編寫包裝類,則它不起作用。 我在下面提供了兩個最小的例子:

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")();
    }
};

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

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

第一個由於唯一指針而無法編譯(我認為 boost::python 不使用唯一指針?),第二個示例抱怨 void 函數內的 return 語句。 有人可以幫我解決這個問題嗎?

這些示例無法編譯,因為:

  • 第一個示例嘗試將未指定的類型( override::operator()的返回類型)轉換為不兼容的類型。 特別是,Boost.Python 當前不支持std::unique_ptr ,因此不會轉換為它。
  • 第二個示例嘗試在調用函數聲明它返回void時返回上面提到的未指定類型。

從 Python 的角度來看,字符串是不可變的,嘗試將字符串的所有權從 Python 轉移到 C++ 違反了語義。 但是,可以在 C++ 中創建字符串的副本,並將復制的字符串的所有權傳遞給 C++。 例如:

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));
}

this->get_override("f")()返回的對象具有未指定的類型,但可用於轉換為 C++ 類型。 如果 Python 拋出,則覆蓋的調用將拋出,如果從 Python 返回的對象不可轉換為 C++ 類型,則向 C++ 類型的轉換將拋出。


這是一個完整的示例,演示了將返回的 Python 對象調整為 C++ 對象的兩種方法。 如上所述,可以使用override轉換。 或者,可以使用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);
  });
}

互動使用:

>>> 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