libev中ev_loop結構體中巨集定義的理解

I_belong_to_jesus發表於2020-12-15

libev原始碼由於有各種巨集定義,十分讓人費解,作者這麼寫確實使得程式碼很簡練,但也給讀者的閱讀帶來了巨大的麻煩,下面將分析下ev_loop這個結構體的定義,加深對作者程式碼簡化的理解,先上程式碼:

struct ev_loop
{
ev_tstamp ev_rt_now;
#define ev_rt_now ((loop)->ev_rt_now)
#define VAR(name,decl) decl;
#include "ev_vars.h"
#undef VAR
};
​

ev_loop是表示libev事件迴圈的結構體,我們看前兩行就極其費解,首先定義了變數ev_tstamp ev_rt_now;其中,ev_tstamp為float的轉義,這句沒什麼問題,即定義了一個float型變數ev_tstamp。然而作者緊接著就給來了一個相同的巨集定義#define ev_rt_now ((loop)->ev_rt_now),what a fuck!初讀感覺寫這程式碼的人真的腦抽了,定義的巨集和上一行定義的變數名一模一樣!這不會有問題?然而程式碼跑起來依然是好好的。那麼這麼定義會帶來什麼樣的結果呢?作者又為什麼要這麼玩?首先,我們要理解一下巨集定義的作用域,先看一個非常簡單的例子:

#include<iostream>
using namespace std;
int main()
{
int x=11;
cout<<x<<endl;
#define x 13
cout<<x<<endl;
return 0;
}
//輸出結果:
//11
//13

這是一個非常簡單的例子,兩個輸出結果分別是11和13,現在就比較容易理解了,C語言標準中巨集定義的作用域是,從定義位置開始,到其當前所在作用域結束,也就是說在巨集定義#define x 13之前我們定義了變數x,並初始化為11,在第一次cout的時候巨集定義未出現,所以預編譯階段x並不會被替換成13,而仍然是變數x,隨後由於加入巨集定義#define x 13,後面所有的x都會被替換為常量13。理解了這些,那麼就比較容易理解了ev_loop的結構了,ev_loop的定義在ev.c檔案中,首先定義了float成員變數ev_rt_now,由於此時巨集定義#define ev_rt_now ((loop)->ev_rt_now)在其後面,故預編譯並不會替換變數ev_rt_now,也就是對於結構體定義本身來說其和程式碼

struct ev_loop
{
ev_tstamp ev_rt_now;
#define VAR(name,decl) decl;
#include "ev_vars.h"
#undef VAR
};
​

的效果是一樣的,巨集定義並不會成為結構體成員,也不會替換其前面的結構體變數ev_rt_now。而在此之後,ev_rt_now都會被替換為((loop)->ev_rt_now),那作者為什麼要做這種替換呢,我們沿著ev.c的程式碼往下找,3097行可以找到如下程式碼:

ev_loop_new (unsigned int flags) EV_THROW
{
  EV_P = (struct ev_loop *)ev_malloc (sizeof (struct ev_loop));

  memset (EV_A, 0, sizeof (struct ev_loop));
  loop_init (EV_A_ flags);

  if (ev_backend (EV_A))
    return EV_A;

  ev_free (EV_A);
  return 0;
}

可以找到EV_P這個巨集定義實際為 struct ev_loop *loop,而loop_init定義也在ev.c,其中部分程式碼如下:

loop_init (EV_P_ unsigned int flags) EV_THROW
{
  if (!backend)
    {
      origflags = flags;

#if EV_USE_REALTIME
      if (!have_realtime)
        {
          struct timespec ts;

          if (!clock_gettime (CLOCK_REALTIME, &ts))
            have_realtime = 1;
        }
#endif

#if EV_USE_MONOTONIC
      if (!have_monotonic)
        {
          struct timespec ts;

          if (!clock_gettime (CLOCK_MONOTONIC, &ts))
            have_monotonic = 1;
        }
#endif

      /* pid check not overridable via env */
#ifndef _WIN32
      if (flags & EVFLAG_FORKCHECK)
        curpid = getpid ();
#endif

      if (!(flags & EVFLAG_NOENV)
          && !enable_secure ()
          && getenv ("LIBEV_FLAGS"))
        flags = atoi (getenv ("LIBEV_FLAGS"));

      ev_rt_now          = ev_time ();
      mn_now             = get_clock ();
      now_floor          = mn_now;
      rtmn_diff          = ev_rt_now - mn_now;

其中倒數第四行可以看到程式碼ev_rt_now = ev_time (),看到這裡,差不多明白了。也就是對於新建一個事務結構體struct ev_loop的例項(ev_loop_new函式來實現),需要分配記憶體、初始化其成員變數並返回指向該記憶體的指標,程式碼中這個指標變數名為loop( struct ev_loop *loop),先分配記憶體(EV_P = (struct ev_loop *)ev_malloc (sizeof (struct ev_loop));),後續初始化其成員變數的話需要通過指標呼叫的方式來進行,若不加此巨集定義,那麼ev_rt_now = ev_time ()必須改為loop->ev_rt_now=ev_time (),顯然就不如之前的程式碼看起來簡潔(不過我還是要說,簡潔是簡潔了,讀起來是真累啊,%>_<%)。ev_loop結構體其他變數的呼叫也是這麼實現的,變數定義在#ev_vars.h中,#include "ev_vars.h"在結構體中,故這裡定義了結構體的其他成員變數,定義完ev_loop結構體後,緊跟著的一行程式碼是

#include "ev_wrap.h"

er_wrap.h的內容如下:

#ifndef EV_WRAP_H
#define EV_WRAP_H
#define acquire_cb ((loop)->acquire_cb)
#define activecnt ((loop)->activecnt)
#define anfdmax ((loop)->anfdmax)
#define anfds ((loop)->anfds)
#define async_pending ((loop)->async_pending)
#define asynccnt ((loop)->asynccnt)
#define asyncmax ((loop)->asyncmax)
#define asyncs ((loop)->asyncs)
#define backend ((loop)->backend)
#define backend_fd ((loop)->backend_fd)
#define backend_mintime ((loop)->backend_mintime)
#define backend_modify ((loop)->backend_modify)
#define backend_poll ((loop)->backend_poll)
#define checkcnt ((loop)->checkcnt)
#define checkmax ((loop)->checkmax)
#define checks ((loop)->checks)
#define cleanupcnt ((loop)->cleanupcnt)
#define cleanupmax ((loop)->cleanupmax)
#define cleanups ((loop)->cleanups)
#define curpid ((loop)->curpid)
#define epoll_epermcnt ((loop)->epoll_epermcnt)
#define epoll_epermmax ((loop)->epoll_epermmax)
#define epoll_eperms ((loop)->epoll_eperms)
#define epoll_eventmax ((loop)->epoll_eventmax)
#define epoll_events ((loop)->epoll_events)
#define evpipe ((loop)->evpipe)
#define fdchangecnt ((loop)->fdchangecnt)
#define fdchangemax ((loop)->fdchangemax)
#define fdchanges ((loop)->fdchanges)
#define forkcnt ((loop)->forkcnt)
#define forkmax ((loop)->forkmax)
#define forks ((loop)->forks)
#define fs_2625 ((loop)->fs_2625)
#define fs_fd ((loop)->fs_fd)
#define fs_hash ((loop)->fs_hash)
#define fs_w ((loop)->fs_w)
#define idleall ((loop)->idleall)
#define idlecnt ((loop)->idlecnt)
#define idlemax ((loop)->idlemax)
#define idles ((loop)->idles)
#define invoke_cb ((loop)->invoke_cb)
#define io_blocktime ((loop)->io_blocktime)
#define iocp ((loop)->iocp)
#define kqueue_changecnt ((loop)->kqueue_changecnt)
#define kqueue_changemax ((loop)->kqueue_changemax)
#define kqueue_changes ((loop)->kqueue_changes)
#define kqueue_eventmax ((loop)->kqueue_eventmax)
#define kqueue_events ((loop)->kqueue_events)
#define kqueue_fd_pid ((loop)->kqueue_fd_pid)
#define loop_count ((loop)->loop_count)
#define loop_depth ((loop)->loop_depth)
#define loop_done ((loop)->loop_done)
#define mn_now ((loop)->mn_now)
#define now_floor ((loop)->now_floor)
#define origflags ((loop)->origflags)
#define pending_w ((loop)->pending_w)
#define pendingcnt ((loop)->pendingcnt)
#define pendingmax ((loop)->pendingmax)
#define pendingpri ((loop)->pendingpri)
#define pendings ((loop)->pendings)
#define periodiccnt ((loop)->periodiccnt)
#define periodicmax ((loop)->periodicmax)
#define periodics ((loop)->periodics)
#define pipe_w ((loop)->pipe_w)
#define pipe_write_skipped ((loop)->pipe_write_skipped)
#define pipe_write_wanted ((loop)->pipe_write_wanted)
#define pollcnt ((loop)->pollcnt)
#define pollidxmax ((loop)->pollidxmax)
#define pollidxs ((loop)->pollidxs)
#define pollmax ((loop)->pollmax)
#define polls ((loop)->polls)
#define port_eventmax ((loop)->port_eventmax)
#define port_events ((loop)->port_events)
#define postfork ((loop)->postfork)
#define preparecnt ((loop)->preparecnt)
#define preparemax ((loop)->preparemax)
#define prepares ((loop)->prepares)
#define release_cb ((loop)->release_cb)
#define rfeedcnt ((loop)->rfeedcnt)
#define rfeedmax ((loop)->rfeedmax)
#define rfeeds ((loop)->rfeeds)
#define rtmn_diff ((loop)->rtmn_diff)
#define sig_pending ((loop)->sig_pending)
#define sigfd ((loop)->sigfd)
#define sigfd_set ((loop)->sigfd_set)
#define sigfd_w ((loop)->sigfd_w)
#define timeout_blocktime ((loop)->timeout_blocktime)
#define timercnt ((loop)->timercnt)
#define timermax ((loop)->timermax)
#define timers ((loop)->timers)
#define userdata ((loop)->userdata)
#define vec_eo ((loop)->vec_eo)
#define vec_max ((loop)->vec_max)
#define vec_ri ((loop)->vec_ri)
#define vec_ro ((loop)->vec_ro)
#define vec_wi ((loop)->vec_wi)
#define vec_wo ((loop)->vec_wo)
#else
#undef EV_WRAP_H
#undef acquire_cb
#undef activecnt
#undef anfdmax
#undef anfds
#undef async_pending
#undef asynccnt
#undef asyncmax
#undef asyncs
#undef backend
#undef backend_fd
#undef backend_mintime
#undef backend_modify
#undef backend_poll
#undef checkcnt
#undef checkmax
#undef checks
#undef cleanupcnt
#undef cleanupmax
#undef cleanups
#undef curpid
#undef epoll_epermcnt
#undef epoll_epermmax
#undef epoll_eperms
#undef epoll_eventmax
#undef epoll_events
#undef evpipe
#undef fdchangecnt
#undef fdchangemax
#undef fdchanges
#undef forkcnt
#undef forkmax
#undef forks
#undef fs_2625
#undef fs_fd
#undef fs_hash
#undef fs_w
#undef idleall
#undef idlecnt
#undef idlemax
#undef idles
#undef invoke_cb
#undef io_blocktime
#undef iocp
#undef kqueue_changecnt
#undef kqueue_changemax
#undef kqueue_changes
#undef kqueue_eventmax
#undef kqueue_events
#undef kqueue_fd_pid
#undef loop_count
#undef loop_depth
#undef loop_done
#undef mn_now
#undef now_floor
#undef origflags
#undef pending_w
#undef pendingcnt
#undef pendingmax
#undef pendingpri
#undef pendings
#undef periodiccnt
#undef periodicmax
#undef periodics
#undef pipe_w
#undef pipe_write_skipped
#undef pipe_write_wanted
#undef pollcnt
#undef pollidxmax
#undef pollidxs
#undef pollmax
#undef polls
#undef port_eventmax
#undef port_events
#undef postfork
#undef preparecnt
#undef preparemax
#undef prepares
#undef release_cb
#undef rfeedcnt
#undef rfeedmax
#undef rfeeds
#undef rtmn_diff
#undef sig_pending
#undef sigfd
#undef sigfd_set
#undef sigfd_w
#undef timeout_blocktime
#undef timercnt
#undef timermax
#undef timers
#undef userdata
#undef vec_eo
#undef vec_max
#undef vec_ri
#undef vec_ro
#undef vec_wi
#undef vec_wo
#endif

顯然套路都是一樣的,先定義結構體中的變數,保證結構體內的變數不會被巨集定義替換,後加巨集定義保證後面程式碼的簡潔性。

     好啦,至此我們明白了整個ev_loop的結構,瞻仰下大神的精妙設計!但還是想奉勸一句,平時寫程式碼最好不要這麼玩,很容易出錯的!!!很容易捱揍!!!

,

相關文章