LoginSignup
6
4

More than 5 years have passed since last update.

Cマクロによる似非lambda

Last updated at Posted at 2015-11-30

C言語への感謝の正拳突き 1日目です。

概要

タイトルは「マクロによる似非lambda」とか偉そうなことを言ってますが、
要はマクロを使って重複した宣言や記述を取り除ためのtipsの紹介です。

冗長な関数定義、switchのcase定義、structの定義
などにおける繰り返しを除去し、マクロを使って一ヶ所に纏めることができます。

似非lambdaは3ステップから構成されています。
(1) 繰り返し要素のリストを定義するマクロ
(2) 手続きを定義するマクロ
(3) リストのマクロを手続きのマクロを使って展開する

具体例

Dart SDK(ただしこいつはC++)から引用しました。

dart/runtime/vm/method_recognizer.h

#define CORE_INTEGER_LIB_INTRINSIC_LIST(V)                                     \  ...(1)
  V(_IntegerImplementation, _addFromInteger, Integer_addFromInteger, 438687793) \
  V(_IntegerImplementation, +, Integer_add, 1324179652)                        \
  V(_IntegerImplementation, _subFromInteger, Integer_subFromInteger, 562800077)\
  ...

(1) CORE_INTEGER_LIB_INTRINSIC_LISTマクロによりINTRINSICの一覧を定義しています。

dart/runtime/vm/intrinsifier.cc

#define SETUP_FUNCTION(class_name, function_name, destination, fp)             \  ...(2)
  if (strcmp(#class_name, "::") == 0) {                                        \
    str = String::New(#function_name);                                         \
    func = lib.LookupFunctionAllowPrivate(str);                                \
  }

  // Set up all core lib functions that can be intrisified.
  lib = Library::CoreLibrary();
  CORE_INTEGER_LIB_INTRINSIC_LIST(SETUP_FUNCTION);                                ...(3)

(2) SETUP_FUNCTIONマクロにより、展開する手続きを定義しています。
SETUP_FUNCTIONの仮引数は4つありますが、CORE_INTEGER_LIB_INTRINSIC_LISTの実引数4つに対応しているのがポイントです。

(3)はCORE_INTEGER_LIB_INTRINSIC_LIST(SETUP_FUNCTION)の行です。
CORE_INTEGER_LIB_INTRINSIC_LISTで定義した要素分、
SETUP_FUNCTIONのifの式を展開することができます。

dart/runtime/vm/intrinsifier.cc
#define EMIT_CASE(class_name, function_name, enum_name, fp)                    \  ...(2)
    case MethodRecognizer::k##enum_name:                                       \
      compiler->assembler()->Comment("Intrinsic");                             \
      enum_name(compiler->assembler());                                        \
      break;

  // Integer intrinsics are in the core library, but we dont want to
  // intrinsify when Smi > 32 bits if we are looking for javascript integer
  // overflow.
  if (!(FLAG_throw_on_javascript_int_overflow && (Smi::kBits >= 32))) {
    switch (function.recognized_kind()) {
      CORE_INTEGER_LIB_INTRINSIC_LIST(EMIT_CASE)                                 ...(3)
      default:
        break;
    }
  }

こちらの例は、(2)でcase式を定義し、(3)でswitchのブロック内にcase式をリスト数分展開しています。

Dart SDKはC++ですが、ソースコードを引用したのはわかりやすい名前をつけているからです。

注意点

(1) 非常にわかりやすいシンプルな例を上げましたが、調子にのって複雑な使い方をすると痛い目にあいます。
gcc/clangのプリプロセッサでは置換できるけれども、windows clでは置換に失敗するケースに出会うことがあります。マクロの使いすぎには注意しましょう。

(2) 思ったとおりに展開されないと思ったら、プリプロセス結果を出力しておいて目視確認です。

(3) マクロで書かれた手続きの実行時デバッグは非常に困難です。プリプロセッサで1行に置換されてしまいますし、デバッガは行単位でのstep実行しかサポートしていないからです。そのためstep実行したときに止まりSEGVするのは(3)で示した行だけです。

(4) マクロを使った小手先の共通化や抽象化より、C++のテンプレートのほうが良い場合が多いため、ModernなC++の使用を検討しましょう。

このマクロ置換方法の名前をご存知の方、教えていただけるとうれしいです。

以上

6
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4