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