これはマクロで省略記号を使用できますか?テンプレートに変換できますか?
-
03-07-2019 - |
質問
まともなロギングを行うためにCLogClassを実装し、マクロも定義しましたが、1つのパラメーターでのみ機能します...
class CLogClass
{
public:
static void DoLog(LPCTSTR sMessage, ...);
};
#define DebugLog(sMessage, x) ClogClass::DoLog(__FILE__, __LINE__, sMessage, x)
まあ、3つ以上のパラメーターを指定して呼び出すと失敗します:( ...それを回避することはまったく可能ですか?どういうわけかテンプレートに変換できますか?
編集:可変長マクロはVS 2005で導入されました(しかし、私は現在VS 2003にいます...)。何かアドバイスはありますか?
解決
可変数の引数を取るカスタムファンクターオブジェクトを返すMYLOGマクロを使用できます。
#include <string>
#include <cstdarg>
struct CLogObject {
void operator()( const char* pFormat, ... ) const {
printf( "[%s:%d] ", filename.c_str(), linenumber );
va_list args;
va_start( args, pFormat );
vfprintf( stderr, pFormat, args );
va_end( args );
}
CLogObject( std::string filename, const int linenumber )
: filename( filename ), linenumber( linenumber )
{}
std::string filename;
int linenumber;
};
#define MYLOG CLogObject( __FILE__, __LINE__ )
int _tmain(int argc, _TCHAR* argv[])
{
MYLOG( "%s, %d", "string", 5 );
return 0;
}
この回答:operator<<
。
struct CTSLogObject {
template< typename T >
std::ostream& operator<<( const T& t ) const {
return std::cout << "[" << filename << ":" << linenumber << "] ";
}
CTSLogObject( std::string filename, const int linenumber )
: filename( filename ), linenumber( linenumber )
{}
std::string filename;
int linenumber;
};
#define typesafelog CTSLogObject( __FILE__, __LINE__ )
int _tmain(int argc, _TCHAR* argv[])
{
typesafelog << "typesafe" << ", " << 5 << std::endl;
return 0;
}
他のヒント
あなたの質問は、実際には2つの答えに訴えます。 printfのように機能しますが、完全にカスタマイズできるユニバーサルロギング機能を実行します。必要なもの:
- 可変数の引数を取るマクロ
- 可変数の引数を取る関数
ここにコード例を示します:
#include <stdio.h>
#include <stdarg.h>
class CLogClass
{
public:
static void DoLogWithFileLineInfo( const char * fmt, ... )
{
va_list ap;
va_start( ap, fmt );
vfprintf( stderr, fmt, ap );
va_end( ap );
}
};
#define MYLOG(format, ...) CLogClass::DoLogWithFileLineInfo("%s:%d " format , __FILE__, __LINE__, __VA_ARGS__)
int main()
{
MYLOG("Hello world!\n", 3); // you need at least format + one argument to your macro
MYLOG("%s\n", "Hello world!");
MYLOG("%s %d\n", "Hello world!", 3);
}
可変長マクロはC99で導入されたため、C99またはC ++ 0xをサポートするコンパイラで動作します。 gcc 3.4.2およびVisual Studio 2005で正常にテストしました。
関数の可変引数は永遠に存在するため、ここでは互換性について心配する必要はありません。
テンプレートのメタプログラミングを使用してそれを行うことはおそらく可能ですが、上記のコードの単純さを考えると、それが興味を引くとは思いません。
最後のメモとして、なぜ関数の代わりに空のクラスで静的メソッドを使用するのですか?
class Log {
stringstream buffer;
public:
class Proxy {
public:
Proxy(Log& p) : parent(p) {}
template<class T>
Proxy& operator,(T const& t) {
parent.buffer << t;
return *this;
}
~Proxy() {
parent.buffer << endl;
cout << parent.buffer.str();
parent.buffer.str("");
}
private:
CLog& parent;
};
template<class T>
Proxy operator<<(T const& t) {
buffer << t;
return Proxy(*this);
}
};
タイムスタンプの書き込み、ログレベルの確認、ファイルへの書き込みなどに簡単に拡張できます。
または、よりシンプルだが柔軟性が低い:
class Log {
public:
class Proxy {
public:
template<class T>
Proxy& operator,(T const& t) {
cout << t;
return *this;
}
~Proxy() {
cout << endl;
}
};
template<class T>
Proxy operator<<(T const& t) {
cout << t;
return Proxy();
}
};
使用法:
Log log;
void f() {
log << "hey, my age is ", age;
}
このインスタンスではマクロではなく、グローバルに表示可能なextern関数を使用し、va_listを使用してこの関数の省略記号を解決する傾向があります。 これを実現する方法の例については、以前の投稿を参照してください 。