如何使用 boost/python 向 python 公开 C++ 虚函数?

Posted

技术标签:

【中文标题】如何使用 boost/python 向 python 公开 C++ 虚函数?【英文标题】:How to expose C++ virtual functions to python using boost/python? 【发布时间】:2020-07-27 22:30:46 【问题描述】:

编辑:使构造函数和析构函数为虚拟并没有帮助

根据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 和this post 这是受保护和私有继承带来的问题),所以我尝试将继承改为公共,但它仍然给了我一个错误(两个基类都是公共的,并且没有在 bases 中指定 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

我该如何解决这个问题?

谢谢

【问题讨论】:

【参考方案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 向 python 公开 C++ 虚函数?的主要内容,如果未能解决你的问题,请参考以下文章

Boost.Python 如何拥有 C++ 类?

如何将 asyncio 与 boost.python 一起使用?

如何使用 Boost.Python 定义 Python 元类?

如何使用boost.python中的-fPIC编译静态库

使用 boost python 元组,如何循环元组项?

将派生类型的对象从 python 传递到 C++ 函数时会出现 Boost Python 运行时错误,该函数期望将 shared_ptr 传递给基类型