创建一个可在linux和windows上运行的可移植库

gcc (GCC) 4.7.2 

你好,

我正在创建一个将在linux上编译的共享库和一个将在Windows上使用相同的源代码编译的DLL。 所以我正在为linux和windows创建一个可移植的库。

在我的库头文件中是这个,即module.h

 #ifdef __cplusplus extern "C" { #endif #ifdef _WIN32 #define LIB_INTERFACE(type) EXTERN_C __declspec(dllexport) type #else #define LIB_INTERFACE(type) type #endif LIB_INTERFACE(int) module_init(); #ifdef __cplusplus } #endif 

在源代码中我有以下即module.c

 #include "module.h" LIB_INTERFACE(int) module_init() { /* do something useful return 0; } 

在我的测试应用程序中将链接并使用此模块。所以我有这个:

 #include "module.h" int main(void) { if(module_init() != 0) { return -1; } return 0; } 

1)我上面所做的是为linux和windows创建可移植库的正确实现吗?

2)我只是想知道我已经将函数包装在extern "C"以便可以从用C ++编译的程序中调用该库。 我是否仍然需要以下EXTERN_C

 #define LIB_INTERFACE(type) EXTERN_C __declspec(dllexport) type 

3) EXTERN_C的目的是EXTERN_C

提前谢谢了,

这是为Windows导出DLL API并仍然支持Linux的典型方法:

 #ifdef __cplusplus extern "C" { #endif #ifdef _WIN32 # ifdef MODULE_API_EXPORTS # define MODULE_API __declspec(dllexport) # else # define MODULE_API __declspec(dllimport) # endif #else # define MODULE_API #endif MODULE_API int module_init(); #ifdef __cplusplus } #endif 

在DLL源代码中:

 #define MODULE_API_EXPORTS #include "module.h" MODULE_API int module_init() { /* do something useful */ return 0; } 

您的申请来源是正确的。

使用上面的模型,在Windows上DLL将导出API,而应用程序将导入它。 如果不在Win32上,则删除__declspec修饰。

由于标头将整个接口包装在extern "C" ,因此EXTERN_C在每个接口上使用EXTERN_C宏。 extern "C"用于告诉链接器使用C链接而不是C++ 。 C链接是编译器的标准,而C ++则不是,将DLL的使用限制为使用相同编译器构建的应用程序。

无需将返回类型集成到API宏中。

extern“C”基本上意味着你告诉编译器不要破坏你的函数名。 Mangling是“编码”函数名以供以后执行的过程,在C和C ++中有很大不同,因为C ++可以具有不同的具有相同名称的函数(通过重载等…)。

在C ++源代码中,extern“C”的作用是什么?

编译完成后,可以从任何地方调用这些函数,但您可能希望在开始之前确定要创建的库(静态或动态)。

另外,我建议您不要像在同一文件中那样使用DEFINES以实现可移植性,因为您在开发后期可能会遇到维护或可读性问题。 我将创建一个定义接口的基本文件,该接口完全可以移植到WIN和UNIX,然后创建另外两个实现接口但不同平台的库。

例如,您可以拥有:AbstractInterface.h,WinInterface.h,UnixInterface.h

然后根据平台编译您需要的那些。

对于Linux,没有-fvisibility = hidden的gcc将默认导出函数,静态函数除外。

使用-fvisibility = hidden时,除了由…修饰的函数外,gcc默认不会导出任何函数

 __attribute__ ((visibility ("default"))) 

对于Windows,导出的函数由装饰

 __attribute__ ((dllexport)) 

使用导出的函数时,必须对它们进行修饰

 __attribute__ ((dllimport)) 

post中的宏

 __declspec(dllexport) 

由MSVC支持。

所以交叉的linux和windows宏如下:

 #if defined _WIN32 || defined __CYGWIN__ || defined __MINGW32__ #ifdef BUILDING_DLL #ifdef __GNUC__ #define DLL_PUBLIC __attribute__ ((dllexport)) #else #define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. #endif #else #ifdef __GNUC__ #define DLL_PUBLIC __attribute__ ((dllimport)) #else #define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. #endif #endif #define DLL_LOCAL #else #if __GNUC__ >= 4 #define DLL_PUBLIC __attribute__ ((visibility ("default"))) #define DLL_LOCAL __attribute__ ((visibility ("hidden"))) #else #define DLL_PUBLIC #define DLL_LOCAL #endif #endif 
  • 确保必须使用 -DBUILDING_DLL编译共享对象或DLL项目。
  • 必须在不使用 -DBUILDING_DLL的情况下编译依赖于共享对象或DLL的项目

有关详细信息,请阅读http://gcc.gnu.org/wiki/Visibility

由于c ++语言特有的多态性概念,c ++中定义的所有函数都被破坏了。 即,为每个重写函数创建唯一名称,“编译器”装饰函数名称。

由于名称修改由“编译器”处理,并且没有严格定义名称修改规则的规范,因此每个编译器以不同方式修饰名称。 简而言之,gcc和msvc编译器为相同的代码创建不同的函数签名。 你可以在这里的wiki文章中进一步阅读有关名称修改的内容。

你的module.h文件只是告诉编译器使用c样式名称修改或根本没有修改。 由于该指令,gcc编译的库可用于链接到Visual Studio中编写的二进制文件。 这将帮助您分发库的二进制文件而不是源代码。

另一方面,如果不使用EXTERN_C指令,则应使用相同的编译器编译链接到库的库和项目。 例如,你必须使用gcc进行linux编译,使用msvc进行windows编译,以便链接到该库和链接到该库的项目。

您也可以让CMake使用CMake的generate_export_header (如从链接页面获取的示例)为构建编译器生成一个头文件,而不是自己编写头文件:

 add_library(libfoo foo.cpp) generate_export_header(libfoo) 
 #include "libfoo_export.h" class LIBFOO_EXPORT FooClass { int bar; };