C语言边角料5:一个跨平台的头文件

一、语言边前言

我们平常在写代码的角料时候,特别是个跨在制造轮子的时候(为别人提供库文件),会遇到各种不同的平台需求场景:

有些人需要在 Linux 系统下使用,有些人需要在 Windows 系统下使用; 有些人使用 C 语言开发,文件有些人使用 C++ 来开发; 有些人使用动态库,语言边有些人使用静态库;

特别是角料在 Windows 系统中,库文件中导出的个跨函数需要使用 _declspec(dllexport) 来声明函数,而使用者在导入的平台时候,需要使用 _declspec(dllimport) 来声明函数,文件甚是语言边麻烦!

这篇短文分享一个头文件,利用这个头文件,角料再加上几个编译期间传递的个跨宏,就可以完美的平台处理刚才所说的各种需求。

二、文件头文件

先直接上代码,可以先试着分析一下,后面我们再逐一分析不同的使用场景。

这个头文件的主要目的,就是亿华云计算定义一个宏:MY_API,然后把这个宏添加在库文件中每一个需要导出的函数或者类的声明中即可。例如:

void MY_API do_work(); 

下面是头文件:

_Pragma("once") #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)     #define MY_WIN32 #elif defined(linux) || defined(__linux) || defined(__linux__)     #define MY_LINUX #endif #if defined(MY_WIN32)     #ifdef MY_API_STATIC       #ifdef __cplusplus          #define MY_API extern "C"       #else          #define MY_API       #endif    #else       #ifdef MY_API_EXPORTS          #ifdef __cplusplus             #define MY_API extern "C" __declspec(dllexport)          #else             #define MY_API __declspec(dllexport)          #endif       #else          #ifdef __cplusplus             #define MY_API extern "C" __declspec(dllimport)          #else             #define MY_API __declspec(dllimport)          #endif       #endif    #endif #elif defined(MY_LINUX)     #ifdef __cplusplus        #define MY_API extern "C"     #else        #define MY_API     #endif #endif 

三、预定义的宏

假设需要写一个库文件,提供给别人使用。定义了上面这个头文件之后,其他的文件中都要include 这个头文件。

1. 平台宏定义

不同的平台预定义了相应的宏定义,例如:

Windows 平台:WIN32, _WIN32, WIN32; Linux 平台:linux, __linux, linux;

在一个确定的平台上,这些宏不一定全部定义,很可能只有其中的某一个宏是被定义的。

为了统一性,我们在头文件的刚开始部分,把这些可能的宏统一起来,定义我们出我们自己的平台宏定义:MY_WIN32 或者是 MY_LINUX,后面需要区分不同的平台时,就用这个自己定义的服务器租用平台宏。

当然,还可以继续扩充出其他平台,例如:MY_MAC, MY_ARM 等等。

2. 编译器宏定义

如果在写库代码的时候,使用的是 C++,而使用者使用的是 C 语言,那么就需要对库函数进行extern “C” 声明,让编译器不要对函数的名称进行改写。

编译器 g++ 预定义了宏 __cplusplus,因此,在头文件中,就利用了这个宏,在 MY_API 中添加 extern "C" 声明。

四、Windows 平台场景分析

1. 编译生成库文件

(1) 生成静态库

在静态库中,不需要 __declspec(dllexport/dllimport) 的声明,因此只需要区分编译器即可(gcc or g++),在编译选项中定义宏 MY_API_STATIC,即可得到最终的 MY_API 为:

gcc 编译器:#define MY_API g++ 编译器:#define MY_API extern "C"

(2) 生成动态库

在编译选项中,定义宏 MY_API_EXPORTS,这样最终得到的 MY_API 就会变成:

gcc 编译器:#define MY_API __declspec(dllexport) g++ 编译器:#define MY_API extern "C" __declspec(dllexport)

2. 使用库

在使用库的应用程序中,也需要在代码中 include 这个头文件,源码库然后加上编译选项中定义的各种宏,来生成对应的 MY_API 宏定义。

(1) 使用静态库

需要在编译选项中定义 MY_API_STATIC,即可得到最终的 MY_API 为:

gcc 编译器:#define MY_API g++ 编译器:#define MY_API extern "C"

(2) 使用动态库

在编译选项中不需要任何宏定义,即可得到最终的 MY_API 为:

gcc 编译器:#define MY_API extern "C" __declspec(dllimport) g++ 编译器:#define MY_API __declspec(dllimport)

这样就相当于声明导入库函数了。

五、Linux 平台场景分析

Linux 平台下就简单多了,只需要注意编译器的问题,而没有导出和导入之分。

数据库
上一篇:在数以亿计的网站中,我们应该抓住每一个可能带来宣传的机会,域名可以带有企业的名字,一般可以使用汉语拼音或者英语单词或者是相关缩写的形式,只要用户记住了你企业的名字,就能很容易的打出你的网站域名,同样的,记住了网站域名也能很快的记住你公司的名字。
下一篇:域名资源有限,好域名更是有限,但机会随时都有,这取决于我们能否抓住机会。一般观点认为,国内域名注册太深,建议优先考虑外国注册人。外国注册人相对诚实,但价格差别很大,从几美元到几十美元不等。域名投资者应抓住机遇,尽早注册国外域名。