转自:http://blog.csdn.net/htf15/article/details/8270191
引言
我们知道ACE日志宏是否产生日志方法调用,由三个配置在编译时的值决定:ACE_NTRACE、ACE_NDEBUG,以及ACE_NLOGGING。要启用相应的日志宏,需要定义相应的宏。ACE_NTRACE默认为1(禁用),ACE_NDEBUG和ACE_NLOGGING默认为未定义(启用)。
通过在包含头文件“Log_Msg.h”头文件前定义ACE_NTRACE为0可以启用ACE_TRACE日志宏。但在下面的简单代码示例中,并不能成功输出跟踪日志:
#include "stdafx.h"
#define ACE_NTRACE 0
#include "ace/Log_Msg.h"
void fun (){
ACE_TRACE ("fun ()");
}
int _tmain(int argc, _TCHAR* argv[])
{
ACE_TRACE ("main ()");
fun ();
return 0;
}
注:以上代码在VisualStudio 2005中构建工程。其中头文件stdafx.h由编译器自动生成并添加引用。
本文将一步步查找ACE_TRACE失灵的原因。启用ACE_TRACE本身不值得一提,倒是分析查找的过程有些值得参考的价值,特写出来和大家分享。
ACE_TRACE日志宏原理
ACE_TRACE日志宏的功能
语法:ACE_TRACE(string)
功能:在ACE_TRACE出现之处显示文件名、行号以及string。在退出作用域时显示“Leaving ‘string’”。根据ACE_TRACE日志宏的功能可知,该宏非常适合用来跟踪包含复杂逻辑或嵌套层次很深或分支比较多的函数调用栈。
我们先来看一下,前面的示例代码如果跟踪日志正常输出,应该输出什么:
(6128) calling main () in file`t_ACE_TRACE.cpp' on line 10
(6128) calling fun () in file `t_ACE_TRACE.cpp' on line 5
(6128) leaving fun ()
(6128) leaving main ()
其中括号中的值为线程ID。
ACE_TRACE日志宏的工作原理
ACE_TRACE是一个宏,定义如下:
# define ACE_TRACE(X) ACE_TRACE_IMPL(X)
其中,ACE_TRACE_IMPL也是一个宏,定义如下:
# define ACE_TRACE_IMPL(X) ACE_Trace ____(ACE_TEXT (X), __LINE__, ACE_TEXT (__FILE__))
其中ACE_Trace是一个类,在其构造函数中输出“calling”日志信息,在其析构函数中输出“leaving”日志信息。在输出时由ACE_Trace类静态变量维护缩进信息。
注:通过上面的分析,我们知道在一个名字作用域中,不能使用2个或以上的ACE_TRACE宏,否则会报“____”重定义错误
ACE_TRACE日志宏为什么没有输出?
引言中的代码为什么没有预期的日志输出?我们看一下ACE_Trace类的构造函数定义:
ACE_Trace::ACE_Trace (const ACE_TCHAR *n,
int line,
const ACE_TCHAR *file) {
this->name_ = n;
// If ACE has not yet beeninitialized, don't try to trace... there's
// too much stuff not yetinitialized.
if (ACE_Trace::enable_tracing_ &&
!ACE_OS_Object_Manager::starting_up ()) {
ACE_Log_Msg *lm = ACE_LOG_MSG;
if (lm->tracing_enabled ()
&&lm->trace_active () == 0) {
lm->trace_active (1);
ACE_DEBUG ((LM_TRACE,
ACE_TEXT ("%*s(%t) calling %s infile `%s' on line %d\n"),
ACE_Trace::nesting_indent_ *lm->inc (),
ACE_TEXT (""),
this->name_,
file,
line));
lm->trace_active (0);
}
}
}
ACE_Trace类使用ACE_DEBUG宏,以日志严重级别LM_TRACE输出跟踪信息。ACE_DEBUG宏由ACE_NLOGGING控制是否启用,默认情况ACE_DEBUG是启用的。对于日志严重级别,默认情况下启动所有级别。因此,不输出跟踪日志的原因一定是上面代码中的if条件不满足。
下面我们来看if中的两个条件。
ACE_Trace::enable_tracing_
enable_tracing为ACE_Trace类中的静态变量:
staticbool enable_tracing_;
通过调用ACE_Trace::is_tracing()可以获取该值,调试发现该值为true。
注:该值默认为true。通过调用ACE_Trace::start_tracing()设置该值为true,通过调用ACE_Trace::stop_tracing()设置该值为false。
!ACE_OS_Object_Manager::starting_up ()
下面来看一下ACE_OS_Object_Manager::starting_up()函数的定义:
ACE_OS_Object_Manager::shutting_down (void)
{
return ACE_OS_Object_Manager::instance_
? instance_->shutting_down_i ()
: 1;
}
通过调试发现ACE_OS_Object_Manager::instance_值为NULL,所以该NULL值就是造成日志不输出的原因。
这是一个ACE_OS_Object_Manager单例(Singleton)指针。通过查看ACE的源码,发现这个指针或者在ACE_OS_Object_Manager构造函数中初始化,或者在ACE_OS_Object_Manager::instance()调用时初始化。
因此为了保证正常的跟踪日志输出,必须有一个地方实例化一个ACE_OS_Object_Manager单体实例。再次查看ACE源码,继续跟踪下去,发现如下的函数调用序列:
Int ACE::init(void)
| | Init_ACE.cpp
| |
V
ACE_Object_Manager::instance()->init ()
| |Object_Manager.cpp
| |
V
ACE_OS_Object_Manager::instance()
到这里我们就发现了了ACE_TRACE不输出日志的原因了,是因为没有调用ACE::init() 进行初始化。这恰好就是ACE_Trace类的构造函数的注释:
// If ACE has not yet beeninitialized, don't try to trace... there's
// too much stuff not yetinitialized.
ACE_TMAIN宏
看来我们已经找到了输出跟踪日志的方法,只需在面函数调用ACE_TRACE前先调用ACE::Init ()函数。但仔细想想我们在使用ACE库时似乎从没有主动调用过这个函数,也没有哪本参考书中说要这样做。
的确,这个函数不需要人工调用,调用它是ACE库的职责。一切还得从ACE_TMAIN说起(当然你使用main也没什么问题,只是我个人喜欢用ACE_TMAIN作为主函数入口)。
ACE_TMAIN定义
我们来看ACE_TMAIN定义:
# define ACE_TMAIN main
原来就是main啊。别急,这个不是我们常见的main,它依然是一个宏定义:
# define main \
ace_main_i (int,char *[]); \
ACE_BEGIN_VERSIONED_NAMESPACE_DECL \
ACE_Export intace_os_main_i (ACE_Main_Base&, int, char *[]); \
class ACE_Main : public ACE_Main_Base {int run_i (int, char*[]);}; \
inline int ACE_Main::run_i (int argc, char *argv[]) \
{ \
return ace_main_i (argc, argv); \
} \
ACE_END_VERSIONED_NAMESPACE_DECL \
int \
ACE_MAIN (intargc, char *argv[]) /* user's entry point, e.g., wmain */ \
{ \
ACE_Main m; \
return m.run (argc, argv); /*ace_os_main_i (m, argc,argv); what the user calls"main" */ \
} \
int \
ace_main_i
下面来解释一下这个宏:
这个宏首先前置声明了两个函数
int ace_main_i (int, char *[]);
int ace_os_main_i (ACE_Main_Base&, int, char*[]);
注:这里忽略了ACE_Export关键字,实际上是跟操作系统相关符号导入导出声明,这里可暂时先理解为前置声明,不影响对main宏的理解。
接下来的几行定义了一个类ACE_Main,这个类继承自ACE_Main_Base,并实现了虚函数int run_i (int, char *[]),这个函数在继承来的ACE_Main_Base::run()函数中被调用。接下来的int ACE_MAIN函数才是我们常见的main函数,ACE_MAIN是一个宏,被定义为main。在ACE_MAIN中我们声明了一个ACE_Main的实例,调用这个对象的run函数。而我们的ACE_TMAIN函数被定义为一个名为ace_main_i的函数,这个函数在ACE_Main::run_i中被调用。总结一些我们的函数调用序列:
ACE_MAIN是我们的程序入口
| | ACE_MAIN是我们的程序入口
| |
V
ACE_Main_Base::run
| |
| |
V
ACE_Main::run_i
| |
| |
V
int ace_main_i (int, char *[])
| | 这就是我们写的ACE_TMAIN主函数
| |
V
从ACE_TMAIN到ACE_TRACE
接下来我们看一下ACE_Main_Base的构造函数:
ACE_Main_Base::ACE_Main_Base ()
{
ACE::init ();
}
一切真相大白。之所以使用ACE_TMAIN,只不过是让ACE自己帮我们调用ACE::init (),而省去手工调用的麻烦。
#define ACE_NTRACE 0