閱讀Mitsuba的代碼的時候,發現了一個有意思的地方:
#define Log(level, fmt, ...) do { \ mitsuba::Thread *thread = mitsuba::Thread::getThread(); \ if (EXPECT_NOT_TAKEN(thread == NULL)) \ throw std::runtime_error("Null thread pointer"); \ mitsuba::Logger *logger = thread->getLogger(); \ if (logger != NULL && level >= logger->getLogLevel()) \ logger->log(level, m_theClass, \ __FILE__, __LINE__, fmt, ## __VA_ARGS__); \ } while (0)
定義了一個Log的宏函數,使用了do{...} while(0)的語法,這里的while中的條件是常量0,上面的代碼永遠只執行一遍。
感覺是多此一舉,做法令人費解。上stack overflow查了下資料。說法很多,我在下面歸納兩條比較有價值的分析:
1.就是上面的宏定義中,do{}while(0)的意義:
可以先看看Log宏函數如何被使用的:
Log(EInfo, "The time cost by init is %f",Time_stas::init_time);
Log別當作了一個函數來使用,所以,宏定義替換函數后,需要保證語義不會受到影響。
假設這樣的場景:
if( xxxx)
Log(xxx,"xxxxx");
else
xxxx;
如果我們不使用do{}while(0),使用{}把do{}中的語句括住。
上面的語句就成了:
if(xxxx)
{....};
else
就會出現編譯錯誤。
當然,使用
if(xxxx){
Log(xxx,xxx);
}else
{
}
可以避免上面使用{}的問題。但是,do{}while(0)的確為一種穩健的做法。
2.使用do{}while(0),可以使用break語句,從do中跳出,避免goto語句:
int test(int p) { if(p==-1) { ...//do something goto smaecode; } if(p ==0) { ...//do something goto smaecode; } if(p==1) { ...//do something goto smaecode; } samecode: ...//do something return p; }
在do{}while(0)內部使用break語句可以避免使用goto:
int test(int p) { do { if(p == -1) { ..//do something break; } if(p==0) { ..//do something break; } if(p == 1) { ..//do something break; } }while(0); ...//same code return p ; }