【C/C++】いまさらだけど文字列化演算子(#)とトークン連結演算子(##)について理解する
C/C++のマクロには、文字列化演算子(#)とトークン連結演算子(##)という機能があります。これらを使うと、より強力で便利なマクロを書くことができます。恥ずかしながら、この機能について理解が薄かったので、調べてまとめてみました。
文字列化演算子
文字列化演算子(#)は、引数を取る関数型のマクロで使用できます。仮引数に#
をつけることで、引数自体を文字列リテラルに変換します。
使い方は以下のとおりです。
#define STRINGIFY(str) #str
int main()
{
char a[] = STRINGIFY(test); // char a[] = "test";
char b[] = STRINGIFY(a); // char b[] = "a";
return 0;
}
文字列化演算子を使うと、引数自体にダブルクオーテーションを付けて文字列化します。2つ目の例のように、変数を指定しても、変数名が文字列になるだけで変数の中身は参照されません。
これだけだと、なかなか使い道がありませんが、たとえば次のような使い方ができます。
#include <iostream>
#define PRINT_VALUE(value) std::cerr << #value " = " << value << std::endl
int main()
{
int a = 10;
char b[] = "This is a pen.";
PRINT_VALUE(a);
PRINT_VALUE(b);
return 0;
}
出力結果は以下のようになります。
a = 10
b = This is a pen.
マクロの展開時に、最初のvalue
は文字列化演算子によって変数名が文字列リテラルに変換され、さらに" = "
と結合して1個の文字列リテラルとなります。2つ目のvalue
には文字列化演算子がついていないので、そのまま変数名として展開されます。
この例ように、デバッグ用の文字列出力などで便利に使うことができます。
トークン連結演算子
トークン連結演算子(##)は、オブジェクト型のマクロと関数型のマクロの両方で使用できます。##
の前後のトークンを連結し、1個のトークンに置き換えます。関数型のマクロの場合、仮引数と組み合わせると、引数を含む新たなトークンを作り出すことができます。
使い方は以下のとおりです。
#define CONCATENATE(i) test##i
int main()
{
int CONCATENATE(1) = 1; // int test1 = 1;
int CONCATENATE(2) = 10; // int test2 = 10;
return 0;
}
引数をtest
に連結した新たなトークンを生成します。引数がそのまま連結されるので、この例のように数値を指定することもできます。
これだけだと、正直なところ「コピペでよくないか?」となり、使い道がよくわかりません。しかし、もっと複雑なマクロを定義するときに、トークン連結演算子は役に立ちます。
#define DEFINE_LIST_CONTAINER(struct_name) \
typedef struct struct_name##_list\
{\
struct struct_name##_list * prev;\
struct struct_name##_list * next;\
struct_name * data;\
} struct_name##List;
typedef struct product
{
char * name;
unsigned int id;
unsigned int price;
} Product;
typedef struct person
{
char * name;
unsigned int age;
} Person;
DEFINE_LIST_CONTAINER(Product)
DEFINE_LIST_CONTAINER(Person)
マクロの展開時に、引数のProduct
やPerson
がList
と連結され、ProductList
やPersonList
となり、新たな構造体が定義されます。
このようにテンプレート的に使うことができます。
ディスカッション
ピンバック & トラックバック一覧
[…] […]