从ACE_TRACE到ACE_TMAIN

本文详细分析了ACE日志宏ACE_TRACE未输出日志的原因,并揭示了ACE_TRACE宏的工作原理。通过检查ACE_OS_Object_Manager的初始化状态,发现未调用ACE::init()导致跟踪日志无法输出。解决方法是在使用ACE_TRACE宏前调用ACE::Init()函数,确保ACE库正确初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

我们知道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 (),而省去手工调用的麻烦。

 

 

 

 

 

 

 

 

<think>我们正在处理一个关于ACE框架(Adaptive Communication Environment)的问题,特别是关于如何声明和实现一个虚函数,该函数接受一个ACE_Reactor指针作为参数。根据用户提供的引用,我们可以得到一些关键信息: 1. 引用[1]展示了虚函数的声明方式,如`virtual ACE_HANDLE get_handle (void) const;`和`virtual void set_handle (ACE_HANDLE);`。 2. 引用[2]提到了ACE的初始化和main函数的写法。 3. 引用[3]提到了ACE_Acceptor和ACE_Reactor的协同使用。 4. 引用[4]强调了Reactor框架中事件处理器(Event_Handler)的资源释放问题,并提到在handle_close中完成资源释放。 用户意图是声明或实现一个接收ACE_Reactor指针的虚函数,即`virtual void reactor(ACE_Reactor* reactor);`。 在ACE框架中,事件处理器(ACE_Event_Handler)类通常会有一些虚函数,其中就包括`reactor`函数,用于设置或获取关联的Reactor实例。根据ACE的常见用法,我们通常会在事件处理器类中声明这样的函数。 ### 声明虚函数 在头文件中,我们通常在类声明中声明虚函数。例如: ```cpp class MyEventHandler : public ACE_Event_Handler { public: // 其他成员函数... // 设置关联的Reactor virtual void reactor (ACE_Reactor *reactor); // 获取关联的Reactor virtual ACE_Reactor *reactor (void) const; // ... 其他虚函数,如handle_close, handle_input等 }; ``` ### 实现虚函数 在源文件中,我们实现这两个函数: ```cpp // 设置关联的Reactor void MyEventHandler::reactor (ACE_Reactor *reactor) { // 通常,我们只需要将传入的reactor指针存储到成员变量中 this->reactor_ = reactor; } // 获取关联的Reactor ACE_Reactor *MyEventHandler::reactor (void) const { return this->reactor_; } ``` 注意:在ACE_Event_Handler基类中,实际上已经提供了`reactor`函数的默认实现,并且有一个成员变量`reactor_`。因此,如果我们只是简单地设置和获取,可能不需要重写这两个函数。但是,如果我们需要特殊的行为,则可以重写。 ### 关于引用[4]的注意事项 引用[4]提到Reactor框架在删除事件处理器时会调用`handle_close`函数,因此我们应在`handle_close`中释放资源(如关闭Socket、释放内存等)。但是,我们这里讨论的`reactor`函数并不直接涉及资源释放,而是设置和获取Reactor指针。 ### 总结 根据用户需求,声明和实现`virtual void reactor(ACE_Reactor* reactor)`的步骤如下: 1. 在事件处理器类的头文件中声明该虚函数。 2. 在源文件中实现该函数,通常是将传入的指针存储到成员变量中。 注意:在ACE中,通常我们使用基类ACE_Event_Handler中已经存在的`reactor`函数,所以如果不需要特殊行为,则不需要重写。但根据用户要求,我们给出了声明和实现的示例。 ### 相关问题 1. 在ACE框架中,事件处理器(Event_Handler)通常需要重写哪些虚函数? 2. 如何将事件处理器注册到Reactor并处理事件? 3. 在ACE中,Reactor模式的工作机制是怎样的?</think>### ACE框架中虚函数 `reactor` 的声明与实现 在ACE框架中,`virtual void reactor(ACE_Reactor* reactor)` 是用于设置事件处理器关联的Reactor对象的虚函数。以下是声明和实现的规范方式: #### **1. 声明(头文件中)** ```cpp #include <ace/Event_Handler.h> #include <ace/Reactor.h> class MyEventHandler : public ACE_Event_Handler { public: // 声明设置Reactor的虚函数 virtual void reactor(ACE_Reactor* reactor); // 其他必要虚函数(如handle_close) virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE, ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK); }; ``` #### **2. 实现(源文件中)** ```cpp void MyEventHandler::reactor(ACE_Reactor* reactor) { // 关键步骤:调用基类方法设置Reactor ACE_Event_Handler::reactor(reactor); // 可选:自定义逻辑(如日志记录) ACE_DEBUG((LM_INFO, "Reactor set to %@\n", reactor)); } int MyEventHandler::handle_close(ACE_HANDLE handle, ACE_Reactor_Mask mask) { // 必须实现:资源清理(引用[4]要求) if (handle != ACE_INVALID_HANDLE) ACE_OS::closesocket(handle); // 删除处理器(根据内存管理策略) delete this; return 0; } ``` ### **关键说明** 1. **基类调用** 必须通过 `ACE_Event_Handler::reactor(reactor)` 设置内部Reactor指针,否则会破坏ACE的事件调度机制[^3][^4]。 2. **资源管理** - Reactor框架通过 `handle_close` 释放资源,而非直接删除处理器[^4]。 - 在 `handle_close` 中关闭Socket/释放内存是标准做法[^4]。 3. **初始化要求** ACE必须初始化后才能使用Reactor(引用[2]): ```cpp int ACE_TMAIN(int argc, ACE_TCHAR* argv[]) { ACE::init(); // 关键初始化 ACE_Reactor reactor; MyEventHandler handler; handler.reactor(&reactor); // 设置Reactor reactor.run_reactor_event_loop(); ACE::fini(); } ``` ### **相关问题** 1. 如何正确继承 `ACE_Event_Handler` 以实现自定义事件处理逻辑? 2. ACE框架中 `handle_close` 和 `handle_input` 等虚函数的作用和调用时机是什么? 3. Reactor模式在ACE中如何实现多路复用和事件分发? 4. 如何避免ACE事件处理器中的内存泄漏?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值