在 C/C++ 裡 debug 的時候,__FILE____LINE__ 很好用,可以幫我們將錯誤發生的地點指出來。可是,因為 __FILE__ 對應的型別是 const char* (__LINE__ 對應的型別是 size_t),在使用 wide character I/O 的時候,便不能用。就算像 L"some string" 一樣,前面加一個 L 也不行,因為這樣子就變成了 L__FILE__,是另外一個 symbol 了。空一格也不行,因為 L"some string"L 必須與引號直接連接。 還好,使用 ## 這個 preprocessor operator 可以解決這個問題:

#define WIDEN_(x) L##x#define WIDEN(x)  WIDEN_(x)#define __WFILE__ WIDEN(__FILE__)

這樣子我們就可以依據 output 的環境,選擇 __FILE____WFILE__ 來使用了:

void fout(ostream& os){    os << __FILE__ << endl;}

void wfout(wostream& os){    os << __WFILE__ << endl;}

int main(){    fout(cout);    wfout(wcout);    return 0;}

可是,如果是在 template class/function 裡面呢?Character/string 的真實型別,由 template parameter 決定,我們根本無從選擇該用 __FILE__ 還是 __WFILE__ 怎麼辦?這時候,如果有個 generic 版本的 __TFILE__,能夠自動選擇要用 __FILE__ 還是 __WFILE__ 的話,那該有多好?可惜 pre-processor 不支援 template resolution,無法做到 generic。

#define __TFILE__(char_type) \        ((sizeof(char_type) == sizeof(char)) ? __FILE__ : __WFILE__)

如上列程式使用 conditional operator 將 __FILE____WFILE__ 的選擇,留待 compile-time 再來解決,是不可行的。原本的想法是,(sizeof(char_type) == sizeof(char))__FILE____WFILE__ 的型別不同 (分別為 const char*const wchar_t*),所以這樣子使用 conditional operator 是非法的。 山不轉路轉,「把 __FILE____WFILE__ 的選擇,留待 compile-time 再來解決」的基本想法應該是對的,既然 conditional operator 不能用,那用 template specialization 如何?如下列程式:

是個 constant expression,所以 compiler 可以 optimize 選擇正確的版本。但因為 C++ standard 5.16.5 說,當 conditional operator 的第二個及第三個 operands 為 r-value 時,兩者的 type 必須匹配,除非可藉由 overload resolution 解決歧義。因為

#include <functional>

template <class CharT>struct tfile_sel    : public ::std::binary_function<const char*, const wchar_t*,                                    const CharT*>{    const CharT* operator () (const char* f, const wchar_t* wf)    {        return 0;    }};

template <>inlineconst char* tfile_sel<char>::operator () (const char* f,                                          const wchar_t* wf){    return f;}

template <>inlineconst wchar_t* tfile_sel<wchar_t>::operator () (const char* f,                                                const wchar_t* wf){    return wf;}

#define __TFILE__(char_type) tfile_sel<char_type>()(__FILE__, __WFILE__)

利用 template specialization 選擇 __FILE____WFILE__,然後因為 template specialization 以及 inline 的關係,最後的 run-time overhead 理論上也應該要蠻小的。就算有,也不大,反正大多在 debug mode 裡用,可以省略這一點點的 overhead。如此,完成的 __TFILE__ 可以這麼樣子用:

using namespace ::std;

template <class CharT>void tfout(basic_ostream<CharT>& os){    os << __TFILE__(CharT) << endl;}

int main(){    tfout(cout);    tfout(wcout);    return 0;}

總整理上面的 code,含測試程式如下:

#include <iostream>#include <string>#include <functional>

#define WIDEN_(x) L##x#define WIDEN(x)  WIDEN_(x)#define __WFILE__ WIDEN(__FILE__)

using namespace ::std;

template <class CharT>struct tfile_sel    : public ::std::binary_function<const char*, const wchar_t*,                                    const CharT*>{    const CharT* operator () (const char* f, const wchar_t* wf)    {        return 0;    }};

template <>inlineconst char*tfile_sel<char>::operator () (const char* f, const wchar_t* wf){    return f;}

template <>inlineconst wchar_t*tfile_sel<wchar_t>::operator () (const char* f, const wchar_t* wf){    return wf;}

#define __TFILE__(char_type) tfile_sel<char_type>()(__FILE__, __WFILE__)

template <class CharT>void tfout(basic_ostream<CharT>& os){    os << __TFILE__(CharT) << endl;}

int main(){    tfout(cout);    tfout(wcout);    return 0;}

以上技巧有待統整的地方很多,好比 WIDEN() 可以提出來變成一個獨立的模組以便再利用 (reuse)。另外,雙底線開頭的識別字,在 C/C++ 裡是保留給 standard 與 compiler vendor 用的,所以最好另外換個名字。最後,應該也可以參考 Boostpreprocessor library,也許可以有其他不一樣的作法也說不定。