您的位置:首页 > 其它

函数体中打印日志信息自动附加函数名字的一种实现办法

2012-05-05 21:54 519 查看

一段代码,在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~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: