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);
}