使用 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()
的 return 类型)转换为不兼容的类型。特别是,Boost.Python 目前不支持 std::unique_ptr
,因此不会转换为它。
- 第二个例子在调用函数声明它returns
void
. 时试图return上面提到的未指定类型
从 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")()
编辑的对象 return 具有未指定的类型,但可用于转换为 C++ 类型。如果 Python 抛出,覆盖的调用将抛出,如果从 Python 编辑的对象 return 不可转换为 C++ 类型,则转换为 C++ 类型将抛出。
这是一个完整的示例demonstrating two ways to adapt the returned Python object to a C++ object. As mentioned above, the override
conversion can be used. Alternatively, one can use 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))
我尝试将两个不同的 类 暴露给 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()
的 return 类型)转换为不兼容的类型。特别是,Boost.Python 目前不支持std::unique_ptr
,因此不会转换为它。 - 第二个例子在调用函数声明它returns
void
. 时试图return上面提到的未指定类型
从 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")()
编辑的对象 return 具有未指定的类型,但可用于转换为 C++ 类型。如果 Python 抛出,覆盖的调用将抛出,如果从 Python 编辑的对象 return 不可转换为 C++ 类型,则转换为 C++ 类型将抛出。
这是一个完整的示例demonstrating two ways to adapt the returned Python object to a C++ object. As mentioned above, the override
conversion can be used. Alternatively, one can use 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))