作曲・指導・C言語・Linux

金沢音楽制作

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

__func__で関数名にアクセスする

C99に、関数名にアクセスするための識別子__func__が追加されました。デバッグやログをとる時に使います。なお、__func__はマクロではなく、const char型で定義されていますから、関数の引数にする場合は、型も合わせなければいけません。

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

関数名を表示する

__fucn__を使って関数名を表示させてみます。

#include <stdio.h>

static void doNothing(void);
static int addInt(int x, int y);

int main(void)
{
  printf("%s: %s()\n", __LINE__, __func__);

  doNothing();

  int ans = addInt(2, 3);

  return 0;
}

static void doNothing(void)
{
  printf("%d: %s()\n", __LINE__, __func__);
}

static int addInt(int x, int y)
{
  printf("%d: %s()\n", __LINE__, __func__);
  return x + y;
}
$ ./a.out
8: main()
19: doNothing()
24: addInt()

__func__が呼ばれた行とそこの関数名が表示されました。

__func__の正体

__func__の正体は、関数内で宣言された静的なchar型の定数です(4.10 事前定義済み __func__ シンボルの関数名としての使用)。

コンパイルできませんが、次のようなイメージです。

static void usage(void) {
  static const char __func__[] = "usage";
}

int main(void)
{
  static const char __func__[] = "main";

  return 0;
}

__func__の正体が、char型の定数配列だということが分かりました。ということは、関数の引数に渡したいときは、hoge(const char *str);と、const修飾子を忘れずにつけます。

#include <stdio.h>

static void showFuncName(const char *p);

int main(void)
{
  showFuncName(__func__);

  return 0;
}

static void showFuncName(const char *p)
{
  puts(__func__);
}

関数化して実用的に

原始的なcatコマンドを書いてみました。fopen()で失敗してNULLが返ってくると、その行番号と関数名が表示されます。普段は不要ですから-DEBUGオプションをつけました。

#include <stdio.h>
#include <stdbool.h>

static void printNullError(int line, const char *func);
static bool openFile(FILE **fpp, char *fileName);
static void showTextFile(FILE *fp);
static void closeFile(FILE **fpp);

int main(int argc, char *argv[])
{
  for (int i = 1; i < argc; i++) {
    FILE *fp;
    if(!openFile(&fp, argv[i])) {
      continue;
    }
    showTextFile(fp);
    closeFile(&fp);
  }

  return 0;
}

static void printNullError(int line, const char *func)
{
  fprintf(stderr, "%d: %s(): NULL pointer exception\n",
      line, func);
}

static bool openFile(FILE **fpp, char *fileName)
{
  *fpp = fopen(fileName, "r");
  if (*fpp == NULL) {
  fprintf(stderr, "%s: No such file or directory\n", fileName);

#ifdef DEBUG
printNullError(__LINE__, __func__);
#endif

    return false;
  }

  return true;
}

static void showTextFile(FILE *fp)
{
  const int Max = 8;
  char buf[Max];

  while (fgets(buf,Max,fp) != NULL) {
    fputs(buf, stdout);
  }
}

static void closeFile(FILE **fpp)
{
  fclose(*fpp);
}

openFile()の引数に存在しないファイルを指定すると、NULLが返ってくるため、行と関数名が表示されます。

$ echo '0123456789abcdefghijk' > hoge.txt
$ ./a.out hoge.txt fuga.txt
0123456789abcdefghijk
fuga.txt: No such file or directory
33: openFile(): NULL pointer exception

更新情報