我可以将C++模板类导出到C,再导入到Python吗?

1 投票
2 回答
1280 浏览
提问于 2025-04-18 07:50

对于一个非模板类,我会写类似这样的代码

但是如果我的类是一个模板类,我就不知道该怎么做了。

我尝试过类似的做法,但并没有成功。

extern "C" {
    Demodulator<double>* Foo_new_double(){ return new Demodulator<double>(); }
    Demodulator<float>* Foo_new_float(){ return new Demodulator<float>(); }
    void demodulateDoubleMatrix(Demodulator<double>* demodulator, double * input, int rows, int columns){ demodulator->demodulateMatrixPy(input, rows, columns) }
}

2 个回答

0

C++语言最开始是C语言的一个扩展,也就是说,它包含了C语言没有的新关键词、语法和功能。C语言没有类的概念,也没有成员函数的概念,更不支持访问限制的概念。此外,C语言也不支持继承。不过,C++和C最大的不同在于模板。C语言只有宏定义,没有其他的。

所以,直接把C++代码暴露给C语言是不行的,你必须在C++中使用C风格的代码来展示C++的部分。

 template<T> T foo(T i) { /* ... */ }
 extern "C" int fooInt(int i) { return foo(i); }

不过,C++最初基本上是一个C代码生成器,C++仍然可以与C的ABI(应用程序二进制接口)进行单向交互:成员函数实际上是通过将this->function(int arg);转换成ThisClass0int1(this, arg);这样的形式来实现的。理论上,你可以写一些东西来处理你的代码,可能会用到clang。

但这并不是一件简单的事情,SWIG、Boost::Python和Cython已经很好地解决了这个问题。

不过,模板的问题在于,编译器在你“实例化”(使用)它们之前是不会处理它们的。std::vector<>并不是一个具体的东西,直到你指定std::vector<int>或其他的东西。现在,唯一的具体实现就是std::vector<int>。在你某个地方指定之前,std::vector<string>在你的二进制文件中是不存在的。

你可能想先看看这个 http://kos.gd/2013/01/5-ways-to-use-python-with-native-code/,选择一个工具,比如SWIG,然后开始构建一个接口,把你想要的东西暴露给C。这比自己手动构建包装器要简单得多。根据你使用的工具,可能只需要写一行代码,比如using std::vector<int>typedef std::vector<int> IntVector之类的。

---- 编辑 ----

模板类的问题在于,你创建了一个C语言无法理解的完整类型,想想看:

template<typename T>
class Foo {
    T a;
    int b;
    T c;
public:
    Foo(T a_) : a(a_) {}
    void DoThing();
    T GetA() { return a; }
    int GetB() { return b; }
    T GetC() { return c; }
};

C语言不支持class这个关键词,更不用说理解成员a、b和c是私有的,或者什么是构造函数,C语言也不理解成员函数。

同样,C语言也不理解模板,所以你需要手动生成一个实例化,就像C++自动做的那样:

struct FooDouble {
    double a;
    int b;
    double c;
};

不过,所有这些变量都是私有的。那么你真的想把它们暴露出去吗?如果不想,你可能只需要把"FooDouble"定义为与Foo大小相同的类型,并做一个宏来实现。

然后你需要为成员函数写替代品。C语言不理解构造函数,所以你需要写一个

extern "C" FooDouble* FooDouble_construct(double a);

FooDouble* FooDouble_construct(double a) {
    Foo* foo = new Foo(a);
    return reinterept_cast<FooDouble*>(foo);
}

和一个析构函数

extern "C" void FooDouble_destruct(FooDouble* foo);

void FooDouble_destruct(FooDouble* foo) {
    delete reinterpret_cast<Foo*>(foo);
}

以及类似的访问器。

1

注意:你的问题和代码有点矛盾,所以我暂时不考虑代码部分。

C++中的模板其实是一种复杂的宏机制,它是在编译时解决的。换句话说,最终生成的程序只包含了模板实例化后的代码(也就是当你给模板传入参数,通常是类型时,得到的代码),这些代码才是你可以从程序中导出到其他语言的。导出这些代码就像导出任何普通类型一样,比如std::string

因为模板本身在编译后就不存在了,所以你无法从程序中导出它们,无论是导出到C语言、Python,还是C++!不过对于C++来说,你可以提供模板本身,但这并不意味着它们会包含在最终的程序中。

有两个假设:

  • 导出/导入是通过程序的二进制文件进行的。当然,你可以写一个解析C++的导入工具。
  • C++曾经规定过(或者说现在规定?)导出模板,但据我所知,这个功能在实际使用中并没有真正实现,所以我就不提这个选项了。

撰写回答