はじめに
下のコードで let
と let mut
が使えます。
それぞれ C++ の const auto
と auto
に対応します。
namespace rust_like_variable_definition {
struct dummy_for_mut {};
}
#define rust_like_variable_definition_dummy_impl2(counter) rust_like_variable_definition_dummy_##counter
#define rust_like_variable_definition_dummy_impl(counter) rust_like_variable_definition_dummy_impl2(counter)
#define rust_like_variable_definition_dummy() rust_like_variable_definition_dummy_impl(__COUNTER__)
#define let const auto
#define mut rust_like_variable_definition_dummy() [[maybe_unused]] = rust_like_variable_definition::dummy_for_mut{}; auto
int main() {
let x = 1;
// x = 2; // error!
let mut y = 3;
y = 4; // ok
}
おわりに
おわりです。
おまけ
仕組みを知りたい人のために軽く説明します。
int main() {
let x = 1;
let mut y = 3;
}
このコードはマクロを展開すると以下のようになります。
int main() {
const auto x = 1;
const auto rust_like_variable_definition_dummy_0 [[maybe_unused]] = rust_like_variable_definition::dummy_for_mut{}; auto y = 3;
}
let
はそのまま const auto
に展開されるだけです。
問題は let mut
で、これを auto
に展開したいわけですが、 let
が const auto
に展開されるので、 mut
がそれを打ち消すようなコードに展開される必要があります。
そこで、ダミー変数を作って変数定義を一度終わらせてしまいます。
そのあとに auto
を置いておけば、 let mut
が auto
に展開されたかのように動きます。
#define mut rust_like_variable_definition_dummy() [[maybe_unused]] = rust_like_variable_definition::dummy_for_mut{}; auto
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// この変数定義を終わらせに来た!!!(ドン!!)
ダミー変数の変数名には一意じゃないと、スコープごとに1回しか mut
を使えないことになるので、困りますね。
そこで、 __COUNTER__
マクロを使って一意な識別子を生成します。
__COUNTER__
マクロは数値に展開され、その数値は展開されるたびにインクリメントされます。それを、ほかの識別子と衝突しないように長いプレフィックスと繋げて、変数名として使います。
参考: __COUNTER__マクロ - yohhoyの日記
#define rust_like_variable_definition_dummy_impl2(counter) rust_like_variable_definition_dummy_##counter
#define rust_like_variable_definition_dummy_impl(counter) rust_like_variable_definition_dummy_impl2(counter)
#define rust_like_variable_definition_dummy() rust_like_variable_definition_dummy_impl(__COUNTER__)
マクロ rust_like_variable_definition_dummy()
がプレフィックス rust_like_variable_definition_dummy_
と __COUNTER__
の値を結合した識別子を返すのですが、なんか冗長に書いていますね。
これは展開された __COUNTER__
とプレフィックスを結合するためです。
ここまでしないと __COUNTER__
が展開されず rust_like_variable_definition_dummy___COUNTER__
になってしまい、連続するアンダースコアを含むため、晴れて未定義動作です。
一度関数型マクロの引数に __COUNTER__
渡すことで __COUNTER__
が展開され、 rust_like_variable_definition_dummy_0
や rust_like_variable_definition_dummy_1
といった識別子が生成されます。
おまけのおわり
おわりです。