我正在尝试模仿我最近看到的提供" C ++二进制兼容"的项目模式。 API。我这样做是通过向纯C接口提供C ++头文件包装器。 (这与C ++ OpenCL包装器文件cl.hpp在纯C OpenCL接口中的作用相同。)
这是一个精简的例子。
foo.h
是纯C接口:
typdef void *foo_t;
extern "C" DLLEXPORT foo_t foo_open(const char *);
extern "C" DLLEXPORT int foo_compute(foo_t, const char *buf, int blen);
extern "C" DLLEXPORT void foo_close(foo_t);
foo.hpp
是C ++包装器,它也随头部分发:
struct Foo {
foo_t handle;
Foo() { handle = foo_open(...); }
~Foo() { foo_close(handle); }
std::string compute() {
char buf[512];
foo_compute(handle, buf, sizeof(buf))
return std::string(buf);
}
}
Joe用户获取foo.h
,foo.hpp
和foo.dll
(带有导出库)。 Foo.dll本可以使用MSVC2012,MSVC2013或mingw编译,但Joe User仍然可以使用MSVC2015。这一切都很有效,因为非二进制可移植C ++包装器实现在公共头中。
问题是:我的包装器有点复杂,我想简化它 通过移动一些较大方法的定义。即我希望C ++接口简洁,并且与实现分开列出(可以在下面)。
// INTERFACE: keep it concise
struct Foo {
foo_t handle;
Foo() { handle = foo_open(...) }
~Foo() { foo_close(handle); }
std::string compute(); // keep it concise
}
// IMPLEMENTATIONS: most folks don't need to read this
std::string Foo::compute() {
char buf[512];
foo_compute(handle, buf, sizeof(buf))
return std::string(buf);
}
问题在于,如果多个目标文件包含foo.hpp
(并且它们会),我最终会得到Foo::compute
的多个定义,因为每个目标文件都会标记副本而不是使用定义的内联类版本
cl.hpp
我在简单介绍了所有定义之后对其进行了建模,因此没有任何帮助。我环顾网络,但无法找到我想要做的好例子,但也许我使用了错误的命名法。
如果只是这些功能,我只需将它们全部标记为static
,它们就不会逃脱它们的编译单元。最糟糕的情况是,我想我可以使用静态辅助函数来解决这个问题。
有什么想法吗?
谢谢!
参考文献: [1]这有点相关。 https://chadaustin.me/cppinterface.html
答案 0 :(得分:1)
对于瘦包装器,保持成员函数inline
是有意义的。这可以通过在类中定义 类(如发布代码的第一个片段),或在类外定义它们并将它们明确声明为{{1例如在你的例子中:
inline
声明为// IMPLEMENTATIONS: most folks don't need to read this
inline std::string Foo::compute() {
char buf[512];
foo_compute(handle, buf, sizeof(buf));
return std::string(buf);
}
的函数可以在头文件中定义,该文件在多个源文件(又名翻译单元)中#include&#d; dd,而不会导致多个定义编译器错误。引自inline specifier的文档:
只要每个定义出现在不同的翻译单元中,程序中的内联函数可能有多个定义。例如,可以在多个源文件中#include&#d; d的头文件中定义内联函数。
编译器是否实际内联函数(在"扩展"每次出现的内联代码与生成实际函数调用的意义上)是无关紧要的,并且不会影响/更改,声明为inline
的函数的标准所保证的行为。来自同一个链接页面:
由于关键字 inline 的含义是非绑定的,编译器可以自由地使用内联替换任何未标记为内联的函数,并且可以自由地生成对任何函数的函数调用。功能标记为内联。这些选择不会改变有关上面列出的多个定义和共享静态的规则。