LoginSignup
34
10

C++ でどうしても Rust みたいに let で変数定義したい人のための記事

Last updated at Posted at 2024-03-04

はじめに

下のコードで letlet mut が使えます。
それぞれ C++ の const autoauto に対応します。

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 に展開したいわけですが、 letconst auto に展開されるので、 mut がそれを打ち消すようなコードに展開される必要があります。
そこで、ダミー変数を作って変数定義を一度終わらせてしまいます。
そのあとに auto を置いておけば、 let mutauto に展開されたかのように動きます。

#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_0rust_like_variable_definition_dummy_1 といった識別子が生成されます。

おまけのおわり

おわりです。

34
10
0

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
34
10