跨平台的库一直被视为减轻程序员工作负担的一大利器。那么这些程序是怎么样做到跨平台的呢。这里我分析了SDL2和Catch2的源码,发现了编写的方法。
首先需要注意的是:以下说的方法都只在GUNC编译器下编译,其他编译器不知道能不能通过。
识别当前的操作系统
首先需要知道如何识别当前的操作系统。GNUC编译器会在编译程序的时候定义平台相关的宏。你可以在SDL2和Catch2中看到如下代码:
|
|
如果你是Linux系统,那么GNUC会给你宏linux
,__linux
,__linux__
三个宏中的一个。如果你是安卓系统,那么就会给ANDROID
,__ANDROID__
宏中的一个。
所以跨平台就是根据这些GNUC给出的宏来判断的。根据源码,我们可以知道GUNC会给出如下的宏:
- linux系统:
linux, __linux, __linux__
- 苹果系统:
__APPLE__
- 苹果电脑:
__MACOSX__
- 苹果手机:
__IPHONEOS__
- 苹果电视:
__TVOS__
- 苹果电脑:
- 安卓系统:
__ANDROID__
- windows系统:
WIN32, _WIN32, __CYGWIN__, __MINGW32__,__WINDOWS__
- PSP系统:
__PSP__
需要注意的是,苹果系统GNUC只会提供__APPLE__
宏。你在判断这个宏之后你还需要包含苹果的头文件AvailabilityMacros.h
,TargetConditionals.h
,然后继续通过__MACOSX__
等宏来判断。
还有很多很多的平台识别宏。这里就不列举了。
声明自己的宏用于方便识别当前系统
当你能够识别当前的操作系统时,你需要定义自己的宏来便于以后自己识别操作系统,和根据系统来进行不同的操作。比如SDL2就是这样做的。在判断是linux系统后,他会定义__LINUX__
宏;判断为FreeBSD操作系统之后,会定义__FREEBSD__
宏。
通过系统宏来定义系统特定操作
SDL2在这一点上做的很好。它定义了平台宏之后,为每一个平台写了一个config文件(SDL_config_os2.h,SDL_config_android.h等)。然后将这些头文件按照平台包含到一个总的config文件中(SDL_config.h),之后想要利用平台相关特性就可以只包含这个总的config文件了:
|
|
在每个系统独立config文件中,通过宏来表示“这个系统能做到什么”:
|
|
这里在Macosx中就没有ATOI函数,所以就没有定义HAVE_ATOI
宏。而windows里面有了,就可以定义这个宏。
一般都会定义如下的宏:
- 是否有某个头文件的,比如
HAVE_MATH_H
- 是否有需要用到的函数,比如
HAVE_MALLOC
- 为平台特定函数声明宏,比如
HAVE_ATOI
然后你就可以通过这些宏来控制平台头文件和函数了。
同一模块不同系统编写不同的文件
有了以上的准备,当我们编写平台之间差别较大的函数的时候(比如GUI界面需要用到不同系统的API),就可以为不同的平台编写不同的文件。比如SDL的文件组织如下:
这里为线程方面编写了不同系统的文件。为计时器方面编写了不同系统的文件。
总结
跨平台库需要设计者知道各个系统的API,并且有很大的代码量。开发跨平台库实属不易。