Dartにおけるマクロ機能の削除について議論し、GoやPython、Javaといった他の高級言語がなぜマクロを採用しないのかを探ります。
共通点:コンパイル時のコード生成の利点
C言語のマクロとDartのbuild_runnerは異なる実装方法を持っていますが、同じ目標を持っています。それは、実行前に問題を解決し、ランタイムのオーバーヘッドを減らすことです。
1. コンパイル前のコード生成
// C言語でのマクロ例
#define SQUARE(x) ((x) * (x))
double area = 3.14159 * SQUARE(radius);
// Dartでのbuild_runner例
@JsonSerializable()
class User {
final String name;
final int age;
}
2. ボイラープレートコードの排除
// C言語のgetter/setterマクロ
#define GETTER_SETTER(type, name) \
type get_##name() { return this->name; } \
void set_##name(type value) { this->name = value; }
// Dartのbuild_runnerによる自動生成
Map _$UserToJson(User instance) => {
'name': instance.name,
'age': instance.age,
};
主な違い:実装メカニズム
C言語のマクロ:テキスト置換
#define PI 3.14159
double area = PI * SQUARE(radius); // テキスト置換される
Dartのbuild_runner:ASTベースのスマート生成
@JsonSerializable()
class User {
final String name;
final int? email;
final DateTime createdAt;
Map _$UserToJson(User instance) => {
'name': instance.name,
'email': instance.email,
'createdAt': instance.createdAt.toIso8601String(),
};
}
タイプ安全性とスコープの問題
C言語のマクロの欠点
#define SWAP(a, b) { typeof(a) temp = a; a = b; b = temp; }
int x = 5;
char* y = "hello";
SWAP(x, y); // 型不一致エラー
Dartのbuild_runnerの利点
class User {
final File file; // シリアル化できない型
}
// build_runnerはここでエラーを報告します。
開発者体験
C言語のマクロ:シームレスな統合
#ifdef DEBUG
#define DEBUG_PRINT(fmt, ...) printf("Debug: " fmt "\n", ##__VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...)
#endif
DEBUG_PRINT("User login: %s", username); // IDEで即座に認識
Dartのbuild_runner:手動操作が必要
dart run build_runner build # コード生成
dart run build_runner watch # ファイル変更監視
dart run build_runner clean # 生成ファイルクリーンアップ
@JsonSerializable()
class User {
final String name;
final int age;
factory User.fromJson(Map json) => _$UserFromJson(json);
Map toJson() => _$UserToJson(this);
}