如何使用 boost/python 将 C++ 虚函数公开给 python?
How to expose C++ virtual functions to python using boost/python?
编辑:将构造函数和析构函数设为虚拟没有帮助
根据 boost/python documentation,我可以通过包装器来公开虚函数。这是他们在此处复制粘贴的示例:
// base class
struct Base
{
virtual ~Base() {}
virtual int f() { return 0; }
};
// wrapper for the base function
struct BaseWrap : Base, wrapper<Base>
{
int f()
{
if (override f = this->get_override("f"))
return f(); // *note*
return Base::f();
}
int default_f() { return this->Base::f(); }
};
// exposing the class
class_<BaseWrap, boost::noncopyable>("Base")
.def("f", &Base::f, &BaseWrap::default_f);
这是我的 cpp 文件,其中包含复制错误所需的最少代码:
#include <iostream>
#include <python3.6m/Python.h>
#include <boost169/boost/python.hpp>
#include <boost169/boost/python/make_constructor.hpp>
#include <boost169/boost/python/detail/api_placeholder.hpp>
using namespace boost::python;
class BaseNode{
public:
BaseNode(boost::python::object position);
~BaseNode();
boost::python::object _position;
virtual int test(){
return 10;
}
};
class BaseNodeVirtual:BaseNode, boost::python::wrapper<BaseNode>{
public:
int test(){
if (override test = this->get_override("test"))
return test();
return BaseNode::test();
}
int default_test(){
return this->BaseNode::test();
}
};
// pickle support for BaseNode
struct BaseNode_pickle_suite : boost::python::pickle_suite{
static boost::python::tuple getinitargs(BaseNode const& baseNode){
return boost::python::make_tuple(baseNode._position);
}
static boost::python::tuple getstate(boost::python::object obj)
{
BaseNode& baseNode = boost::python::extract<BaseNode&>(obj);
return boost::python::make_tuple(obj.attr("__dict__"));
}
static void setstate(boost::python::object obj, boost::python::tuple state)
{
BaseNode& baseNode = boost::python::extract<BaseNode&>(obj);
boost::python::dict d = extract<dict>(obj.attr("__dict__"));
d.update(state[0]);
}
static bool getstate_manages_dict() { return true; }
};
BaseNode::BaseNode(boost::python::object position){
this->_position = position;
}
BaseNode::~BaseNode(){
}
BOOST_PYTHON_MODULE(BaseNode){
class_<BaseNodeVirtual, bases<BaseNode,boost::python::wrapper<BaseNode>>>("BaseNode", init<boost::python::object>())
.def_readwrite("_position", &BaseNode::_position)
.def_pickle(BaseNode_pickle_suite())
.def("test", &BaseNode::test, &BaseNodeVirtual::default_test);
}
它给出了一个我不太明白的长错误。
/usr/include/boost169/boost/python/object/value_holder.hpp:133:13: note: candidates are:
minNode.cpp:17:7: note: BaseNodeVirtual::BaseNodeVirtual(const BaseNodeVirtual&)
class BaseNodeVirtual: BaseNode, boost::python::wrapper<BaseNode>{
^
minNode.cpp:17:7: note: no known conversion for argument 1 from ‘const boost::python::api::object’ to ‘const BaseNodeVirtual&’
minNode.cpp:17:7: note: BaseNodeVirtual::BaseNodeVirtual(BaseNodeVirtual&&)
minNode.cpp:17:7: note: no known conversion for argument 1 from ‘const boost::python::api::object’ to ‘BaseNodeVirtual&&’
In file included from /usr/include/boost169/boost/preprocessor/iteration/detail/iter/forward1.hpp:52:0,
from /usr/include/boost169/boost/python/object/value_holder.hpp:46,
from /usr/include/boost169/boost/python/object/class_metadata.hpp:14,
from /usr/include/boost169/boost/python/class.hpp:23,
from /usr/include/boost169/boost/python.hpp:18,
from minNode.cpp:3:
/usr/include/boost169/boost/python/object/value_holder.hpp:135:80: erreur: ‘boost::python::detail::wrapper_base’ is an inaccessible base of ‘BaseNodeVirtual’
{
^
make: *** [minNode.o] Erreur 1
最初我认为这与私有继承有关(因为根据 this post and 这是受保护和私有继承带来的问题),所以我尝试将继承更改为 public 相反,但它仍然给了我一个错误(基数 类 为 public 并且没有在基数中指定 boost::python::wrapper):
/usr/include/boost169/boost/python/object/value_holder.hpp:133:13: erreur: no matching function for call to ‘BaseNodeVirtual::BaseNodeVirtual(boost::reference_wrapper<const BaseNode>::type&)’
BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_UNFORWARD_LOCAL, nil)
^
/usr/include/boost169/boost/python/object/value_holder.hpp:133:13: note: candidates are:
minNode.cpp:17:7: note: BaseNodeVirtual::BaseNodeVirtual(const BaseNodeVirtual&)
class BaseNodeVirtual:public BaseNode, public boost::python::wrapper<BaseNode>{
^
minNode.cpp:17:7: note: no known conversion for argument 1 from ‘boost::reference_wrapper<const BaseNode>::type {aka const BaseNode}’ to ‘const BaseNodeVirtual&’
minNode.cpp:17:7: note: BaseNodeVirtual::BaseNodeVirtual(BaseNodeVirtual&&)
minNode.cpp:17:7: note: no known conversion for argument 1 from ‘boost::reference_wrapper<const BaseNode>::type {aka const BaseNode}’ to ‘BaseNodeVirtual&&’
make: *** [minNode.o] Erreur 1
我该如何解决这个问题?
谢谢
为了匹配示例和构建而没有任何错误,您需要将包装器的 class
更改为 struct
,因为 Boost 使用 struct 来布局内存。此外,您还需要在包装器中明确定义您的自定义构造函数。最后,将行 bases(...)
更改为 boost::noncopyable
:
#include <iostream>
#include <python3.6m/Python.h>
#include <boost169/boost/python.hpp>
#include <boost169/boost/python/make_constructor.hpp>
#include <boost169/boost/python/detail/api_placeholder.hpp>
using namespace boost::python;
class BaseNode{
public:
BaseNode(boost::python::object position);
~BaseNode();
boost::python::object _position;
virtual int test(){ return 10; }
};
BaseNode::BaseNode(boost::python::object position){
this->_position = position;
}
BaseNode::~BaseNode(){
}
// pickle support for BaseNode
struct BaseNode_pickle_suite : boost::python::pickle_suite{
static boost::python::tuple getinitargs(BaseNode const& baseNode){
return boost::python::make_tuple(baseNode._position);
}
static boost::python::tuple getstate(boost::python::object obj)
{
BaseNode& baseNode = boost::python::extract<BaseNode&>(obj);
return boost::python::make_tuple(obj.attr("__dict__"));
}
static void setstate(boost::python::object obj, boost::python::tuple state)
{
BaseNode& baseNode = boost::python::extract<BaseNode&>(obj);
boost::python::dict d = extract<dict>(obj.attr("__dict__"));
d.update(state[0]);
}
static bool getstate_manages_dict() { return true; }
};
struct BaseNodeVirtual : BaseNode, boost::python::wrapper<BaseNode> {
BaseNodeVirtual(boost::python::object position) : BaseNode(position) {}
int test(){
if (override test = this->get_override("test"))
return test();
return BaseNode::test();
}
int default_test(){
return this->BaseNode::test();
}
};
int main() {
}
BOOST_PYTHON_MODULE(example){
// exposing the class
class_<BaseNodeVirtual, boost::noncopyable>("BaseNode", init<boost::python::object>())
.def_readwrite("_position", &BaseNode::_position)
.def_pickle(BaseNode_pickle_suite())
.def("test", &BaseNode::test, &BaseNodeVirtual::default_test);
}
编辑:将构造函数和析构函数设为虚拟没有帮助
根据 boost/python documentation,我可以通过包装器来公开虚函数。这是他们在此处复制粘贴的示例:
// base class
struct Base
{
virtual ~Base() {}
virtual int f() { return 0; }
};
// wrapper for the base function
struct BaseWrap : Base, wrapper<Base>
{
int f()
{
if (override f = this->get_override("f"))
return f(); // *note*
return Base::f();
}
int default_f() { return this->Base::f(); }
};
// exposing the class
class_<BaseWrap, boost::noncopyable>("Base")
.def("f", &Base::f, &BaseWrap::default_f);
这是我的 cpp 文件,其中包含复制错误所需的最少代码:
#include <iostream>
#include <python3.6m/Python.h>
#include <boost169/boost/python.hpp>
#include <boost169/boost/python/make_constructor.hpp>
#include <boost169/boost/python/detail/api_placeholder.hpp>
using namespace boost::python;
class BaseNode{
public:
BaseNode(boost::python::object position);
~BaseNode();
boost::python::object _position;
virtual int test(){
return 10;
}
};
class BaseNodeVirtual:BaseNode, boost::python::wrapper<BaseNode>{
public:
int test(){
if (override test = this->get_override("test"))
return test();
return BaseNode::test();
}
int default_test(){
return this->BaseNode::test();
}
};
// pickle support for BaseNode
struct BaseNode_pickle_suite : boost::python::pickle_suite{
static boost::python::tuple getinitargs(BaseNode const& baseNode){
return boost::python::make_tuple(baseNode._position);
}
static boost::python::tuple getstate(boost::python::object obj)
{
BaseNode& baseNode = boost::python::extract<BaseNode&>(obj);
return boost::python::make_tuple(obj.attr("__dict__"));
}
static void setstate(boost::python::object obj, boost::python::tuple state)
{
BaseNode& baseNode = boost::python::extract<BaseNode&>(obj);
boost::python::dict d = extract<dict>(obj.attr("__dict__"));
d.update(state[0]);
}
static bool getstate_manages_dict() { return true; }
};
BaseNode::BaseNode(boost::python::object position){
this->_position = position;
}
BaseNode::~BaseNode(){
}
BOOST_PYTHON_MODULE(BaseNode){
class_<BaseNodeVirtual, bases<BaseNode,boost::python::wrapper<BaseNode>>>("BaseNode", init<boost::python::object>())
.def_readwrite("_position", &BaseNode::_position)
.def_pickle(BaseNode_pickle_suite())
.def("test", &BaseNode::test, &BaseNodeVirtual::default_test);
}
它给出了一个我不太明白的长错误。
/usr/include/boost169/boost/python/object/value_holder.hpp:133:13: note: candidates are:
minNode.cpp:17:7: note: BaseNodeVirtual::BaseNodeVirtual(const BaseNodeVirtual&)
class BaseNodeVirtual: BaseNode, boost::python::wrapper<BaseNode>{
^
minNode.cpp:17:7: note: no known conversion for argument 1 from ‘const boost::python::api::object’ to ‘const BaseNodeVirtual&’
minNode.cpp:17:7: note: BaseNodeVirtual::BaseNodeVirtual(BaseNodeVirtual&&)
minNode.cpp:17:7: note: no known conversion for argument 1 from ‘const boost::python::api::object’ to ‘BaseNodeVirtual&&’
In file included from /usr/include/boost169/boost/preprocessor/iteration/detail/iter/forward1.hpp:52:0,
from /usr/include/boost169/boost/python/object/value_holder.hpp:46,
from /usr/include/boost169/boost/python/object/class_metadata.hpp:14,
from /usr/include/boost169/boost/python/class.hpp:23,
from /usr/include/boost169/boost/python.hpp:18,
from minNode.cpp:3:
/usr/include/boost169/boost/python/object/value_holder.hpp:135:80: erreur: ‘boost::python::detail::wrapper_base’ is an inaccessible base of ‘BaseNodeVirtual’
{
^
make: *** [minNode.o] Erreur 1
最初我认为这与私有继承有关(因为根据 this post and
/usr/include/boost169/boost/python/object/value_holder.hpp:133:13: erreur: no matching function for call to ‘BaseNodeVirtual::BaseNodeVirtual(boost::reference_wrapper<const BaseNode>::type&)’
BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_UNFORWARD_LOCAL, nil)
^
/usr/include/boost169/boost/python/object/value_holder.hpp:133:13: note: candidates are:
minNode.cpp:17:7: note: BaseNodeVirtual::BaseNodeVirtual(const BaseNodeVirtual&)
class BaseNodeVirtual:public BaseNode, public boost::python::wrapper<BaseNode>{
^
minNode.cpp:17:7: note: no known conversion for argument 1 from ‘boost::reference_wrapper<const BaseNode>::type {aka const BaseNode}’ to ‘const BaseNodeVirtual&’
minNode.cpp:17:7: note: BaseNodeVirtual::BaseNodeVirtual(BaseNodeVirtual&&)
minNode.cpp:17:7: note: no known conversion for argument 1 from ‘boost::reference_wrapper<const BaseNode>::type {aka const BaseNode}’ to ‘BaseNodeVirtual&&’
make: *** [minNode.o] Erreur 1
我该如何解决这个问题?
谢谢
为了匹配示例和构建而没有任何错误,您需要将包装器的 class
更改为 struct
,因为 Boost 使用 struct 来布局内存。此外,您还需要在包装器中明确定义您的自定义构造函数。最后,将行 bases(...)
更改为 boost::noncopyable
:
#include <iostream>
#include <python3.6m/Python.h>
#include <boost169/boost/python.hpp>
#include <boost169/boost/python/make_constructor.hpp>
#include <boost169/boost/python/detail/api_placeholder.hpp>
using namespace boost::python;
class BaseNode{
public:
BaseNode(boost::python::object position);
~BaseNode();
boost::python::object _position;
virtual int test(){ return 10; }
};
BaseNode::BaseNode(boost::python::object position){
this->_position = position;
}
BaseNode::~BaseNode(){
}
// pickle support for BaseNode
struct BaseNode_pickle_suite : boost::python::pickle_suite{
static boost::python::tuple getinitargs(BaseNode const& baseNode){
return boost::python::make_tuple(baseNode._position);
}
static boost::python::tuple getstate(boost::python::object obj)
{
BaseNode& baseNode = boost::python::extract<BaseNode&>(obj);
return boost::python::make_tuple(obj.attr("__dict__"));
}
static void setstate(boost::python::object obj, boost::python::tuple state)
{
BaseNode& baseNode = boost::python::extract<BaseNode&>(obj);
boost::python::dict d = extract<dict>(obj.attr("__dict__"));
d.update(state[0]);
}
static bool getstate_manages_dict() { return true; }
};
struct BaseNodeVirtual : BaseNode, boost::python::wrapper<BaseNode> {
BaseNodeVirtual(boost::python::object position) : BaseNode(position) {}
int test(){
if (override test = this->get_override("test"))
return test();
return BaseNode::test();
}
int default_test(){
return this->BaseNode::test();
}
};
int main() {
}
BOOST_PYTHON_MODULE(example){
// exposing the class
class_<BaseNodeVirtual, boost::noncopyable>("BaseNode", init<boost::python::object>())
.def_readwrite("_position", &BaseNode::_position)
.def_pickle(BaseNode_pickle_suite())
.def("test", &BaseNode::test, &BaseNodeVirtual::default_test);
}