我一直在尝试为我的C ++ 17项目生成C接口的方法。该项目将生成一个可执行文件,该文件可动态加载插件。在发现SWIG之前,我与clang一起玩了一段时间,我想知道SWIG是否能胜任这项工作,或者我是否可以做一些琐碎的工作来使其适合这种情况。
这是我对插件界面的看法。假设程序的源代码如下:
header.h
namespace Test {
struct TestStruct {
int Data;
};
class TestClass {
public:
virtual ~TestClass() = default;
void TestMethod(TestStruct&) const;
virtual void TestVirtual(int);
};
}
然后应生成以下代码:
api.h
// opaque structs
typedef struct {} Test_TestStruct;
typedef struct {} Test_TestClass;
typedef struct {
void (*Test_TestClass_destructor)(Test_TestClass*);
void (*Test_TestClass_TestVirtual)(Test_TestClass*, int);
} Test_TestClass_vtable;
typedef struct {
Test_TestStruct *(*Test_TestStruct_construct)();
void (*Test_TestStruct_dispose)(Test_TestStruct*);
int *(*Test_TestStruct_get_Data)(Test_TestStruct*);
int *(*Test_TestStruct_set_Data)(Test_TestStruct*, int);
Test_TestClass *(*Test_TestClass_construct)();
Test_TestClass *(*Test_TestClass_construct_derived(const Test_TestClass_vtable*);
void (*Test_TestClass_dispose)(Test_TestClass*);
void (*Test_TestClass_TestMethod)(const Test_TestClass*, Test_TestStruct*);
void (*Test_TestClass_TestVirtual)(Test_TestClass*, int);
} api_interface;
api_host.h
#include "api.h"
void init_api_interface(api_interface&);
api_host.cpp
#include "header.h"
#include "api.h"
// wrapper class
class _derived_TestClass : public Test::TestClass {
public:
_derived_TestClass(const Test_TestClass_vtable &vtable) : _vtable(vtable) {
}
~_derived_TestClass() {
if (_vtable.Test_TestClass_destructor) {
_vtable.Test_TestClass_destructor(reinterpret_cast<Test_TestClass*>(this));
}
}
void TestVirtual(int v) override {
if (_vtable.Test_TestClass_TestVirtual) {
_vtable.Test_TestClass_TestVirtual(reinterpret_cast<Test_TestClass*>(this), v);
} else {
TestClass::TestVirtual(v);
}
}
private:
const Test_TestClass_vtable &_vtable;
};
// wrapper functions
Test_TestStruct *_api_Test_TestStruct_construct() {
return reinterpret_cast<Test_TestStruct*>(new TestStruct());
}
void _api_Test_TestStruct_dispose(Test_TestStruct *p) {
auto *phost = reinterpret_cast<TestStruct*>(p);
delete phost;
}
int *_api_Test_TestStruct_get_Data(Test_TestStruct *p) {
return &reinterpret_cast<TestStruct*>(p)->Data;
}
...
...
// sets the values of all function pointers
void init_api_interface(api_interface &iface) {
iface.Test_TestStruct_construct = _api_Test_TestStruct_construct;
iface.Test_TestStruct_dispose = _api_Test_TestStruct_dispose;
iface.Test_TestStruct_get_Data = _api_Test_TestStruct_get_Data;
...
...
}
编译宿主程序时,我将所有这些文件编译为可执行文件,然后调用init_api_interface()
初始化函数指针。当其他人编译插件时,它们仅包含api.h
,并将文件编译为具有某个公开函数(例如init_plugin(const api_interface*)
)的动态库。当用户加载插件时,主机程序只需要将指向结构的指针传递给动态库中的init_plugin
,插件就可以开始使用所有这些功能。
使用这种方案的好处是:
当然,这只是该方法的要旨,实践中还需要考虑更多细节。
所以我的问题是: