溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

宏如何正確的在C語(yǔ)言與C++項(xiàng)目中使用

發(fā)布時(shí)間:2020-12-11 14:35:44 來源:億速云 閱讀:182 作者:Leah 欄目:開發(fā)技術(shù)

宏如何正確的在C語(yǔ)言與C++項(xiàng)目中使用?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

C++項(xiàng)目中常使用宏來做跨平臺(tái)、功能實(shí)現(xiàn)隔離、變量定義的功能,這篇文章來討論下是否所有情況下都適合用宏

小D的故事

程序員小D接到一個(gè)任務(wù),需要給同事A提供一個(gè)復(fù)雜公式的實(shí)現(xiàn)。輸入為一組參數(shù),輸出一個(gè)計(jì)算結(jié)果。

大致如下:

double computeSomeThing(double paramA,double paramB,double paramC);

小D很快完成了。過了幾天同事A又來找他,說現(xiàn)在需要提升該函數(shù)的性能,建議改為在float類型上,用一些SIMD指令。且同事A表示不是很愿意修改接口。于是小D在考慮以下兩點(diǎn)后決定用一個(gè)宏把原來double的實(shí)現(xiàn)和float的實(shí)現(xiàn)分開來。

1、上層需求變動(dòng)性比較大,說不定哪天又要用double了。所以還是保留double類型的實(shí)現(xiàn)

2、用宏把兩份代碼隔開來,互相不影響比較省事

于是代碼就變成了這樣:

double computeSomeThing(double paramA,double paramB,double paramC)
{
 #ifdef _USE_DOUBLE_
 // do something in double
 #else
 // convert dobule to float 
 // do something in float
 // convert float to double 
 #endif
}

同事A很滿意,因?yàn)樗灰鎿Q一下.so或.a即可,代碼層不需要改動(dòng)。于是和小D合作開發(fā)了很多這樣的函數(shù),并且都有float和double兩種實(shí)現(xiàn)。在對(duì)性能要求高的時(shí)候要求小D提供float版本;性能要求低,精度要求的時(shí)候要求小D提供double版本。

此時(shí)小D會(huì)在出庫(kù)的時(shí)候感到一絲不方便。第一,版本號(hào)中需要區(qū)分float和double版本。

第二,因?yàn)橛煤旮糸_,切換兩個(gè)版本的時(shí)候需要重新編譯,而代碼量很多所以編譯時(shí)間很長(zhǎng),但這些都是能克服的。

直到有一天同事小B的模塊也需要這個(gè)庫(kù),并且小A和小B的模塊要組合起來給小C用,最要命的是小A和小B的模塊分別要用float版本和double版本。所以此時(shí)應(yīng)該提供float版本so還是提供double版本so呢。

問題分析

在上面的場(chǎng)景中,小D作為一個(gè)基礎(chǔ)庫(kù)的提供者不應(yīng)該因?yàn)橥虏辉敢庑薷慕涌诨蛘邎D方便用宏去隔離功能,使得一個(gè)接口有了二義性。比較合適的一種做法是,再提供一個(gè)控制選擇變量,來選擇用哪種實(shí)現(xiàn),即允許運(yùn)行時(shí)決定用float還是double版本。

double computeSomeThing(double paramA,double paramB,double paramC,bool isFast);

或者小A就是不愿意改接口(考慮實(shí)際項(xiàng)目中,接口參數(shù)復(fù)雜且調(diào)用分散在各處),那么也可以通過增加接口實(shí)現(xiàn)。

double computeSomeThing(double paramA,double paramB,double paramC);

void setFast(bool isFast);

下面的情況用宏做隔離就是比較合理的選擇。

比如一套代碼要分別運(yùn)行在linux和windows上,依賴的頭文件、部分基礎(chǔ)函數(shù)接口都是有區(qū)別的。此時(shí)用宏去隔離就比較合理。因?yàn)檫@兩個(gè)版本在運(yùn)行時(shí)永遠(yuǎn)不會(huì)同時(shí)出現(xiàn)。除了平臺(tái)差異性外,版本管理也可以用宏來做隔離。

比如opencl 1.2和opencl 2.0版本相比較的話,2.0版本中新增了SVM相關(guān)的接口。當(dāng)一個(gè)opencl程序未來可能運(yùn)行在1.2版本的設(shè)備和2.0版本的設(shè)備上時(shí)。

可以用宏來選擇是否屏蔽掉SVM接口。因?yàn)?.0的接口運(yùn)行在1.2的設(shè)備上時(shí),無法從環(huán)境中獲取2.0新增的接口實(shí)現(xiàn)導(dǎo)致程序跑不起來(1.2的相關(guān)so中沒有SVM函數(shù)實(shí)現(xiàn))。

不過這個(gè)問題用宏來處理也不是最優(yōu)的,使用dlopen可以有更靈活的實(shí)現(xiàn)。

總結(jié)

對(duì)于做基礎(chǔ)庫(kù)提供給很多人使用的同學(xué),當(dāng)用宏隔開的代碼有可能會(huì)同時(shí)運(yùn)行在一個(gè)環(huán)境時(shí)建議改為運(yùn)行時(shí)選擇走哪條分支。但肯定互相不兼容的時(shí)候就放心的用宏吧,比如跨操作系統(tǒng)。

另外提一下,對(duì)于有很多代碼的大項(xiàng)目用宏的時(shí)候也要慎重考慮一下,不要?jiǎng)硬粍?dòng)就用宏去做一些功能開關(guān),因?yàn)榫幾g時(shí)間太長(zhǎng)是很影響效率的。

比如有以下宏定義:

#define _OPEN_LOG_
#ifdef _OPEN_LOG_
 #define LOG_PRINT(...) printf(...)
#else
 #define LOG_PRINT(...) 
#endif

開發(fā)階段代碼中到處插著LOG_PRINT的使用,發(fā)布時(shí)關(guān)閉打印又是一波整個(gè)項(xiàng)目重新編譯。再多來幾個(gè)這種功能,每次切換又是整個(gè)項(xiàng)目重新編譯,非常煩人??梢杂煤瘮?shù)指針代替:

typedef void (*LogPrint)(const char * pstrMsg);
LogPrint g_LogPrint;
void LogPrint_Imp(const char *pstrMsg)
{
 printf("%s\n",pstrMsg);
 return;
}
void LogPrint_Empty(const char *pstrMsg)
{
 return;
}
int main(int argc,char **argv)
{
 // 此處對(duì)日志功能進(jìn)行開關(guān)
 g_LogPrint = LogPrint_Imp ; 
 //g_LogPrint = LogPrint_Empty ; 
 // .....
}
void someFun()
{
 g_LogPrint("in someFun"); //到底打印還是不打印,運(yùn)行時(shí)決定
}

在這個(gè)例子中,關(guān)閉日志時(shí)編譯器只會(huì)對(duì)main函數(shù)所在的文件進(jìn)行重新編譯,就不用費(fèi)時(shí)費(fèi)力的重新編譯整個(gè)項(xiàng)目了。而且還可以把g_LogPrint的賦值的行為通過接口開放到上層,由調(diào)用者決定是否需要打開log。

再舉個(gè)例子,有些人喜歡項(xiàng)目中各個(gè)代碼模塊中用到的參數(shù)提到一個(gè)頭文件中,然后各個(gè).c都包含這個(gè)頭文件。就像這樣:

// GobalParam.h
#ifndef XX_XX
#define XX_XX
#define DETECTION_MAX 100
#define INPUT_WIDTH_MAX 4096
#define INPUT_HEIGHT_MAX 4096
// 諸如此類很多宏
#endif

我個(gè)人覺得下面這種實(shí)現(xiàn)更好

// GobalParam.h
#ifndef XX_XX
#define XX_XX
extern const int DETECTION_MAX;
extern const int INPUT_WIDTH_MAX ;
extern const int INPUT_HEIGHT_MAX ;
// 在某個(gè).c或.cpp中賦值 :const int INPUT_HEIGHT_MAX = 100;
#endif

這樣你對(duì)某個(gè)參數(shù)修改的時(shí)候,就不用眼巴巴的等著所有包含此頭文件的編譯模塊重新編譯了。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

c c++
AI