6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C++11 で __FILE__, __LINE__ を含んだログ関数を作るメモ

Last updated at Posted at 2020-11-10

背景

  • C++11 で __LINE__, __FILE__ などを含んでいい感じにログ関数を実装したい
  • コンパイラの拡張機能使わずにポータブルにやりたい

問題

__FILE__, __LINE__

__FILE____LINE__ はプリプロセッサで展開されるため, 関数の引数に指定してもうまくいきません.

// does not work as you expected
void log(const char *fname = __FILE__) {
 ...
}

C++20 では std::source_location https://en.cppreference.com/w/cpp/utility/source_location がサポートされますのでこの問題は解決しますが, C++20 が普及するまであと数年(現在 2020 年)はかかるでしょう.

C++11 ~ C++17 では結局のところマクロを介する以外に解決方法はありません.

ちなみに C11 から __func__ で関数が取得できるようになりました.

__VA_ARGS__

C マクロでは __VA_ARGS__ で可変引数を対応することができますが, __VA_ARGS__ に割り当たる引数がゼロ個になるとコンパイルエラーになってしまいます.

#define MYLOG(s, ...) mylog(__FILE__, __LINE__, s, __VA_ARGS__)

MYLOG("val = %d", val); // OK
MYLOG("warning"); // NG. 

後者は展開すると mylog(__FILE, __LINE__, s, ) となり, コンマのあとにかっこがきて構文エラーになっていまいます.

gcc(clang も?)では, ##__VA_ARGS__ という拡張でゼロ個の引数を対応することができますが, コンパイラ依存になってしまいます.

C++ VA_ARGS のメモ
https://qiita.com/syoyo/items/e5e03a52a953371b82c8

これも C++20 であれば __VA_OPTS__ の導入で解決されます.

解法

C++11 で導入されました variadic template を使います.

ゼロ個の __VA_ARGS__ の問題がありますので, もし __VA_ARGS__ が 0 個になる可能性があるのであれば,

にあるようにクラスを定義しオブジェクトを作るとよさそうです!


#include <cstdio>
#include <cstdlib>

class Message
{
public:
  Message(const char *fname, int line)  : fname_(fname), line_(line) {}

  template<typename... Args>
  void write(Args&&... args) {
    int nargs = sizeof...(args);
    printf("fname %s: line %d, # args %d\n", fname_, line_, nargs);
    // fixme: Fully implement `write` function.
  }

private:
  Message() = delete;
  Message(const Message&) = delete;

  const char *const fname_;
  const int line_;
};

#define msg(...) Message(__FILE__, __LINE__).write(__VA_ARGS__);

int main(int argc, char **argv)
{
        msg();
        msg("bora");
}

あとは variadic template をぺろぺろ処理すればいけます!

参考

spdlog では普通の引数形式でした. __VA_ARGS__ が必ず 1 個以上ある前提の場合ですね.

pprintpp ではコンパイル時にフォーマット文字列をテンプレートで処理してコンパイル時型チェックしています! :rolling_eyes:

でも ##__VA_ARGS__ を使っているので MSVC では動きません.

また, 完全に型安全というわけでもなく, std::string など未対応な型の変数を引数に渡すとクラッシュします.

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?