跨平臺程式碼三種組織方式詳解
導讀 | 我們在原始碼中也會遇到一些跨平臺的問題。不同的功能,在不同的平臺下,實現方式是不一樣的,如何對這些平臺相關的程式碼進行組織呢?這篇文章就來聊聊這個問題。 |
在上一篇文章中,分享了一個跨平臺的標頭檔案是長成什麼樣子的,這個標頭檔案對於 windows 平臺下更有意義一些,因為要處理庫函式的匯入和匯出宣告(dllexport、dllimport)。
其實,可以在這個標頭檔案的基礎上繼續擴充,以達到更細粒度的控制。例如:對編譯器的判斷、對編譯器版本的判斷等等。
同樣的,我們在原始碼中也會遇到一些跨平臺的問題。不同的功能,在不同的平臺下,實現方式是不一樣的,如何對這些平臺相關的程式碼進行組織呢?這篇文章就來聊聊這個問題。
PS: 文末提供了一個簡單的、跨平臺構建程式碼示例。
假設我們寫一個庫,需要實現一個函式:獲取系統時間戳。作為實現庫的作者,你決定提供下面的 API 函式:
t_time.h: 宣告介面函式(t_get_timestamp); t_time.c:實現介面函式;
下面的任務就是在函式實現中,透過不同下的 C 庫或系統呼叫,來計算系統當前的時間戳。
在 平臺下,可以透過下面這段程式碼實現:
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;
那麼問題來了:怎麼把這兩段平臺相關的程式碼組織在一起?下面就介紹 3 種不同的組織方式,沒有優劣之分,每個人都有不同的習慣,選擇適合自己和團隊的方式就行。
此外,這個示例中只有 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 函式中了,透過平臺宏定義進行條件編譯,因為程式碼比較短小,看起來還不錯。
把不同平臺的實現程式碼放在獨立的檔案中,然後透過 #include 預處理符號,在 API 函式中,把平臺相關的程式碼引入進來。
也就是再增加 2 個檔案:
t_time_linux.c:存放 Linux 平臺下的程式碼實現; t_time_windows.c:存放 Windows 平臺下的程式碼實現; (1) t_time_linux.c #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#includeint64 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#elif defined(T_WINDOWS) #include#else int64 t_get_timestamp() { return -1; } #endif
有些人比較反感這樣的組織方式,一般都是 include 一個 .h 標頭檔案,而這裡透過平臺宏定義,include 不同的 .c 原始檔,感覺怪怪的?!
其實,也有一些開源庫是這麼幹的,比如下面:
在上面方案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 t_time_${PLATFORM}.c)
這樣的組織方式,感覺程式碼更“乾淨”一些。同樣的,我們也可以看到一些開源庫也是這麼做的:
為了文章的篇幅,以上只是貼了程式碼的片段。
我寫了一個最簡單的 demo,使用 cmake 來構建跨平臺的動態庫、靜態庫、可執行程式。寫這個 demo 的目的,主要是作為一個外殼,來測試一些寫文章時的程式碼。
在 Linux 平臺下,透過 cmake 指令手動編譯;在 Windows 平臺下,可以透過 CLion 整合開發環境直接編譯、執行,也可以透過 cmake 工具直接生成 VS2017/2019 解決方案。
已經把這個 demo 放在 gitee 倉庫中了
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2769691/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 程式碼模型組織方式模型
- 六種組織CSS的方式CSS
- 組織css程式碼CSS
- 程式的定義、組成、組織方式、特徵特徵
- 短視訊平臺原始碼,Spring配置資料來源的三種方式原始碼Spring
- LR低程式碼快速開發平臺 高效調整企業組織架構架構
- 小程式跨平臺開發解決方案探索
- Sublime Text——高效的跨平臺程式碼編輯器
- Python|讀、寫Excel檔案(三種模組三種方式)PythonExcel
- Go包-程式碼組織者Go
- jProcesses:使用Java獲取跨平臺程式的詳細資訊Java
- 程式間的五種通訊方式介紹-詳解
- SaaS平臺的組織資料模型設計模型
- 直播平臺原始碼,JavaScript 的四種除錯輸出方式原始碼JavaScript除錯
- 如何組織大型 Rust 程式碼庫Rust
- 低程式碼平臺建立數字應用程式的高效方式
- Python種匯入模組的三種方式總結Python
- 組織部幹部管理平臺開發,智慧組工人事決策平臺建設方案
- 跨平臺渲染引擎之路:框架與核心模組框架
- 相關前臺跨域的解決方式跨域
- (譯)詳解在React中跨元件分發狀態的三種方法React元件
- 短視訊平臺原始碼,分享時生成二維碼的兩種方式原始碼
- 【SpringMVC】解決跨域問題的兩種方式SpringMVC跨域
- Jmeter系列(45)- 詳解 Jmeter 跨執行緒組取引數值的方法,免程式碼!JMeter執行緒
- 低程式碼平臺選型(三)國產化
- 位元組碼詳解
- rust跨平臺Rust
- 詳解Nginx 虛擬主機配置的三種方式(基於埠)Nginx
- 詳解Nginx 虛擬主機配置的三種方式(基於IP)Nginx
- 多表連線的三種方式詳解 hash join、merge join、 nested loopOOP
- 你是如何組織html程式碼的?HTML
- 如何凝聚黨員力量?智慧組工系統構架組織部管理平臺解決方案
- 黨組織部幹部管理系統開發,智慧組工平臺建設解決方案
- 程式碼迭代的幾種方式
- 好程式設計師分享JavaScript六種繼承方式詳解程式設計師JavaScript繼承
- 如何組織軟體模組的程式碼結構?
- 前端開發入門到實戰:六種組織CSS的方式前端CSS
- 詳解驗證碼與打碼平臺的攻防對抗