« LPCXpressoでCMSIS Peripheral Libraryを使う | トップページ | LPCXpresso LPC1769にMARY拡張基板を乗せる »

Debug printf用の可変長引数マクロ

Cプログラマな方には既知だと思いますが、printfデバッグに便利なマクロ定義を発見したので備忘録も兼ねてポストします。


Printfデバッグとは

プログラムのデバッグのために、printf関数を使用して変数の内部状態をコンソールに表示したい場合があります。JTAGデバッグが使えないmbedやArduinoでは必須のテクニックです。今更言うまでもないですね。


デバック用マクロ

デバックが終了してプログラムをリリースする段階に進むと、デバック用のprintfは無効にしたいものです(printfは結構ROM/RAMを消費するなどMCU資源の無駄使いするため)。リリース時にいちちprintfをコメントアウトするのは面倒なので、Cのマクロを使って、デバック用のprintfを一括して無効化できると便利です。

Arduinoでは以下のマクロを定義していました。

#define DEBUG

#ifdef DEBUG
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_PRINTLN(x)
#endif
  • プログラム中でDEBUG_PRINTLN(hoge)と書くと、4行目のマクロで Serial.println(hoge)に変換されます。
  • 1行目をコメントアウトすると、DEBUG_PRINTLN(hoge)は空行に変換され、コンソール出力が無効になります。

Arduinoのように、print関数の引数が固定個数の場合は、この方法でもよいのですが、mbedやLPCXpressoのようにフルセットのprintfが使える場合、引数の数が可変長になるためうまくいきません。

で、マクロで可変長の引数を扱うための機能がないかと調べると、すぐに出てくるんですね。便利な時代だ。


可変長引数マクロ

__VA_ARGS__というパラメータを使うと、可変長引数をマクロ内で展開してくれます。mbedでの使い方はこんな感じです。

#include "mbed.h"

#define DEBUG

// Debug Macro
#ifdef DEBUG
#define DBG(fmt, ...) printf(fmt, __VA_ARGS__)
#else
#define DBG(...)
#endif

int i = 0;
int j = 1000;

int main() {
    while(1) {
        i++;
        j--;
        printf("Loop: %d\n", i);
        DBG("Debug Print: i=%d j=%d\n", i, j);
        // DBG("Degub");       // Cause compiler error
        wait(1.0);
    }
    
    return 0;
}

7行目の、...が可変長引数を示し、 __VA_ARGS__で実際の引数に展開します。
そのため、20行目のマクロは;
  printf("Debug Print: i=%d j=%d\n", i, j);
に展開されます。

この方法では、引数を1つ以上指定しないと、マクロの展開後カンマが1つ残ってしまいコンパイルエラーになります。そのため、21行目のような使い方はできません。


LPCXpressoでは

LPCXpressoのIDEでは、Debugビルドを指定した場合、コンパイラの-DオプションにDEBUGが追加されるため、プログラム中で#define DEBUGを定義する必要はありません。

Releaseビルドに切り替えた場合、-DオプションからDEBUGが自動的に外れます。


おわりに

Cのマクロはコードの可読性が下がるので(特にネストしたマクロの場合)嫌いだったのですが、少し見直しました。ただ、引数をつけなかった場合のエラーの発生原因が直感的に分からないのはマクロの欠点ではあります。


参考資料:

« LPCXpressoでCMSIS Peripheral Libraryを使う | トップページ | LPCXpresso LPC1769にMARY拡張基板を乗せる »

mbed」カテゴリの記事

NXP-ARM」カテゴリの記事

コメント

こんにちは
マクロの可変長引数機能はC99規格での拡張みたいですね。MS系のCコンパイラだとまだサポートされていないようでした。
C99より前は括弧を2重にするマクロを書いていました。
#define DBG(x) printf x
でDBG((fmt, a, b, c))はprintf (fmt, a, b, c)のように展開されることを利用したものです。
こんなやり方もあるということで^^

Simさんコメントありがとうございます。
なるほどこういうやり方もあるのですね。この方法だと、引数を取らない場合でもエラーにならない利点がありそう。(*^^)

こゆことじゃ?
#ifdef DEBUG
#define DBG(...) printf(__VA_ARGS__)
#else
#define DBG(...)

この記事へのコメントは終了しました。

« LPCXpressoでCMSIS Peripheral Libraryを使う | トップページ | LPCXpresso LPC1769にMARY拡張基板を乗せる »

2018年10月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ