C ++头文件包装器库

时间:2016-02-18 22:51:47

标签: c++ c dll

我正在尝试模仿我最近看到的提供" 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.hfoo.hppfoo.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

1 个答案:

答案 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 的含义是非绑定的,编译器可以自由地使用内联替换任何未标记为内联的函数,并且可以自由地生成对任何函数的函数调用。功能标记为内联。这些选择不会改变有关上面列出的多个定义和共享静态的规则。