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

时间:2021-04-30 17:38:44
gcc (GCC) 4.7.2

Hello,

你好,

I am creating a shared library that will compile on linux and a dll that will compile on windows using the same source code. So i am creating an portable library for both linux and windows.

我正在创建一个共享库,它将在linux上编译,并在windows上使用相同的源代码编译。因此,我正在为linux和windows创建一个可移植的库。

In my header file for the library is this i.e. module.h

在我的库的头文件中,就是modu .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

In the source I have the following i.e. module.c

在源文件中,我有如下内容:模块

#include "module.h"

LIB_INTERFACE(int) module_init()
{
    /* do something useful
    return 0;
}

And in my test application that will link and use this module.so I have this:

在我的测试应用中,它会链接并使用这个模块。所以我有这个:

#include "module.h"

int main(void)
{
    if(module_init() != 0) {
    return -1;
    }
    return 0;
}

1) Is what I have done above is it a correct implementation of creating a portable library for linux and windows?

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

2) I am just wondering as I have wrapped the functions in extern "C" so that this library can been called from a program that has been compiled in C++. Do I still need this EXTERN_C in the following:

2)我只是想知道,因为我已经用extern“C”包装了函数,因此可以从使用c++编译的程序中调用这个库。我是否仍然需要如下的EXTERN_C:

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

3) What is the purpose of the EXTERN_C?

3) EXTERN_C的用途是什么?

Many thanks in advance,

非常感谢,

5 个解决方案

#1


20  

This is a typical way to export a DLL API for Windows and still support Linux:

这是为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

In the DLL source:

DLL的来源:

#define MODULE_API_EXPORTS
#include "module.h"

MODULE_API int module_init()
{
    /* do something useful */
    return 0;
}

Your application source is correct.

您的应用程序源代码是正确的。

Using the above model, on Windows the DLL will export the API while the application will import it. If not on Win32, the __declspec decoration is removed.

使用上述模型,在Windows上,DLL将导出API,而应用程序将导入API。如果不是Win32,那么__declspec装饰就会被删除。

Since the header wraps the entire interface in extern "C", using the EXTERN_C macro on each interface is not required. extern "C" is used to tell the linker to use C linkage instead of C++. C linkage is standard across compilers, whereas C++ is not, limiting the use of a DLL to application built with the same compiler.

由于header以extern“C”形式包装整个接口,因此不需要在每个接口上使用EXTERN_C宏。extern“C”用于告知链接器使用C链接而非c++时。C链接是跨编译器的标准,而c++不是,限制了使用同一个编译器构建的DLL的使用。

There is no need to integrate the return type into the API macro.

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

#2


13  

extern "C" basically means that you are telling the compiler not to mangle your function name. Mangling is the process of "encoding" function names for later execution and is quite different in C and C++ as C++ can have different functions having the same name (via overloading etc...).

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

In C++ source, what is the effect of extern "C"?

在c++源代码中,outside的作用是什么?

Once compiled these functions can be called from anywhere but you might want to be sure what kind of library you are creating (static or dynamic) before you start.

编译完这些函数后,可以从任何地方调用它们,但是您可能需要在开始之前确定您正在创建的是哪种类型的库(静态的还是动态的)。

Also I recommend you not using DEFINES like you do in the same file for portability purposes because of the maintenance or readability problems you might encounter later in the development. I would create a basic file defining an interface which is fully portable to WIN and UNIX then create two other libraries implementing the interface but for different platforms.

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

For example you can have: AbstractInterface.h, WinInterface.h, UnixInterface.h

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

Then only compile the ones you need depending on the platform.

然后根据平台只编译需要的文件。

#3


10  

For Linux, gcc without -fvisibility=hidden will make functions exported by default, except for static functions.

对于Linux,没有-fvisibility=隐藏的gcc将使函数在默认情况下输出,除了静态函数。

With -fvisibility=hidden, gcc will make no functions exported by default, except that functions decorated by

使用-fvisibility=hidden, gcc在默认情况下不会导出函数,除非是用修饰过的函数

__attribute__ ((visibility ("default")))

For Windows, exported functions decorated by

对于Windows,导出用于修饰的函数

__attribute__ ((dllexport))

when using the exported functions, they must be decorated by

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

__attribute__ ((dllimport))

The macros in your posts

你的文章中的宏

__declspec(dllexport)

are supported by MSVC.

受到MSVC的支持。

So the crossed linux and windows macros are as following:

因此,交叉的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
  • Make sure that shared object or DLL projects must be compiled with -DBUILDING_DLL.
  • 确保共享对象或DLL项目必须使用-DBUILDING_DLL编译。
  • The project that depends on your shared object or DLL must be compiled without -DBUILDING_DLL
  • 依赖于共享对象或DLL的项目必须在没有-DBUILDING_DLL的情况下编译

For the more details, please read http://gcc.gnu.org/wiki/Visibility

更多细节请阅读http://gcc.gnu.org/wiki/Visibility。

#4


3  

Due to the concept of polymorphism which is specific to c++ language, All the functions defined in c++ are mangled. i.e., to create unique names for each overridden function, the "compiler" decorates the function names.

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

Since name mangling is handled by "compiler" and there is no specification to strictly define the name mangling rules, each compiler decorates the names in different ways. Simply put, gcc and msvc compilers create different function signatures for the same code. you can read further about name mangling at the wiki article here.

由于名称管理是由“编译器”处理的,并且没有规范来严格定义名称管理规则,每个编译器都以不同的方式修饰名称。简单地说,gcc和msvc编译器为相同的代码创建不同的函数签名。你可以在这里阅读更多关于名字管理的文章。

Your module.h file simply tells the compiler to use c style name mangling or no mangling at all. due to this directive, the library that is compiled by gcc can be used to link to a binary that is written in visual studio. This will help you to distribute the binaries of your library instead of source code.

你的模块。h文件只是告诉编译器使用c样式的名称进行管理,或者根本不进行管理。由于这个指令,gcc编译的库可以用来链接到visual studio中编写的二进制文件。这将帮助您分发库的二进制文件,而不是源代码。

On the other hand, if you do not use the EXTERN_C directive, the library and the project that links to the library should be compiled using same compiler. for example, you have to use gcc for linux compilation and msvc for windows compilation for both the library and the project linking to that library.

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

#5


2  

Instead of writing a header file yourself, you can also let CMake generate one for the building compiler using CMake's generate_export_header like this (examples taken from the linked page):

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

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

#1


20  

This is a typical way to export a DLL API for Windows and still support Linux:

这是为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

In the DLL source:

DLL的来源:

#define MODULE_API_EXPORTS
#include "module.h"

MODULE_API int module_init()
{
    /* do something useful */
    return 0;
}

Your application source is correct.

您的应用程序源代码是正确的。

Using the above model, on Windows the DLL will export the API while the application will import it. If not on Win32, the __declspec decoration is removed.

使用上述模型,在Windows上,DLL将导出API,而应用程序将导入API。如果不是Win32,那么__declspec装饰就会被删除。

Since the header wraps the entire interface in extern "C", using the EXTERN_C macro on each interface is not required. extern "C" is used to tell the linker to use C linkage instead of C++. C linkage is standard across compilers, whereas C++ is not, limiting the use of a DLL to application built with the same compiler.

由于header以extern“C”形式包装整个接口,因此不需要在每个接口上使用EXTERN_C宏。extern“C”用于告知链接器使用C链接而非c++时。C链接是跨编译器的标准,而c++不是,限制了使用同一个编译器构建的DLL的使用。

There is no need to integrate the return type into the API macro.

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

#2


13  

extern "C" basically means that you are telling the compiler not to mangle your function name. Mangling is the process of "encoding" function names for later execution and is quite different in C and C++ as C++ can have different functions having the same name (via overloading etc...).

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

In C++ source, what is the effect of extern "C"?

在c++源代码中,outside的作用是什么?

Once compiled these functions can be called from anywhere but you might want to be sure what kind of library you are creating (static or dynamic) before you start.

编译完这些函数后,可以从任何地方调用它们,但是您可能需要在开始之前确定您正在创建的是哪种类型的库(静态的还是动态的)。

Also I recommend you not using DEFINES like you do in the same file for portability purposes because of the maintenance or readability problems you might encounter later in the development. I would create a basic file defining an interface which is fully portable to WIN and UNIX then create two other libraries implementing the interface but for different platforms.

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

For example you can have: AbstractInterface.h, WinInterface.h, UnixInterface.h

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

Then only compile the ones you need depending on the platform.

然后根据平台只编译需要的文件。

#3


10  

For Linux, gcc without -fvisibility=hidden will make functions exported by default, except for static functions.

对于Linux,没有-fvisibility=隐藏的gcc将使函数在默认情况下输出,除了静态函数。

With -fvisibility=hidden, gcc will make no functions exported by default, except that functions decorated by

使用-fvisibility=hidden, gcc在默认情况下不会导出函数,除非是用修饰过的函数

__attribute__ ((visibility ("default")))

For Windows, exported functions decorated by

对于Windows,导出用于修饰的函数

__attribute__ ((dllexport))

when using the exported functions, they must be decorated by

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

__attribute__ ((dllimport))

The macros in your posts

你的文章中的宏

__declspec(dllexport)

are supported by MSVC.

受到MSVC的支持。

So the crossed linux and windows macros are as following:

因此,交叉的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
  • Make sure that shared object or DLL projects must be compiled with -DBUILDING_DLL.
  • 确保共享对象或DLL项目必须使用-DBUILDING_DLL编译。
  • The project that depends on your shared object or DLL must be compiled without -DBUILDING_DLL
  • 依赖于共享对象或DLL的项目必须在没有-DBUILDING_DLL的情况下编译

For the more details, please read http://gcc.gnu.org/wiki/Visibility

更多细节请阅读http://gcc.gnu.org/wiki/Visibility。

#4


3  

Due to the concept of polymorphism which is specific to c++ language, All the functions defined in c++ are mangled. i.e., to create unique names for each overridden function, the "compiler" decorates the function names.

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

Since name mangling is handled by "compiler" and there is no specification to strictly define the name mangling rules, each compiler decorates the names in different ways. Simply put, gcc and msvc compilers create different function signatures for the same code. you can read further about name mangling at the wiki article here.

由于名称管理是由“编译器”处理的,并且没有规范来严格定义名称管理规则,每个编译器都以不同的方式修饰名称。简单地说,gcc和msvc编译器为相同的代码创建不同的函数签名。你可以在这里阅读更多关于名字管理的文章。

Your module.h file simply tells the compiler to use c style name mangling or no mangling at all. due to this directive, the library that is compiled by gcc can be used to link to a binary that is written in visual studio. This will help you to distribute the binaries of your library instead of source code.

你的模块。h文件只是告诉编译器使用c样式的名称进行管理,或者根本不进行管理。由于这个指令,gcc编译的库可以用来链接到visual studio中编写的二进制文件。这将帮助您分发库的二进制文件,而不是源代码。

On the other hand, if you do not use the EXTERN_C directive, the library and the project that links to the library should be compiled using same compiler. for example, you have to use gcc for linux compilation and msvc for windows compilation for both the library and the project linking to that library.

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

#5


2  

Instead of writing a header file yourself, you can also let CMake generate one for the building compiler using CMake's generate_export_header like this (examples taken from the linked page):

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

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