C言語で多重定義(オーバロード)を実現する方法【_GenericとC言語拡張】

C言語は関数の多重定義をサポートしていませんが、C11の_GenericやGNU拡張のoverloadableで関数の多重定義(オーバーロード)を実現することができます。

コンパイラ拡張が利用できる環境ではoverloadableの利用が比較的オススメです。

スポンサーリンク

_Generic

最新のC言語規格(C11)で追加された総称選択(generic selection, 汎用選択)という機能を用いることで、型に応じた式の分岐が可能となりました。

puts(_Generic(9999, int: "a",  double: "b")); // "a"
puts(_Generic(3.14, int: "a",  double: "b")); // "b"

式には一般的な値や文字列ポインタだけでなく、関数ポインタを指定することもできます。

この_Genericとマクロを応用することで、多重定義の挙動を実現することが可能となります。

void print_int(int v)            { printf("%d\n", v); }
void print_double(double v)      { printf("%f\n", v); }
void print_string(const char *v) { printf("%s\n", v); }

#define print(v) _Generic((v) \
   , int:          print_int \
   , double:       print_double \
   , char *:       print_string \
   , const char *: print_string \
)(v);
int main() {
   print(9999); // "9999"
   print(3.14); // "3.140000"
   print("hi"); // "hi"
}

__attribute__((overloadable))

GNU GCCやClangコンパイラの「属性機能」を利用することで、オーバロードの実現が可能となります。overloadable属性を利用します。

void print(int v) __attribute__((overloadable)) {
   printf("%d\n", v);
}
void print(double v) __attribute__((overloadable)) {
   printf("%f\n", v);
}
void print(const char *v) __attribute__((overloadable)) {
   printf("%s\n", v);
}
int main(int argc, char *argv[]) {
   print(9999); // "9999"
   print(3.14); // "3.140000"
   print("hi"); // "hi"
}

属性は前置することもできます。

__attribute__((overloadable)) void print(int v) {
   printf("%d\n", v);
}

マクロ置換で簡潔な記述を実現することも可能です。

#define OVERLOADABLE __attribute__((overloadable))
void print(int v) OVERLOADABLE {
   printf("%d\n", v);
}

overloadableは複数の引数に対するオーバロードにも有効です。これでデフォルト引数の目的を果たすことも可能となります。

void print(int v) __attribute__((overloadable)) {
   const char *terminator = "\n"; // デフォルト引数に相当
   printf("%d%s", v, terminator);
}
void print(int v, const char *terminator) __attribute__((overloadable)) {
   printf("%d%s", v, terminator);
}

int main() {
   print(99, "\t"); // "99\t"
   print(99);       // "99\n"
}

C言語で万能print関数を作る方法

# _Genericを応用することで、C言語で様々な型のフォーマットに対応したprint関数を作ることもできます。

#define GENERIC_DECL_FORMAT(v) _Generic((v), const char*:"%s", char*:"%s", char:"%c", int:"%d", long:"%ld", float:"%f", double:"%f")
#define GENERIC_PRINT(v) do { printf(GENERIC_DECL_FORMAT(v),  v), putchar('\n'); } while (0)
int main() {
   #define print GENERIC_PRINT
   const char* a = "a";
   const int   b = 2;
   const float c = 3.1;
   print(a);
   print(b);
   print(c);
   print(1234);
   print(3.14);
   print('c');
   print("ab");
   print((const char*)"ab");
   print((char*)"ab");
   char ary[3] = "ab";
   print(ary);
}

広告