#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. 包含头文件与源文件的区别

你可能感兴趣的:(#include " *.c / *.cpp "源文件的理解和使用)