Apr 27, 2006

恐るべし関数オブジェクト

ポスト @ 23:38:23 | プログラミング,C++,BREW,ATL/WTL

本日のお題は、BREWのログ取りです。

BREWには DBGPRINTF() という、シリアルにログを吐くマクロがありますが、これが今3くらい信頼性に欠けるものでして、しっかりログを取るためにはやはりファイルにダンプできることも必要になってきます。

そこで私に与えられた任務:リリースビルドではきれいさっぱり消滅し、それ以外は条件コンパイルでシリアルログ・ファイルログを切り替えられる CLogger を作れ。

そしてこれを実装しようとしてすぐ直面するのが、「マクロで可変長引数を実現する」手法です。

「リリースビルドで消滅」は、、マクロ定義を切り分けて、その場合に ';' のみに置換してしまえば問題ありません。デバッグログなので、__FILE__ と __LINE__ を出力に埋め込むのは必須ですが、これもマクロの中に埋め込めばよいわけです。

そしてここではたと困るのが、DBGPRINTF() ってくらいなんだから可変長引数にしなくちゃいけないんだけど、マクロにそんな機能なんかないよ、というところなのです。

ちなみにBREWのマクロは、おおむねこんな感じ。

#define __DBGPRINTF (隠しオブジェクト->関数ポインタ)
#define DBGPRINTF __DBGPRINTF("各種情報 %s:%d", __FILE__, __LINE__) __DBGPRINTF

なるほど、これはお上手。引数つきマクロにしないで定数マクロにしてうまく逃げてます。
そしてこれだと2回、出力することになりますが、なんかよくわからないけど BREW のシリアルログは受信ツール側でこれをうまく切り分けてくれます(なんのこっちゃ、こういうひどいわけわからんのがBREW多すぎ、苦笑)。

…でも、これ、絶対に使えないのです。
BREWアプレットは原則として「グローバル変数不可」。ファイルログを採ろうとする場合、ファイル書き込みのために必ず IShell のポインタが必要です。しかしそれをグローバルに参照することはできず、もし作ろうとしたら、ログを採ろうとする箇所のクラスすべてで、IShellを保存しなくてはいけなくなるのです。
それだと、気軽にデバッグ出力コードを書くことが著しく困難になってしまいます。

(で、グローバル変数の持ちまわしテクニックは後日別に投稿しますので今回は省略)

しかし、世の中には大変えらいひとがいるものです。
「困ったときはパクれ」。さっそく、ATL7のソースを除いてみたりしたのですが、答えがあっさり出てました。

概略はこうです。

class CLogger
{
private:
  const char* file;
  int line;
public:
  CLogger(const char* file_, int line_) : file(file_), line(line_) {}
  void operator()(const char* format, ...)
  {
    va_list ap;
    va_start(ap, format);
    char buf[128];
    SVNPRINTF(buf, sizeof (buf), format, ap);
    va_end(ap);
    DBGPRINTF(buf);
  }
};

#define DBGOUT CLogger(__FILE__, __LINE__)

もうこれ考えた人、というか、関数オブジェクトなんて代物を考えた人はネ申、と声を出して叫んでしまいましたよ今日の職場で私は。笑

Trackback

No Trackbacks

Track from Your Website

http://blog.izumichan.com/trackback/tb.php?id=220

Comment

No Comments

Post Your Comment


*は入力必須です。E-Mailは公開されません。