【问题标题】:Wrapping child classes of templated base class in Cython在 Cython 中包装模板化基类的子类
【发布时间】:2019-05-28 03:39:35
【问题描述】:

所以我正在使用 Cython 为 C++ 库编写 Python API。我有三个具有几乎相同功能的类:A、B 和 C。这种区别仅在于它们的对象之一是如何基于初始化和一些常量构建的。

最初,我将它们全部写成单独的类,然后可以在我的 cython 代码中通过 extern 定义它们。这编译并且运行良好,但是有很多重复的代码,我真的希望这个项目更加干燥。

所以我决定为A、B 和C 编写一个基类,它实现了大部分功能。但是,我需要模板化该基类,当我尝试在 Cython 中定义所有内容时,这让我陷入了噩梦。这是我正在谈论的玩具示例(如果您找到它们,请忽略错过的分号等)。这是我的“classes.h”文件

int library_method_load(std::string file_name){
    return std::string.length();
}

template <class T>
class BaseClass{
public:
    T important_obj;
    BaseClass(std::string file_name){ important_obj = library_method(file_name);};
    virtual T library_method(std::string file_name) = 0;
    // Important logicks...
    ~BaseClass(){};
}



class A : public BaseClass<int> {
    A(std::string file_name): BaseClass<int>(file_name){};
    int library_method(std::string file_name){ return library_method_load(file_name);};
    ~A(){};
}

当我尝试包装它时,如果我不告诉 cython 基类,我会得到未定义的符号。如果我尝试定义基类,模板会导致问题。后者可能是因为我不知道继承模板基类的语法。

这是我目前的尝试

#distutils: language = c++
from libcpp.string cimport string

cdef extern from "classes.h":
    cppclass BaseClass[T]:
        BaseClass(string file_name)

cdef extern from "classes.h":
    cppclass A(BaseClass[int]):
        A(string file_name)

cdef class PyBase:
    cdef BaseClass* wrapped

cdef class PyA(PyBase):
    def __cinit__(self, string file_name):
        self.wrapped = <BaseClass[int]*> new A(file_name)

这样做会给我以下编译器错误:

编译 Cython 文件时出错: -------------------------------------------------- ---------- ...

cdef 类 PyA(PyBase):

def __cinit__(self, string file_name):
    self.wrapped = <BaseClass[int]*> new A(file_name)

^

wrapper.pyx:23:23: 无法将类型 'BaseClass[int] *' 分配给 'BaseClass[T] *' Traceback(最近一次调用最后一次):文件“setup.py”, 第 9 行,在 setup(name="test", version="1.0.0", ext_modules=cythonize([rk])) 文件 “/home/jacob/anaconda3/lib/python3.6/site-packages/Cython/Build/Dependencies.py”, 第 1027 行,在 cythonize cythonize_one(*args) 文件“/home/jacob/anaconda3/lib/python3.6/site-packages/Cython/Build/Dependencies.py”, 第 1149 行,在 cythonize_one 引发 CompileError(None, pyx_file) Cython.Compiler.Errors.CompileError: wrapper.pyx

有人知道怎么做吗?显然我的模板替换已关闭。我应该坚持使用重复的代码吗?还有其他聪明的解决方案吗?

【问题讨论】:

  • 不确定你想要什么;你确定你不是在寻找 CRPT (Curiously recurring template pattern)?
  • 你需要展示你的 cython 包装器,否则没人知道你的问题是什么,见minimal reproducible example。请确保您的示例是最小的 - 可能只有一个子类(而不是 3 个)足以显示问题。
  • 我认为问题在于你实际上没有一个通用的基类——它们都有由模板生成的略有不同的基类,实际上很难为模板类创建好的 Cython 包装器没有重复。一种解决方案是generate the repetitive code。
  • 这种方法行不通。在 C++ 中,从基类构造函数调用虚拟方法将不会在某些派生类中调用覆盖:它将调用虚拟方法的基类实现(如果存在)。
  • 事实上,依赖虚拟方法使一个简单的需求复杂化。使用 T 参数定义 BaseClass 构造函数,并从派生类提供此参数(作为调用其自定义加载器方法的结果)。

标签: c++ templates cython template-specialization base-class


【解决方案1】:

一种解决方案是删除PyBase,因为在cdef BaseClass* wrapped 中缺少BaseClass 的模板参数,这使得该行毫无意义。

例如:

cdef class PyA(PyBase):
    cdef A* wrapped
    def __cinit__(self, string file_name):
        self.wrapped = new A(file_name)

(我不确定您是否可以将 C++ 对象传递给 __init__ 和 __cinit__)

【讨论】:

  • 我尝试了你的建议。它编译但抱怨一个未定义的符号:_Py_ZeroStruct.
  • @batconjurer 您需要链接 Python 库。 -lpython.
  • 我试过这个,但现在它抱怨 BaseClass 库方法有一个未定义的符号。将它包含在 extern defs 中似乎没有帮助。
猜你喜欢
  • 1970-01-01
  • 2013-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
  • 1970-01-01
相关资源
最近更新 更多