当前位置: 首页 > 科技观察

详细讲解跨平台代码的三种组织方式

时间:2023-03-18 21:07:54 科技观察

1.起源上一篇分享了跨平台的头文件是什么样子的。这个头文件对于windows平台比较有意义,因为要处理库函数的导入导出声明(dllexport,dllimport)。其实可以在这个头文件的基础上继续扩展,实现更细粒度的控制。比如:判断编译器,判断编译器的版本等等。同样,我们在源码中也会遇到一些跨平台的问题。不同的功能在不同的平台上以不同的方式实现。如何组织与这些平台相关的代码?本文将讨论这个问题。PS:文末提供了一个简单的跨平台构建代码示例。2.问题介绍假设我们写了一个库,需要实现一个功能:获取系统时间戳。作为实现库的作者,您决定提供以下API函数:t_time.h:声明接口函数(t_get_timestamp);t_time.c:实现接口函数;下面的任务是通过不同的C库或系统实现函数调用来计算系统当前的时间戳。在Linux平台下,可以通过如下代码实现:structtimevaltv;gettimeofday(&tv,null);returntv.tv_sec*1000+tv.tv_usec/1000;在Windows平台上,可以通过如下代码实现:structtimebtp;ftime(&tp);returntp.time*1000+tp.millitm;那么问题来了:这两段平台相关的代码如何组织在一起呢?这里有3种不同的组织方式,没有优劣之分,每个人都有不同的习惯,选择适合自己和团队的方式即可。另外,这个例子中只有1个函数,而且比较短。如果这样的跨平台功能很多,而且很长,可能你的选择不一样。3.三种方案方案一直接在接口函数中,通过平台宏定义来区分不同的平台。平台宏定义(T_LINUX、T_WINDOWS)在上一篇文章中介绍过。它通过操作系统和编译器判断当前平台是什么,然后定义一个统一的平台宏定义供我们自己使用:代码组织如下:int64t_get_timestamp(){int64ts=-1;#ifdefined(T_LINUX)structtimevaltv;gettimeofday(&tv,null);ts=tv.tv_sec*1000+tv.tv_usec/1000;#elifdefined(T_WINDOWS)structtimebtp;ftime(&tp);ts=tp.time;ts=ts*1000+tp.millitm;#endifreturnts;}这样所有平台代码都放在API函数中,通过平台宏定义进行条件编译,因为代码比较短,看它看起来不错。方案二将不同平台的实现代码放在单独的文件中,然后通过#include预处理符号在API函数中引入平台相关代码。即多加两个文件:t_time_linux.c:存放linux平台下的代码实现;t_time_windows.c:存放windows平台下的代码实现;(1)t_time_linux.c#include"t_time.h"#includeint64t_get_timestamp(){int64ts=-1;structtimevaltv;gettimeofday(&tv,null);ts=tv.tv_sec*1000+tv.tv_usec/1000;returns;}(2)t_time_windows.c#include"t_time.h"#include#includeint64t_get_timestamp(){int64ts=-1;structtimebtp;ftime(&tp);ts=tp.time;ts=ts*1000+tp.millitm;返回;}(3)t_time.c文件只包含其他代码。#include"t_time.h"#ifdefined(T_LINUX)#include#elifdefined(T_WINDOWS)#include#elseint64t_get_timestamp(){return-1;}#endif有些人不喜欢这样的组织方式一般是包含一个.h头文件,但是这里通过平台宏定义来包含不同的.c源文件就奇怪了?!其实也有一些开源库是这样做的,比如下面的:方案三在上述方案二中,在源码中填写了不同平台的实现代码。事实上,你可以改变思维方式。由于已经根据不同的平台放在了不同的文件中,所以可以在编译过程中加入不同的源文件。测试代码是使用cmake工具构建的,因此可以通过编辑CMakelists.txt文件来控制哪些源文件参与编译。部分CMakelists.txt文件#设置平台变量if(CMAKE_SYSTEM_NAMEMATCHES"Linux")set(PLATFORMlinux)elseif(CMAKE_SYSTEM_NAMEMATCHES"Windows")set(PLATFORMwindows)endif()#根据平台变量编译不同的源文件set(LIBSRCt_time_${PLATFORM}.c)这种组织方式让代码感觉更“干净”。同样,我们也可以看到一些开源库也是这样做的:4.OneMoreThing由于文章的篇幅,以上只是一小段代码。我写了一个最简单的demo,使用cmake构建跨平台的动态库、静态库和可执行程序。写这个demo的目的主要是作为写文章时测试一些代码的shell。在Linux平台上,通过cmake命令手动编译;在Windows平台上,可以直接通过CLion集成开发环境编译执行,也可以通过cmake工具直接生成VS2017/2019方案。本demo已放在gitee仓库,感兴趣的小伙伴请在公众号回复:dg36,即可领取克隆地址。