我正在尝试在 python 中创建一个类,该类覆盖 C++ 类中的(纯)虚函数(使用 boost.python
)。问题是 C++ 类是通过静态成员函数创建的(所有构造函数都是私有(private)的或已删除)。我已经成功创建了 Python“知道”的 Base 类和 BaseWrap 类。我还能够创建一个可以在 python 中重写的纯虚函数。但是,我的问题是当 Base 的成员函数调用纯虚函数时。发生这种情况时,类找不到 python 实现,程序崩溃。
这是 C++ 代码:
#include <iostream>
#include <boost/python.hpp>
#include <boost/static_assert.hpp>
#define CREATE(NAME) \
static std::shared_ptr<NAME> Create() { \
std::cout << "STATIC BASE CREATE" << std::endl; \
return std::make_shared<NAME>(); \
}
class Base {
protected:
Base() { std::cout << "BASE DEFAULT CONSTRUCTOR" << std::endl; }
private:
std::string CallSay() {
return Say();
}
virtual std::string Say() const = 0;
};
class BaseWrap : public Base, public boost::python::wrapper<Base> {
public:
BaseWrap() : Base() { std::cout << "BASEWRAP DEFAULT CONSTRUCTOR" << std::endl; }
virtual std::string Say() const override
{
std::cout << "C++ Say" << std::endl;
return this->get_override("say") ();
}
CREATE(BaseWrap)
};
BOOST_PYTHON_MODULE(Example)
{
namespace python = boost::python;
// Expose Base.
python::class_<BaseWrap, std::shared_ptr<BaseWrap>, boost::noncopyable>("Base", python::no_init)
.def("__init__", python::make_constructor(&BaseWrap::Create))
.def("Say", python::pure_virtual(&Base::Say))
.def("CallSay", &Base::CallSay);
}
以及用于测试问题的Python代码:
import sys
import Example
class PythonDerived(Example.Base):
def __init__(self):
print "PYTHON DEFAULT CONSTRUCTOR"
Example.Base.__init__(self)
def Say(self):
return "Python Say"
d = PythonDerived()
print d
print
print d.Say()
print
print d.CallSay()
运行时会给出输出:
PYTHON DEFAULT CONSTRUCTOR
STATIC BASE CREATE
BASE DEFAULT CONSTRUCTOR
BASEWRAP DEFAULT CONSTRUCTOR
<__main__.PythonDerived object at 0x1091caf70>
Python Say
C++ Say
Traceback (most recent call last):
File "test.py", line 20, in <module>
print d.CallSay()
TypeError: 'NoneType' object is not callable
看起来Base::CallSay
方法正在查找BaseWrap::Say
的实现,但找不到python实现。有谁知道为什么或如何使其工作?
谢谢!
最佳答案
这看起来像是 Boost.Python 中的一个错误。
boost::python::wrapper
从 boost::python::make_constructor
返回的仿函数中层次结构未得到初始化。随着wrapper
层次结构没有 Python 对象的句柄,get_override()
返回NoneType
,并尝试调用NoneType
提出TypeError
异常。
要解决此问题,可以显式初始化 wrapper
等级制度。下面是一个完整的示例,提供了实现此目的的通用方法。而不是使用 make_constructor()
,可以使用make_wrapper_constructor()
。我选择不使用 C++11 功能。因此,将有一些样板代码可以通过可变参数模板来减少,但移植到 C++11 应该相当简单。
#include <iostream>
#include <boost/function_types/components.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/make_shared.hpp>
#include <boost/mpl/insert.hpp>
#include <boost/python.hpp>
namespace detail {
/// @brief wrapper_constructor will force the initialization
/// of the wrapper hierarchy when a class is held by
/// another type and inherits from boost::python::wrapper.
template <typename Fn>
class wrapper_constructor
{
public:
typedef typename boost::function_types::result_type<Fn>::type result_type;
public:
/// @brief Constructor.
wrapper_constructor(Fn fn)
: constructor_(boost::python::make_constructor(fn))
{}
/// @brief Construct and initialize python object.
result_type operator()(boost::python::object self)
{
constructor_(self);
return initialize(self);
}
/// @brief Construct and initialize python object.
template <typename A1>
result_type operator()(boost::python::object self, A1 a1)
{
constructor_(self, a1);
return initialize(self);
}
// ... overloads for arguments, or use variadic templates.
private:
/// @brief Explicitly initialize the wrapper.
static result_type initialize(boost::python::object self)
{
// Extract holder from self.
result_type ptr = boost::python::extract<result_type>(self);
// Explicitly initialize the boost::python::wrapper hierarchy.
initialize_wrapper(self.ptr(), // PyObject.
get_pointer(ptr)); // wrapper hierarchy.
return ptr;
}
private:
boost::python::object constructor_;
};
} // namespace detail
/// @brief Makes a wrapper constructor (constructor that works with
/// classes inheriting from boost::python::wrapper).
template <typename Fn>
boost::python::object make_wrapper_constructor(Fn fn)
{
// Python constructors take the instance/self argument as the first
// argument. Thus, inject the 'self' argument into the provided
// constructor function type.
typedef typename boost::function_types::components<Fn>::type
components_type;
typedef typename boost::mpl::begin<components_type>::type begin;
typedef typename boost::mpl::next<begin>::type self_pos;
typedef typename boost::mpl::insert<
components_type, self_pos, boost::python::object>::type signature_type;
// Create a callable python object that defers to the wrapper_constructor.
return boost::python::make_function(
detail::wrapper_constructor<Fn>(fn),
boost::python::default_call_policies(),
signature_type());
}
class Base
{
protected:
Base(int x) : x(x) { std::cout << "BASE DEFAULT CONSTRUCTOR" << std::endl; }
virtual ~Base() {}
int x;
public:
std::string CallSay() { return Say(); }
virtual std::string Say() const = 0;
};
class BaseWrap:
public Base,
public boost::python::wrapper<Base>
{
public:
BaseWrap(int x):
Base(x)
{ std::cout << "BASEWRAP DEFAULT CONSTRUCTOR" << std::endl; }
virtual std::string Say() const
{
std::cout << "C++ Say: " << x << std::endl;
return this->get_override("Say")();
}
static boost::shared_ptr<BaseWrap> Create(int x)
{
return boost::make_shared<BaseWrap>(x);
}
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Expose Base.
python::class_<BaseWrap, boost::shared_ptr<BaseWrap>,
boost::noncopyable>("Base", python::no_init)
.def("__init__", make_wrapper_constructor(&BaseWrap::Create))
.def("Say", python::pure_virtual(&Base::Say))
.def("CallSay", &Base::CallSay)
;
}
及其用法:
>>> import example
>>> class PythonDerived(example.Base):
... def __init__(self, x):
... print "PYTHON DEFAULT CONSTRUCTOR"
... example.Base.__init__(self, x)
... def Say(self):
... return "Python Say"
...
>>> d = PythonDerived(5)
PYTHON DEFAULT CONSTRUCTOR
BASE DEFAULT CONSTRUCTOR
BASEWRAP DEFAULT CONSTRUCTOR
>>> d
<__main__.PythonDerived object at 0xb7e688ec>
>>> d.Say()
'Python Say'
>>> d.CallSay()
C++ Say: 5
'Python Say'
关于c++ - 由静态方法(或工厂(?))创建的 boost python 对象中的虚拟覆盖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19254715/