C言語:可変長引数のマクロと関数、およびデバッグ用printf

デバッグ用printfでよく使うテクニックだが忘れがちなこと。

可変長引数のマクロ

  • 引数の可変長部分を ... で表記する。
  • 特別な識別子 __VA_ARGS__ が可変長引数に置換される。
#define DEBUG_PRINT(fmt, ...) printf(fmt, __VA_ARGS__)

可変長引数の関数

  • stdarg.h をインクルードする。
  • 引数の可変長部分を ... で表記する。
  • va_list (可変長引数リスト)の変数を定義する。
  • 可変長引数リストを使う範囲を va_start()va_end() で囲む。
  • va_start() の第2引数には、可変長引数の直前の引数を指定する。
    (つまり、可変長引数のみをとる関数は作れない。)
  • ちなみに printf() の可変長引数リスト型版として vprintf() がある。
  • また sprintf() の可変長引数リスト型版として vsprintf() がある。
void debug_print(char *fmt, ...)
{
  va_list args;
  va_start(args , fmt);
  vprintf(fmt, args);
  va_end(args);
}

デバッグ用printf

標準出力が使える環境であれば、可変長引数のマクロのみでOK。

#ifdef USE_DEBUG_PRINT
#define DEBUG_PRINT(fmt, ...) printf(fmt, __VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...) /* 何もしない */
#endif

マイコン等で標準出力が使えない環境であれば、可変長引数の関数で実装する。
Arduino用の例を示す。(ボードによっては Serial.printf() が使える場合もあり。)

Debug.h

#pragma once

#include <stdio.h>
#include <stdarg.h>

#ifdef USE_DEBUG_PRINT
#define DEBUG_PRINT(fmt, ...) debug_print(fmt, __VA_ARGS__)
#else
#define DEBUG_PRINT(fmt, ...) 
#endif

void debug_print(char *fmt, ...);

Debug.cpp

#include <Arduino.h>
#include "Debug.h"

void debug_print(char *fmt, ...)
{
  static char buff[256]; // staticだとスレッドセーフでないことに注意
  
  va_list args;
  va_start(args , fmt);
  vsprintf(buff, fmt, args);
  va_end(args);

  Serial.print(buff);
}

使い方

#define USE_DEBUG_PRINT
#include "Debug.h"
   DEBUG_PRINT("hoge = %d\n", hoge);