C语言 创建可在Linux和Windows上运行的可移植库

wnrlj8wa  于 2023-06-05  发布在  Linux
关注(0)|答案(5)|浏览(427)
gcc (GCC) 4.7.2

你好
我正在创建一个共享库,将编译在linux和dll,将编译在windows使用相同的源代码。所以我正在为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;
}

在我的测试应用程序中,将链接并使用这个module.so,我有这个:

#include "module.h"

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

1)我上面所做的是为linux和windows创建一个可移植库的正确实现吗?
2)我只是想知道,因为我已经把函数 Package 在extern "C"中,这样这个库就可以从一个用C++编译的程序中调用。在以下情况下,我是否仍然需要此EXTERN_C

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

3)EXTERN_C的用途是什么?
在此先向您致谢,

roejwanj

roejwanj1#

这是导出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装饰将被删除。
由于标头将整个接口 Package 在extern "C"中,因此不需要在每个接口上使用EXTERN_C宏。extern "C"用于告诉链接器使用C链接而不是C++。C链接是跨编译器的标准,而C++不是,这将DLL的使用限制在使用同一编译器构建的应用程序上。
不需要将返回类型集成到API宏中。

xdnvmnnf

xdnvmnnf2#

extern“C”基本上意味着你告诉编译器不要破坏你的函数名。Mangling是对函数名进行“编码”以供以后执行的过程,在C和C中有很大的不同,因为C可以有不同的函数具有相同的名称(通过重载等)。
In C++ source, what is the effect of extern "C"?
这些函数一旦被编译,就可以从任何地方调用,但是在开始之前,你可能想确定你创建的是什么类型的库(静态的还是动态的)。
另外,我建议您不要像在同一个文件中那样使用DEFINES来实现可移植性,因为在以后的开发中可能会遇到维护或可读性问题。我将创建一个基本文件,定义一个完全可移植到WIN和UNIX的接口,然后创建另外两个实现该接口但用于不同平台的库。
例如,您可以拥有:AbstractInterface.h、WinInterface.h、UnixInterface.h
然后根据平台只编译你需要的那些。

vwoqyblh

vwoqyblh3#

对于Linux,不带-fvisibility=hidden的gcc将默认导出函数,静态函数除外。
使用-fvisibility=hidden时,gcc默认情况下不会导出任何函数,除了由

__attribute__ ((visibility ("default")))

对于Windows,导出的函数由

__attribute__ ((dllexport))

当使用导出的函数时,它们必须由

__attribute__ ((dllimport))

你帖子里的宏

__declspec(dllexport)

MSVC支持,但are supported by GCC targetting Windows too不使用上述__attribute__语法。
因此,交叉的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
  • 确保共享对象或DLL项目必须使用**-DBULDING_DLL进行编译**。
  • 依赖于共享对象或DLL的项目必须在没有-DBULDING_DLL的情况下编译

有关可见性的更多详细信息,请阅读http://gcc.gnu.org/wiki/Visibility

c7rzv4ha

c7rzv4ha4#

由于 C++ 特有的多态性概念,C++中定义的所有函数都被篡改了。即,为了为每个被覆盖的函数创建唯一的名称,“编译器”装饰函数名称。
由于名称修改是由“编译器”处理的,并且没有规范来严格定义名称修改规则,因此每个编译器都以不同的方式修饰名称。简单地说,gcc和msvc编译器为相同的代码创建不同的函数签名。你可以在wiki文章here中阅读更多关于名称篡改的内容。
你的module.h文件只是告诉编译器使用c风格的名称修改或者根本不修改。由于此指令,由gcc编译的库可用于链接到在visual studio中编写的二进制文件。这将帮助您分发库的二进制文件,而不是源代码。
另一方面,如果不使用EXTERN_C指令,则库和链接到库的项目应使用相同的编译器进行编译。例如,对于库和链接到该库的项目,必须使用gcc进行linux编译,使用msvc进行windows编译。

9q78igpj

9q78igpj5#

你也可以让CMake使用CMake's generate_export_header为编译器生成一个头文件,而不是自己写一个头文件(例子取自链接页面):

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

相关问题