有些時(shí)候,一些原本的函數(shù)功能可能并不是我們想要的,于是就想著修改函數(shù),或者再封裝一層函數(shù)。
比如對一個(gè)函數(shù)包裝:
void func()
{
printf("hello/n");
}
// 包裝函數(shù)
void my_func()
{
printf("add/n");
func();
}
打印效果如下:
每打印一個(gè) hello 前面都會增加一個(gè) add。
這樣確實(shí)能達(dá)到效果,但是因?yàn)槎嗾{(diào)用了一次函數(shù),所以性能會部分下降,同時(shí)需要更大的棧空間,那么是否有一種更好的方式去達(dá)到相同的目的呢?
有的,那就是使用宏進(jìn)行偷梁換柱,達(dá)到貍貓換太子的目的。
我們以打印函數(shù)為例,對它進(jìn)行偷梁換柱。
一般的打印函數(shù)只會打印我們輸入給它的參數(shù),卻無法打印額外的信息,比如時(shí)間戳、函數(shù)名、文件名、行號等。簡單一點(diǎn),假如我們希望在打印我們的消息前,能添加時(shí)間戳和函數(shù)名,又該如何做呢?
簡單且易理解的偷梁換柱如下:
// 定義我們自己的打印函數(shù)格式
#define OSPREY_LOG(fmt, ...) rt_kprintf("<%08d>[%s] "fmt"/r/n",/
rt_tick_get(), __FUNCTION__, ##__VA_ARGS__)
#undef printf // 使 printf 在下面失去效用
// 重新定義,此時(shí)下面的所有 printf 是一個(gè)宏,而不是函數(shù)
#define printf(fmt, ...) OSPREY_LOG(fmt, ##__VA_ARGS__)
void osprey_task(void *parameter)
{
uint32_t nbr = 0;
while(1)
{
printf("hello, Osprey %u", nbr++);
rt_thread_delay(1);
}
}
原本我們的代碼里面使用的是 printf 進(jìn)行打印,但自己比較懶,不想每次換平臺的時(shí)候都修改打印函數(shù)(比如 RT-Thread 使用 rt_kprintf 打印),或者怕替換的時(shí)候操作失誤,那么此時(shí)就可以使用這個(gè)技巧了。
我們先定義出我們自己的打印格式(關(guān)于這個(gè),魚鷹會專門寫一篇筆記介紹如何設(shè)計(jì)一個(gè)簡單實(shí)用的日志打印框架,里面會詳細(xì)介紹這些內(nèi)容,目前暫時(shí)拿來用就行),這個(gè)格式包含了時(shí)間戳信息、函數(shù)名信息。
之后,使用 #undef 這條預(yù)編譯指令取消掉 printf 的作用域,接下來的代碼將不再調(diào)用標(biāo)準(zhǔn)庫的函數(shù),而是使用我們自己定義的宏函數(shù) printf,所以我們使用 #define 重新定義 printf。
也就是說,#undef printf 指令后面的代碼將使用 宏函數(shù) printf ,而不是標(biāo)準(zhǔn)庫函數(shù) printf,這是一道分水嶺。
接下來看看打印的效果如何:
可以看到,在我們的 “hello,Osprey”之前,打印了我們需要的時(shí)間戳和函數(shù)名信息,完美!
通過這些信息,我們就可以知道打印的消息是在什么時(shí)候打印的,又是在哪個(gè)函數(shù)中打印的,定位問題將更加方便(當(dāng)然你也可以加入文件名和行號,看自己的需要了)。
通過以上三行代碼,我們成功且高效的完成了函數(shù)的再次封裝,并且除了這些代碼,不需要對后面的代碼做任何修改,萬一平臺換了,也只需要修改這些代碼就行。
現(xiàn)在再來一個(gè)稍微難理解一點(diǎn)的。
既然前面的代碼可以替換 printf 打印函數(shù),那么我們會想,是否可以替換 rt_kprintf 本身呢?
也就是說本來我的代碼就是用 rt_kprintf 函數(shù)打印的,我們是否可以對它進(jìn)行封裝呢?
所以接下來的代碼應(yīng)運(yùn)而生:
// 定義我們自己的打印函數(shù)格式
#define OSPREY_LOG(fmt, ...) rt_kprintf("<%08d>[%s] "fmt"/r/n",/
rt_tick_get(), __FUNCTION__, ##__VA_ARGS__)
#undef rt_kprintf // 使 rt_kprintf 在下面失去效用
// 重新定義,此時(shí)下面的所有 rt_kprintf 是一個(gè)宏,而不是函數(shù)
#define rt_kprintf(fmt, ...) OSPREY_LOG(fmt, ##__VA_ARGS__)
void osprey_task(void *parameter)
{
uint32_t nbr = 0;
while(1)
{
rt_kprintf("hello, Osprey %u", nbr++);
rt_thread_delay(1);
}
}
當(dāng)你測試后,你會發(fā)現(xiàn),打印效果和前面的代碼等同,也就是說,通過三條代碼,成功將 rt_kprintf 貍貓換太子了。
其實(shí)當(dāng)你理解了 #undef 和 #define,上面代碼是不難理解的,#undef 取消了 rt_kprintf 的定義,而 #define 又重新定義了 rt_kprintf,所以接下來的:
rt_kprintf("hello, Osprey %u", nbr++);
被替換成了 :
rt_kprintf("<%08d>[%s] "hello, Osprey %u"/r/n",/rt_tick_get(), __FUNCTION__, nbr++)
因?yàn)?rt_kprintf 函數(shù)已經(jīng)有了,最后編譯、鏈接的時(shí)候也就能順利通過了,爽!
當(dāng)然,有時(shí)候版本發(fā)布的時(shí)候,我們發(fā)現(xiàn)不再需要打印函數(shù)了,那么我們只要使用如下方式即可消除打印(文件開頭添加即可,注意 //):
這個(gè)騷操作,你學(xué)會了嗎?
審核編輯 黃昊宇
-
函數(shù)
+關(guān)注
關(guān)注
3文章
4338瀏覽量
62751
發(fā)布評論請先 登錄
相關(guān)推薦
評論