最終更新:2026-10-12
オープンソース C++ コードの読み方¶
オープンソースプロジェクトのコードを瞬時に理解するには?¶
実際のプロジェクトやオープンソースコード、特に C コードはマクロを好んで使うため、大きな問題が起こります。コードの読解が非常に難しく、デバッグもまた別の問題です。大量のマクロを含むプロジェクトを渡されたとき、どうすれば素早く理解できるでしょうか?
面接ではよく、非常に重要な知識ポイントに出くわします。それは gcc のコンパイル過程です。皆さんはすらすら答えられるかもしれません。そこで候補者のレベルを区別するために、こんな質問が派生します:gcc のコンパイルパラメータをどれだけ知っていますか?各パラメータにはどのような役割があるでしょうか?前処理段階では何が起こるでしょうか?あなたは普段この段階で何をしますか?それはあなたにどのような役割があるでしょうか?
この節では、1 つのオープンソースプロジェクトを例に、コードをどう素早く理解するかを見ていきます!
以下は libev のコードの一部で、その中にいくつかのマクロがあります。例えば:
- EV_FREQUENT_CHECK
- assert
EV_P_- ANHE_w
- ANHE_at_cache
void noinline
ev_timer_start (EV_P_ ev_timer *w) EV_THROW
{
if (expect_false (ev_is_active (w)))
return;
ev_at (w) += mn_now;
assert (("libev: ev_timer_start called with negative timer repeat value", w->repeat >= 0.));
EV_FREQUENT_CHECK;
++timercnt;
ev_start (EV_A_ (W)w, timercnt + HEAP0 - 1);
array_needsize (ANHE, timers, timermax, ev_active (w) + 1, EMPTY2);
ANHE_w (timers [ev_active (w)]) = (WT)w;
ANHE_at_cache (timers [ev_active (w)]);
upheap (timers, ev_active (w));
EV_FREQUENT_CHECK;
/*assert (("libev: internal timer heap corruption", timers [ev_active (w)] == (WT)w));*/
}
一瞬「何だこれ」となりませんか?このコードからはいくつかの疑問が浮かびます:
- 関数のパラメータはいくつでしょう?
- 関数の型は?
- コード中の ev_at は何者でしょうか?
コードを瞬時に理解するために、-E を使って前処理段階でマクロ展開された後のコードを取得し、理解と読解を容易にします。
展開後、次のものが得られます:
void __attribute__ ((__noinline__))
ev_timer_start (struct ev_loop *loop, ev_timer *w)
{
if (__builtin_expect ((!!((0 + ((ev_watcher *)(void *)(w))->active))),(0)))
return;
((WT)(w))->at += ((loop)->mn_now);
# 3881 "ev.c" 3 4
((
# 3881 "ev.c"
("libev: ev_timer_start called with negative timer repeat value", w->repeat >= 0.)
# 3881 "ev.c" 3 4
) ? (void) (0) : __assert_fail (
# 388libev: ev_timer_start called with negative timer repeat value\", w->repeat >= 0.)"
# 3881 "ev.c" 3 4
, "ev.c", 3881, __PRETTY_FUNCTION__))
# 3881 "ev.c"
;
do { } while (0);
++((loop)->timercnt);
ev_start (loop, (W)w, ((loop)->timercnt) + (4 - 1) - 1);
if (__builtin_expect ((!!((((W)(w))->active + 1) > (((loop)->timermax)))),(0))) { int __attribute__ ((__unused__)) ocur_ = (((loop)->timermax)); (((loop)->timers)) = (ANHE *)array_realloc (sizeof (ANHE), (((loop)->timers)), &(((loop)->timermax)), (((W)(w))->active + 1)); ; };
(((loop)->timers) [((W)(w))->active]).w = (WT)w;
(((loop)->timers) [((W)(w))->active]).at = (((loop)->timers) [((W)(w))->active]).w->at;
upheap (((loop)->timers), ((W)(w))->active);
do { } while (0);
}
この時点でコードを理解するのはほぼ障害ありませんが、まだ十分に明快とは言えません。あなたと他の候補者との差を際立たせるため、いくつか質問を出しましょう:
- 上には多くの行数が表示されていますが、取り除けますか?どう解決しますか?
- 上の assert のようなシステムマクロは非常にシンプルで理解しやすく、展開する必要がありません。どうやるか分かりますか?
- 上の 2 段階の展開では問題が発生する可能性があります。何が起きているか、どう解決するか分かりますか?
それでは、この節はここまでにします。次の節で続けて共有します。上の答えについて、ぜひコメント欄で議論しましょう!
関連トピック¶
- C++ 文字列の 6 つの分割方法 | C++那些事
- CPython 拡張開発メモ | C++那些事
- libcurl C++ 開発メモ | C++那些事
- ある面白い C++ コード | C++那些事
- C++ ループなしで 1-100 を出力 | C++那些事