標籤:

C++中能顯式定義一個匿名變數嗎?

我想以RAII的方式來log某個域的運行時候的信息(類似boost::progress_timer,做的事情多一點)。使用方式可能是在域開始的時候定義一個並不會用到的對象,類似於:

ProgressLog logger(...);

我把這個語句寫成一個宏,大概像

#define LOG() ProgressLog logger(...)

但是問題是logger會污染那個域的變數名。。。我只能取一個很2的名字,類似於_DONT_USE_THIS_AS_VAR_NAME這樣的。。。

能不能定義顯式定義一個匿名的變數,只是利用它的析構函數....


我應該來推銷一下自己的 proposal 嗎?算了,還是不挖坑了。。。

類似前面某個回答,如果不在意多一層 scope 的話,這個問題可以有不用宏的選項:

ProgressLog(...), []
{
// your scope to be logged
}();

解釋見 @冒泡 的回答,只是這裡用了 lambda, 可以跨編譯器使用。


fuck_the_logger ## __LINE__ ## reggol_eht_kcuf

至少每一行的變數名都不一樣。


#define CONCAT_(s1, s2) s1##s2
#define CONCAT(s1, s2) CONCAT_(s1, s2)

#ifdef __COUNTER__
# define ANONYMOUS_VARIABLE CONCAT(var_, __COUNTER__)
#else
# define ANONYMOUS_VARIABLE CONCAT(var_, __LINE__)
#endif

優先使用大部分編譯器都支持的擴展__COUNTER__,每次預處理展開時自動+1的宏

如果不支持,那再用__LINE__


我認為應該直接每行構造一個便宜的logger,用完即棄,打十行log就造十個logger

#define LOG() Logger(__FILE__,__LINE__)

使用方法LOG().info("foo")

那麼這行log打完以後自動析構,完成格式化操作,或刷新緩衝,或發送到logserver,或發送到logging thread。

如果是複雜字元串可以採用builder模式,構造完消息以後統一輸出。

缺點在於有時候loglevel較低時也意外求值構造了字元串,此時可以用lambda來wrap一下(類似於log4j),或者在LOG宏裡面判定一下。


沒有匿名變數,也用不到,作用域里的局部變數就不用擔心污染名字了,反正作用域都是你的,你自己也知道那個scope guard變種該怎麼用。怕同事不容易看懂就起個名字叫xxx_guard之類,會用RAII的弟兄應該就懂了。真有人還不懂加註釋或者當面科普(手動滑稽


gcc有個擴展語法,可以讓你利用逗號表達式實現:

Logger(), ({ /*在這裡寫你的代碼*/ ; (void)0; });

簡單說就是({})括起來的block整個作為一個表達式,中間可以像寫其他block一樣寫各種語句,只是最後一句必須是表達式,作為整個block表達式的值,然後利用逗號表達式將你的匿名臨時對象和整個block表達式組合,你的匿名對象就在整個逗號表達式結束才析構了


可以參考劉未鵬大神的這篇文章:C++11(及現代C++風格)和快速迭代式開發 - 劉未鵬|C++的羅浮宮 - CSDN博客

class ScopeGuard
{
public:
explicit ScopeGuard(std::function& onExitScope)
: onExitScope_(onExitScope), dismissed_(false)
{ }

~ScopeGuard()
{
if(!dismissed_)
{
onExitScope_();
}
}

void Dismiss()
{
dismissed_ = true;
}

private:
std::function& onExitScope_;
bool dismissed_;

private: // noncopyable
ScopeGuard(ScopeGuard const);
ScopeGuard operator=(ScopeGuard const);
};

#define SCOPEGUARD_LINENAME_CAT(name, line) name##line
#define SCOPEGUARD_LINENAME(name, line) SCOPEGUARD_LINENAME_CAT(name, line)

#define ON_SCOPE_EXIT(callback) ScopeGuard SCOPEGUARD_LINENAME(EXIT, __LINE__)(callback)


雙下劃線開頭,或 單下劃線加大寫字母開頭 的標識名都是保留的,用戶不應該用到,即使用到,也是由他自己去保證不衝突。

所以隨便取個名字,比如 __logger,就可以了。


malloc,不保存返回指針,保證匿名得下一秒就不見


推薦閱讀:

C++遊戲開發擇業前景?
WPF是可行的C++程序GUI解決方案么?
C++序列化json字元串對Unicode有哪些特殊處理?
最近在Oulu進行的關於c++17標準的會議有什麼進展?
如何理解《Effective C++》第31條將文件間的編譯依賴關係降低的方法?

TAG:C |