作曲・指導・C言語・Linux

金沢音楽制作

金沢音楽制作では、楽曲・楽譜の制作と、作曲や写譜などレッスンを行っています。

型総称マクロ(_Generic)

C11から、ジェネリックが使えるようになりました。ジェネリックは、「型」を式にして、switch文のような振る舞いをします。これを総称選択といい、型による処理のリストを総称関連といいます(62頁)。ジェネリックは、「ジェネリックス」「型総称」「総称型」などとも呼ばれています。

『Cクイックリファレンス』265頁

GetType(x)マクロは、引数に渡された値の型を表示します。総称関連にない型、たとえばunsignedlongなど、マッチしない型はdefault:が呼ばれます。

#include <stdio.h>

#define GetType(x) _Generic((x), \
  int:     "int", \
  double:  "double", \
  char:    "char", \
  char*:   "string", \
  void*:   "void*", \
  default: "unknown" \
  )

int main(void)
{
  int    d = 10;
  double f = 1.41421356237;
  char   c = 'A';
  char  *s = "helo, world";
  void *vp = &d;
  long  ld = 1234567890;

  printf("Type: %s\n", GetType(d));
  printf("Type: %s\n", GetType(f));
  printf("Type: %s\n", GetType(c));
  printf("Type: %s\n", GetType(s));
  printf("Type: %s\n", GetType(vp));
  printf("Type: %s\n", GetType(ld));

  return 0;
}
$ ./a.out
Type: int
Type: double
Type: char
Type: string
Type: void*
Type: unknown

それぞれの型が表示されましたが、long型は総称関連にないので、defaultが実行されます。

もう少し役に立つプログラムを考えてみます。入力された値を型に合わせて二乗するプログラムです。GetSquared(x)マクロは、総称選択でint型ならgetSquaredInt()関数を、double型ならgetSquaredReal()関数が呼ばれます。

#include <stdio.h>

#define GetSquared(x) _Generic((x), \
  int: getSquaredInt, \
  double: getSquaredReal \
  )(x)

static int getSquaredInt(int n);
static double getSquaredReal(double n);

int main(void)
{
  int n = 97;
  printf("%d^2 = %d\n", n, GetSquared(n));
  
  double r = 3.14159;
  printf("%.2f^2 = %.2f\n", r, GetSquared(r));

  return 0;
}

static int getSquaredInt(int n)
{
  return n * n;
}

static double getSquaredReal(double n)
{
  return n * n;
}
$ ./a.out
97^2 = 9409
3.141590^2 = 9.869588

Cには継承がないので、型の数だけ関数を書くことになります。簡単な処理であれば、マクロだけで完結してもいいかも知れません。

#define GetSquared(x) _Generic((x), \
  int: x*x, \
  double: x*x \
  )(x)

更新情報