#include " *.c / *.cpp "源文件的理解和使用

今天在看redis源碼的時候,ae庫中爲了跨平臺式多路複用的實現,運用瞭如下代碼。

/* Include the best multiplexing layer supported by this system.
 * The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
    #ifdef HAVE_EPOLL
    #include "ae_epoll.c"
    #else
        #ifdef HAVE_KQUEUE
        #include "ae_kqueue.c"
        #else
        #include "ae_select.c"
        #endif
    #endif
#endif

一開始我還沒有注意到include包含的是源文件,下意識的以爲是常規操作頭文件,直到看了ae_epoll.c,ae_kqueue.c,ae_evport.c,ae_select.c,發現其中都實現了相同的接口(函數簽名),並且都是用靜態函數定義的,這時候才發現有點不對勁。以ae_epoll.c爲例。

static int aeApiCreate(aeEventLoop *eventLoop) {
   ...
}

static int aeApiResize(aeEventLoop *eventLoop, int setsize) {
   ...
}

static void aeApiFree(aeEventLoop *eventLoop) {
   ...
}

static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
   ...
}

static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) {
   ...
}

static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
   ...
}

static char *aeApiName(void) {
   ...
}

在我的印象中,全局靜態函數的作用域就只能在文件中,也就意味着A文件不能調用B文件中定義的靜態函數,突破這種限制的方法有兩個:

  1. B文件中定義一個新的非靜態函數,裏面調用本文件的靜態函數,然後A文件通過調用B文件中的非靜態函數來實現。(不同用於本次場景,因爲非靜態函數會有作用域衝突)
  2. B文件找到靜態函數的地址,A文件通過地址去調用。

Redis提供了另一種思路,就是通過#include “*.c”的方式調用同一個目錄下多個文件的靜態函數,然後用條件編譯指令來選擇具體的實現。其原理是在預處理階段,會將#include指令展開,如果調用的是源文件,就不會通過函數調用,而是直接展開的方式將其放到該源文件中的特定位置中,這樣做法優點是,不用編譯被調用的 *.c 文件,節省了一次調用函數的消耗,並且突破限制,因爲其是直接包含進來的(有點類似inline,但是內聯函數是通過編譯器控制來實現),缺點是被調用的 *.c 文件只能被一個其他文件include,否則就可能出現多重定義。

參考

  1. C與指針
  2. 包含頭文件與源文件的區別
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章