扩展通过 Boost.Python 公开的虚拟 C++ 类

Posted

技术标签:

【中文标题】扩展通过 Boost.Python 公开的虚拟 C++ 类【英文标题】:Extending a virtual C++ class exposed via Boost.Python 【发布时间】:2012-09-06 13:43:30 【问题描述】:

我正在尝试使用 Boost.Python 公开这个 C++ 类:

class VAlgorithm 
public:

  VAlgorithm(const char *name);

  virtual ~VAlgorithm();

  virtual bool Initialize() = 0;
  virtual bool Process() = 0;
  virtual bool Finalize() = 0;

  virtual const char *GetName() const; // Default implementation in cpp file

我的最终目标是在 python shell 中将 VAlgorithm 的子级定义为 python 类。继this example之后,我定义了一个回调类:

class VAlgorithm_callback: public VAlgorithm 
public:
  VAlgorithm_callback(PyObject *p, const char *name) :
      self(p), VAlgorithm(name) 
  
  const char * GetName() const 
    return call_method<const char *>(self, "GetName");
  

  static const char * GetName_default(const VAlgorithm& self_) 
    return self_.VAlgorithm::GetName();
  

private:
  PyObject *self;
;

现在我只公开类本身和 GetName() 方法。由于是虚拟类,所以我把这段代码放在了 BOOST_PYTHON_MODULE 里面:

class_<VAlgorithm, VAlgorithm_callback, boost::noncopyable>("VAlgorithm", no_init) //
  .def("GetName", &VAlgorithm_callback::GetName_default); //

我可以编译它并将模块加载到 python shell 中。然后我尝试定义一个子类并调用C++代码中定义的GetName()默认实现:

>>> class ConcAlg1(VAlgorithm):
...   pass
... 
>>> c1 = ConcAlg1("c1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: This class cannot be instantiated from Python
>>> class ConcAlg2(VAlgorithm):
...   def __init__(self, name):
...     pass
... 
>>> c2 = ConcAlg2("c2")
>>> c2.GetName()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    VAlgorithm.GetName(ConcAlg2)
did not match C++ signature:
    GetName(VAlgorithm)
>>> class ConcAlg3(VAlgorithm):
...   def __init__(self, name):
...     super(ConcAlg3, self).__init__(self, name)
... 
>>> c3 = ConcAlg3("c3")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
RuntimeError: This class cannot be instantiated from Python

我不是专家(只是第一次遇到这些问题),但在我看来,ConcAlg1 和 ConcAlg3 尝试实例化一个 VAlgorithm 对象并失败,因为暴露 VAlgorithm 时使用了 no_init 参数(我不能省略它否则代码将无法编译),并且 ConcAlg2 不能调用 GetName() 因为不知何故它不被识别为 VAlgorithm 的子代。 我一定是做错了什么,但我不知道是什么(我是 Boost.Python 和扩展的新手)。谁能帮帮我吗?谢谢

【问题讨论】:

【参考方案1】:

我做过与此非常相似的事情。您为什么不关注您在评论中已经找到的内容?

当您在 Python 中创建派生自 VAlgorithm 的类的实例时,必须在 C++ 中实例化 VAlgorithm_callback 来表示它。如果您不在 BOOST_PYTHON_MODULE 中声明任何构造函数,这是不可能的。

按照示例中的方式进行操作应该没问题:

class_<VAlgorithm, VAlgorithm_callback, boost::noncopyable>("VAlgorithm", init<std::string>())

当你定义子类的init时,你可以使用下面的语法来调用父类的构造函数(但是你的代码也可以工作,我就是这样)。在您使用的示例中,它们甚至没有定义 init,因此而是调用了父级。

class ConcAlg3(VAlgorithm):
    def __init__(self, name):
    VAlgorithm.__init__(self, name)

【讨论】:

其实我是在发现错误后才写的。我认为我被一个记录不充分的示例和我对 Boost.Python 的了解不足所误导。感谢您的帮助!【参考方案2】:

我想我找到了解决方案。通过我在询问中引用的示例以及 boost 文档here,我发现问题在于回调类是由 Boost.Python 包装的真实类,并且它必须是一个具体的类。所以我在其中实现了缺失的纯虚方法:

class VAlgorithm_callback: public VAlgorithm 
public:
  VAlgorithm_callback(PyObject *p, const char *name) :
      self(p), VAlgorithm(name) 
  

  virtual bool Initialize() 
    return call_method<bool>(self, "Initialize");
  
  virtual bool Process() 
    return call_method<bool>(self, "Process");
  
  virtual bool Finalize() 
    return call_method<bool>(self, "Finalize");
  

  const char * GetName() const 
    return call_method<const char *>(self, "GetName");
  

// Supplies the default implementation of GetName
  static const char * GetName_default(const VAlgorithm& self_) 
    return self_.VAlgorithm::GetName();
  

private:
  PyObject *self;
;

并将包装器修改为:

class_<VAlgorithm, boost::shared_ptr<VAlgorithm_callback>, boost::noncopyable ("VAlgorithm", init<const char *>()) //
 .def("Initialize", &VAlgorithm_callback::Initialize)
 .def("Process", &VAlgorithm_callback::Process)
 .def("Finalize", &VAlgorithm_callback::Finalize)
 .def("GetName", &VAlgorithm_callback::GetName_default);

现在我可以在 Python 中定义一个子类并调用默认的 GetName() 方法:

>>> class ConcAlg(VAlgorithm):
...   pass
... 
>>> c = ConcAlg("c")
>>> c.GetName()
'c'
>>>

【讨论】:

以上是关于扩展通过 Boost.Python 公开的虚拟 C++ 类的主要内容,如果未能解决你的问题,请参考以下文章

在 boost.python 中;如何公开包含在另一个类中的类(通过组合)?

在 boost.python 中;如何公开包含在另一个类中的类(通过组合)?

Boost.python vs Cython 用于 C++/python 接口

我应该包括啥来制作 boost.python 扩展?

C ++在boost python中使用带有命名空间的自定义智能指针

使用boost.python,如何扩展类的__dir__函数?