`
jnn
  • 浏览: 283526 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ACE是Service Config框架的实现分析

阅读更多

在开发和维护的过程中,很多时候会涉及一个问题,就是如何实现派生类的动态加载。由于C++本身不支持动态类的加载机制,需要通过配置文件描述服务信息并采用动态库来加载。因此在ACE中专门设计了service config框架来解决这样的问题。 

这篇文章主要讲述了在ACE中如果通过宏定义的方式实现服务的动态加载与配置。

 

在开发和维护的过程中,很多时候会涉及一个问题,就是如何实现派生类的动态加载。由于C++本身不支持动态类的加载机制,需要通过其他的方式来进行支持。因此在ACE中专门设计了service config框架来解决这样的问题。

service config框架,通过修改配置文件,能够实现对派生于ACE_Service_Object的服务对象的加载,在service config中根据加载服务代码方式的不同可以分为:

静态服务
  是被静态链接至应用程序的服务
动态服务
  是从一个和多个共享库中链接的服务

由于本文讨论的重点时动态类的加载机制,因此主要讨论service config中有关动态服务的实现,其他的实现内容,大家可以参考 C++NPV2中的第5章。

在这里主要是从原理方面,简单分析service config框架的实现。
在分析service config框架之前,需要明确service config需要实现的功能:
1.我们写的新的服务需要在运行时(不是在编译时)能够被service config框架识别以及调用。
2.其次是服务对象的生命周期由service config框架进行管理。
3.通过配置文件,来获知服务对象加载信息。

对于前面提到的两点功能,只需要我们写的服务类继承ACE_Service_Object ,实现这个类所提供的init(),fini(),suspend(),resume(),info()对服务管理的方法。由于ACE_Service_Object继承了ACE_Event_Handler,因此可以实现相关服务处理的方法。

对与第三点来说,如果是用Java语言来实现service config框架,那也很容易,可以通过动态类型识别(RTTI),在语言运行库中,就提过了描述Class信息的机制,使用著名的Class.forName(),可以获得需要加载类的主要信息。

而对于C++来说,这就比较麻烦了,主要是需要解决有关类信息描述的问题。对于DLL来说,如果直接将对象进行输出的话,由于C++所支持的重载机制,输出的类方法名会结合参数产生变化,如果想通过service config框架,加载DLL输出的类,就必须通过静态编译的方法,获知类的定义。而我们的要求是在程序运行时来,通过配置文件中定义的dll名,以及相关的类名来加载相关的服务类。

通过分析源码,我们可以发现一些有意思的东东。

# define ACE_Local_Service_Export

# define ACE_FACTORY_DEFINE(CLS,SERVICE_CLASS)
void _gobble_##SERVICE_CLASS (void *p) {
  ACE_Service_Object *_p = ACE_static_cast (ACE_Service_Object *, p);
  ACE_ASSERT (_p != 0);
  delete _p; }
extern "C" CLS##_Export ACE_Service_Object *
_make_##SERVICE_CLASS (ACE_Service_Object_Exterminator *gobbler)
{
  ACE_TRACE (#SERVICE_CLASS);
  if (gobbler != 0)
    *gobbler = (ACE_Service_Object_Exterminator) _gobble_##SERVICE_CLASS;
  return new SERVICE_CLASS;
}

/// The canonical name for a service factory method
#define ACE_SVC_NAME(SERVICE_CLASS) _make_##SERVICE_CLASS

/// The canonical way to invoke (i.e. construct) a service factory
/// method.
#define ACE_SVC_INVOKE(SERVICE_CLASS) _make_##SERVICE_CLASS (0)
//@}

/** @name Helper macros for services defined in the netsvcs library.
 *
 * The ACE services defined in netsvcs use this helper macros for
 * simplicity.
 *
 */
//@{
# define ACE_SVC_FACTORY_DECLARE(X) ACE_FACTORY_DECLARE (ACE_Svc, X)
# define ACE_SVC_FACTORY_DEFINE(X) ACE_FACTORY_DEFINE (ACE_Svc, X)
//@}

ACE_SVC_FACTORY_DECLARE (...)

在这里用...来代替具体的类名
_make_... (ACE_Service_Object_Exterminator *gobbler) 是dll输出的调用接口(注意是C方式进行输出的,如果是C++的话就另当别论了),我们需要在svc.conf中写出输出的调用函数名。

_make... 这个宏精妙之处就在于此,由于C++不支持reflect机制,为了能够让Service_Config类
在不做任何改变的情况下,就能创建相关的ACE_Service_Object 继承类的实例,_make提供了一个
创建类实例的方法。 这也许是Factory模式的另类应用,其实这个方法在某些环境还是很有效,
也可以帮助我们将C++的类对象输出至delphi中,供delphi进行调用。

return new SERVICE_CLASS;

这样就有人会问了,提供new,如何提供delete呢?
大家再看一下_gobble_...这个宏定义,
从 ACE_Service_Object_Exterminator *gobbler 的定义
typedef void (*ACE_Service_Object_Exterminator)(void *);
可知 ACE_Service_Object_Exterminator 是一个函数指针而gobbler定义是指向这个函数指针的指针。

通过给gobbler赋值,就可以给service config框架提供释放的ACE_Service_Object派生类的方法了。

还有为什么在_gobble_...函数中需要ACE_Service_Object *_p = ACE_static_cast (ACE_Service_Object *, p); 来一个强制转换呢?


强制转换是因为 _gobble_##SERVICE_CLASS (void *p)中的p是无类型指针,强制转换成ACE_Service_Object * 类型,delete *p 时,就可以调用p所指向的ACE_Service_Object子类的析构函数。

svc.conf 示例文件
dynamic Timer_Service_3 Service_Object *
./Timer:_make_Timer_Service_3()
"timer $INTERVAL $MAX_TIMEOUTS $TRACE"
里面没有有关_gobble_##SERVICE_CLASS 函数的描述,
这是因为_make_##SERVICE_CLASS 的宏定义中已经完成有关析构函数的注册工作了。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics