请注意,OP强调:
主模块是用 C 编写的,而子模块是用 C++ 编写的。
恕我直言,为 C++ submodule 添加 C 绑定完全没问题,这样它就可以在 C 代码中使用。
OP 的例子(增强一点使其成为MCVE):
submodule 的 C++ 代码:
标头submodule.hpp:
#ifndef SUB_MODULE_HPP
#define SUB_MODULE_HPP
class Test {
private:
int a = 0;
int b = 0;
public:
Test() = default; // default constructor
Test(int a, int b): a(a), b(b) { } // value constructor
Test(Test&) = delete; // say: copy constructor disabled
Test& operator=(Test&) = delete; // say: copy assignment disabled
public:
int exec() const;
};
#endif // SUB_MODULE_HPP
来源submodule.cpp:
#include "submodule.hpp"
int Test::exec() const { return a + b + 42; }
submodule 的 C 绑定:
标头:submodule.h
#ifndef SUB_MODULE_H
#define SUB_MODULE_H
#ifdef __cplusplus
extern "C" {
#endif
struct TestC_;
typedef struct TestC_ TestC; // opaque structure
extern TestC* testNew();
extern TestC* testNewValues(int a, int b);
extern void testDelete(TestC *pTest);
extern int testExec(TestC *pTest);
#ifdef __cplusplus
}
#endif
#endif // SUB_MODULE_H
来源submoduleC.cpp:
#include "submodule.h"
#include "submodule.hpp"
TestC* testNew() { return (TestC*)new Test(); }
TestC* testNewValues(int a, int b) { return (TestC*)new Test(a, b); }
void testDelete(TestC *pTest) { delete (Test*)pTest; }
int testExec(TestC *pTest) { return ((Test*)pTest)->exec(); }
最后但同样重要的是:使用 submodule 的示例 C 程序:
#include <stdio.h>
#include "submodule.h"
int main(void)
{
TestC *pTest = testNew();
printf("pTest->exec(): %d\n", testExec(pTest));
testDelete(pTest);
pTest = testNewValues(123, -123);
printf("pTest->exec(): %d\n", testExec(pTest));
testDelete(pTest);
}
如何编译和链接g++/gcc:
$ ## compile C++ code
$ g++ -std=c++17 -O2 -Wall -pedantic -c submodule.cpp submoduleC.cpp
$ ## compile C code
$ gcc -std=c11 -O2 -Wall -pedantic -lstdc++ main.c submodule.o submoduleC.o
$ ## test
$ ./a.out
输出:
testExec(pTest): 42
testExec(pTest): 42
Demo on coliru
最值得注意的部分可能是opaque structure:
struct TestC_;
typedef struct TestC_ TestC; // opaque structure
(这是我在开始编写自己的绑定之前从其他 C 绑定中学到的。)
opaque structure 实际上是一个不完整的结构,但这是有意为之。
不透明结构旨在仅在 C 中用作指针。它不旨在允许取消引用任何内容(并且,对于不完整的结构,编译器只会拒绝这样做)。它的唯一目的是通过 C 代码“隧道”指向 C++ Test 实例的指针。
在Test (submoduleC.cpp) 的 C 绑定中,TestC* 在必要时简单地转换为 Test*(反之亦然)。
提示:
我必须链接-lstdc++(标准 C++ 库)。否则,我会收到有关运算符 new 和 delete 的未定义引用的投诉:
submoduleC.o: In function `testNew':
submoduleC.cpp:(.text+0xa): undefined reference to `operator new(unsigned long)'
submoduleC.o: In function `testNewValues':
submoduleC.cpp:(.text+0x30): undefined reference to `operator new(unsigned long)'
submoduleC.o: In function `testDelete':
submoduleC.cpp:(.text+0x4b): undefined reference to `operator delete(void*, unsigned long)'
collect2: error: ld returned 1 exit status
其他想法(但可能不那么相关):
我记得有一个著名的框架
- 用C编写
- 并提供 C++ API(也称为 C++ 绑定)。
我说的是GTK+ 和gtkmm(它的C++ 绑定)。
因此,应该提到 GTK+ 实际上是一个 OOP 库——提供用 C 实现的面向对象的类。(我不确定这个事实是否会使 C++ 绑定的事情变得更容易或更难。)
两者都是开源项目。因此,每个人都可以看看他们是如何做到的。曾经看过,印象深刻……
除此之外,我总是建议相反:用 C++ 编写一个库并添加一个 C 绑定,因为这对我来说似乎更容易。
出于多种原因,我们总是在 Windows 中这样做:
-
实现不同版本Visual Studio编译的DLL可以链接在一起。 (不久之前,这在 C++ 中几乎是不可能的,但现在,我相信,我读到了 Microsoft 引入的 ABI,它可能使之成为可能。)
-
为其他语言的绑定提供基础,例如C#。