一段代码,在Windows下的VC环境下编译没问题,在Linux的GCC下会报一个错误:
error
:
‘
L__FUNCTION__
’
was not declared in
this
scope
对此的解释:
In GCC 3.3 and earlier, in C only,
__FUNCTION__
and__PRETTY_FUNCTION__
were treated as string literals; they could be used to initialize char arrays, and they could be concatenated with other string literals. GCC 3.4 and later treat them as variables, like__func__
. In C++,__FUNCTION__
and__PRETTY_FUNCTION__
have always been variables.
引发此问题的代码段如下:
1)定义
#define FH(fmt) TEXT(__FUNCTION__)TEXT(" >> ")fmt
#define FHT(fmt) FH(__TEXT(fmt))
2)写日志的函数
void SSLogPrintT(LPCTSTR lpInfo)
{
// Write the <lpInfo> to My Log Pool
_tprintf_s(lpInfo);
}
3)使用场景
void Test_Log_Function()
{
SSLogPrintT(FHT("I'm here!\r\n"));
}
4)预期结果
Test_Log_Function >> I'm here!
5)在Linux的GCC环境下有此定义
#ifdef _UNICODE
typedef wchar_t TCHAR;
#define __T(x) L ## x
#define __TEXT(x) L##x
#else
typedef char TCHAR;
#define __T(x) x
#define __TEXT(x) x
#endif
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
#ifndef TEXT
#define TEXT(x) __TEXT(x)
#endif
typedef TCHAR _TCHAR;
typedef wchar_t WCHAR;
typedef char CHAR;
typedef TCHAR *LPTSTR, *PTCHAR;
typedef CONST TCHAR *LPCTSTR, *PCTSTR;
typedef CHAR *LPSTR, *PSTR;
typedef CONST CHAR *LPCSTR, *PCSTR;
typedef WCHAR *LPWSTR, *PWSTR;
typedef CONST WCHAR *LPCWSTR, *PCWSTR;
#ifndef wprintf_s
#define wprintf_s unix_wprintf
#endif
#ifndef printf_s
#define printf_s printf
#endif
#ifdef _UNICODE
#define _tprintf_s printf_s
#else
#define _tprintf_s sprintf_s
#endif
好了,开始分析问题:
上面一段English总的意思是说对于版本号等于大于3.4的GCC编译器, 它会把定义__FUNCTION__认为是当前函数的窄字符形式的名字,但是L__FUNCTION__编译器并不会认为这是__FUNCTION__的宽字符形式,而认为它是一个名为L__FUNCTION__的变量,而这个变量并没有定义,所以编译器在编译的时候会直接报错.
可是如果我还想用
SSLogPrintT(FHT("I'm here!\r\n"));
的形式来用,怎么办?
可以看到错误主要在FH的定义处,如果能用一种办法直接替换TEXT(__FUNCTION__)为宽字符形式,那么问题也解决了.
方案如下:
在windows平台下,定义MP_WINDOWS宏,仍然用上面的定义,在LINUX下面则用工具类CSSCoreLogUtilFmtWithFUNCNameA /W来辅助生成宽字符串.
修改FH的定义
#if defined(MP_WINDOWS)
#define FH(fmt) TEXT(__FUNCTION__)TEXT(" >> ")fmt
#else
#if defined(_UNICODE)
#define FH(fmt) CSSCoreLogUtilFmtWithFUNCNameW(__FUNCTION__, TEXT(" >> ")fmt)
#else
#define FH(fmt) CSSCoreLogUtilFmtWithFUNCNameA(__FUNCTION__, " >> "fmt)
#endif
#endif
辅助类定义如下:
class CSSCoreLogUtilFmtWithFUNCNameW
{
public:
CSSCoreLogUtilFmtWithFUNCNameW(const char *lpName, const wchar_t *lpFmt);
~CSSCoreLogUtilFmtWithFUNCNameW();
operator wchar_t* ();
protected:
wchar_t *m_pStringW;
};
class CSSCoreLogUtilFmtWithFUNCNameA
{
public:
CSSCoreLogUtilFmtWithFUNCNameA(const char *lpName, const char *lpFmt);
~CSSCoreLogUtilFmtWithFUNCNameA();
operator char* ();
protected:
char *m_pStringA;
};
呵呵,实现不写了,麻烦,大体意思是
类CSSCoreLogUtilFmtWithFUNCNameW在构造函数中接受窄字符的函数名字(lpName)和宽字符的格式化参数(lpFmt),把他们复合成宽字符的数据,放入m_pStringW中,m_pStringW会在重载函数operator wchar_t* ()中返回;
类CSSCoreLogUtilFmtWithFUNCNameA在构造函数中接受窄字符的函数名字(lpName)和窄字符的格式化参数(lpFmt),把他们复合成窄字符的数据,放入m_pStringA中,m_pStringA会在重载函数operator char* ()中返回;
到原理了吧 - 很简单
因为
SSLogPrintT(FHT("I'm here!\r\n"));
这种调用格式,可以看到FHT,FH会在一个()域中,我们可以在()域中定义一个临时类的实例A,它的构造参数接受函数名和格式化参数两个字符串,它可以隐式转换为宽/窄字符的字符串,
函数SSLogPrintT执行体的域(即{})跟此临时类的实例所在的域()是相同的,所以在函数SSLogPrintT的函数实现体内,A还是存在的,我们完全可以利用从A隐性转化来的窄/宽字符.
在SSLogPrintT退出之后,域()消失,临时变量A的析构函数会被调用到,可以再此处释放A动态分配的临时变量(如果有的话).
总结
在VC环境下很简单的东西,在GCC下可能就会出现各种状况. O(∩_∩)O~