一、起源
在前一篇文章中,我分享了一个跨平台头文件的示例,该示例在 Windows 平台上更为重要,因为它需要处理库函数的导入和导出声明(dllexport、dllimport)。基于这个头文件,我们可以进一步扩展,以实现更细粒度的控制,比如对编译器和其版本的判断。
在源代码中,我们同样会遇到一些跨平台的问题。不同平台上的相同功能,实现方式可能有所不同。那么,如何有效地组织这些平台相关的代码呢?本文将探讨这个问题。
PS:文末将提供一个简单的跨平台构建代码示例。
二、问题引入
假设我们要编写一个库,需要实现一个获取系统时间戳的函数。作为库的作者,你决定提供以下API函数:
我们的任务是在函数实现中,通过不同平台下的C库或系统调用来计算系统当前的时间戳。
在Linux平台下,可以通过以下代码实现:
struct timeval tv;gettimeofday(&tv, NULL);return tv.tv_sec * 1000 + tv.tv_usec / 1000;
在Windows平台下,可以通过以下代码实现:
struct timeb tp;ftime(&tp);return tp.time * 1000 + tp.millitm;
那么问题来了:如何将这两段平台相关的代码组织在一起?下面将介绍三种不同的组织方式,这些方式没有优劣之分,适合每个人和团队的不同习惯。
此外,这个示例中只有一个函数,而且较短。如果有许多跨平台的函数,且都很长,你的选择可能会有所不同。
三、三个解决方案
方案1
直接在接口函数中,通过平台宏定义来区分不同平台。
平台宏定义(T_LINUX, T_WINDOWS)是在前一篇文章中介绍的,通过操作系统和编译器来判断当前平台,并定义统一的平台宏供我们使用:
代码组织方式如下:
int64 t_get_timestamp() { int64 ts = -1; #if defined(T_LINUX) struct timeval tv; gettimeofday(&tv, NULL); ts = tv.tv_sec * 1000 + tv.tv_usec / 1000; #elif defined(T_WINDOWS) struct timeb tp; ftime(&tp); ts = tp.time; ts = ts * 1000 + tp.millitm; #endif return ts;}
这种方式将所有平台代码放在API函数中,通过平台宏定义进行条件编译。由于代码较短,看起来还不错。
方案2
将不同平台的实现代码放在独立的文件中,然后通过#include预处理符号,在API函数中引入平台相关的代码。
增加两个文件:
(1) t_time_linux.c
代码小浣熊
代码小浣熊是基于商汤大语言模型的软件智能研发助手,覆盖软件需求分析、架构设计、代码编写、软件测试等环节
51 查看详情
#include "t_time.h"#includeint64 t_get_timestamp() {int64 ts = -1;struct timeval tv;gettimeofday(&tv, NULL);ts = tv.tv_sec * 1000 + tv.tv_usec / 1000;return ts;}
(2) t_time_windows.c
#include "t_time.h"include
include
int64 t_get_timestamp() {int64 ts = -1;struct timeb tp;ftime(&tp);ts = tp.time;ts = ts * 1000 + tp.millitm;return ts;}
(3) t_time.c
这个文件不做任何事情,仅用于include其他代码。
#include "t_time.h"if defined(T_LINUX)
#include "t_time_linux.c"elif defined(T_WINDOWS)
#include "t_time_windows.c"else
int64 t_get_timestamp() { return -1;}endif
有些人可能不喜欢这种组织方式,因为通常我们会include头文件(.h),而这里通过平台宏定义include不同的源文件(.c),感觉有些怪异。
实际上,一些开源库也是这样做的,例如:
方案3
在方案2中,我们是在源代码中填入不同平台的实现代码。
实际上,我们可以换一种思路,既然已经根据平台的不同放在了不同的文件中,那么可以通过让不同的源文件加入到编译过程中来实现。
我们使用cmake工具来构建测试代码,因此可以编辑CMakelists.txt文件,来控制参与编译的源文件。
CMakelists.txt文件部分内容:
# 设置平台变量if (CMAKE_SYSTEM_NAME MATCHES "Linux")set(PLATFORM linux)elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")set(PLATFORM windows)endif()
根据平台变量,来编译不同的源文件
set(LIBSRC ttime${PLATFORM}.c)
这种组织方式让代码看起来更“干净”。同样,我们也可以看到一些开源库也是这样做的:
四、One More Thing
由于文章篇幅原因,上述仅展示了代码片段。
我编写了一个最简单的demo,使用cmake来构建跨平台的动态库、静态库和可执行程序。这个demo的主要目的是作为一个外壳,用于测试文章中的代码。
在Linux平台下,可以通过cmake指令手动编译;在Windows平台下,可以通过CLion集成开发环境直接编译和执行,也可以通过cmake工具直接生成VS2017/2019解决方案。
这个demo已上传至gitee仓库,有兴趣的小伙伴可以通过公众号dg36获取克隆地址。
以上就是跨平台代码的3种组织方式的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/593989.html
微信扫一扫
支付宝扫一扫