qiezi的学习园地

AS/C/C++/D/Java/JS/Python/Ruby

  C++博客 :: 首页 :: 新随笔 ::  ::  :: 管理 ::
asgard项目已经准备了一段时间了,不过有些基本问题还需要考虑,也有一些是新发现的问题,以及自认为比较好的解决办法。

通过第2、第4条的仔细研究,已经渐渐完善、明确了动态部分和静态部分的关系,使得Method包装类所完成的功能渐渐接近于一个函数,而元信息则脱离具体的对象提升到全局(当然还有些小问题没有解决)。

1、参数名称的问题。

为了与SOAP等基于XML的协议兼容,必须开始就把参数名称考虑在内。

代码经过C++编译器编译以后,类型、变量名称等都不复存在,唯一留下的是RTTI,显然不能解决这个问题。所以只能在定义时把它加入。

BEGIN_SERVICE(TestService)
    METHOD (
void(in<int>, inout<string>out<short>), method1, index, info, result);
END_SERVICE()

如果使用这种方式,index, info, result分别表示变量名字,在宏里面转成字符串,看起来好像不太舒服,而且宏不支持参数个数变化。

BEGIN_SERVICE(TestService)
    METHOD (
void(in<int>, inout<string>out<short>), method1, "(index, info, result)");
    METHOD (
int(in<int>, inout<string>), method2, "result(index, info)");
END_SERVICE()

这种可能稍稍舒服一点,在Method构造函数或其它地方解析这个字符串,赋给各个参数。不过它的缺点是把编译期应该检查出来的错误,延迟到运行期。如果在编译期来做,又会使接口描述变得很复杂。

只是为了得到参数的名字,就要增加这么些麻烦。

c++0x只是一个库的标准,估计XTI也不会加入这些特性,而且c++0x很遥远,所以暂时以这种方式来做。

暂时的解决办法:

BEGIN_SERVICE(TestService)
    METHOD (
void(in<int>, inout<string>out<short>), method1);
    METHOD (
int(in<int>, inout<string>), method2);
    BEGIN_SERVICE_DEFINE(TestService)
        METHOD_DEFINE (method1, 
"(index, info, result)", test_func);
        METHOD_DEFINE (method2, 
"result(index, info)"&Test::test_method);
    END_SERVICE_DEFINE()
END_SERVICE()

缺点是参数名称中的错误,要延迟到运行期才能解决掉。

2、服务对象的大小。

如果客户端要调用其中一个方法,生成一个TestService,则构造成本太高,特别是一个服务中有多个方法的时候。一个服务容纳了多个方法,而每个方法包含一个vector,以及各个参数,这还没考虑以后的扩展。

所以应该修改调用方式,让它只只需要生成调用所需的最小(少)对象。

这部分考虑还不成熟,暂时可以不管它,而以方法作为考虑的对象。

暂时想到的解决办法:

Method对象中的parameters容器和各个参数,只在调用operator ()或async_call时,才真正生成出来。

这样的话,Method对象中仅保存一个空的vector。

甚至这个vector也可以只是一个空指针,当调用那几个函数时,才生成一个。

暂时把这个过程命名为Create On Call(COC)。

COC的好处是显而易见的,每个对象将只有8字节,虚表指针+数据对象的指针,“数据对象”是实际调用时才生成的对象,包括参数vector容器、回调函数指针(可能由动态生成一个委托对象,以适应广泛类型的回调函数)、对象锁(防止干扰到前一个调用)。初始化成本接近0(虚函数表的初始化忽略不计)。

当调用operator()或async_call时(以下简称CALL),将调用create_parameters虚函数,动态生成一个vector。这样,没有调用到的Method不会象原来一样影响到服务对象的构建性能。

这就要求把Method的“元”信息提到全局,当然更符合“元”的本意,原来由服务对象查询Method以获得“元”信息的过程,现在看来也是不合理的。

3、in模板可以省略。

in是默认的参数类型,返回值则默认为out类型,这都是不需要明确指定的。

解决办法:

这个问题是比较好解决的,在InOutTypeTraits模板类中,为各个偏特化版本定义一个type类型,InOutTypeTraits<T>::type的类型为in<T>,InOutTypes<in<T>>::type的类型为in<T>,InOutTypes<inout<T>>::type的类型为inout<T>,InOutTypes<out<T>>::type的类型为out<T>,InList模板类中进行这种转换。

4、异步调用队列。

在第2点中介绍道:

每个对象将只有8字节,虚表指针+数据对象的指针,“数据对象”是实际调用时才生成的对象,包括参数vector容器、回调函数指针(可能由动态生成一个委托对象,以适应广泛类型的回调函数)、对象锁(防止干扰到前一个调用)。初始化成本接近0(虚函数表的初始化忽略不计)。

提到了对象锁,这是一种低效的做法,可以使用异步调用队列来替代它。

解决办法:

当开始一个调用时,临时生成上面所说的“数据对象”,交由一个调用队列去完成。这时,由于Method对象基本不管理数据,所以它成了一个空壳,作用是保存类型信息。

异步调用最好的实现就是整个系统都由异步调用构成,而同步调用是由异步调用模拟而成。原本打算绕过这种方式,用最简单的方法来做,现在好像又绕回来了。

上面这个做法,很好地把元信息和真实数据分开了,所以打算改成这种结构。

5、全局元信息。

通过第4条的研究,已经使得Method对象成为一个空壳,而“数据对象”在没有调用时又不生成,使得自省结构必须重新做。

考察了java等语言的自省,也打算把元信息的位置提升到全局,而每个Method对象将只保留一个全局元信息的指针,这样应该更自然。


(以后遇到的问题只更新到这个文档中)
posted on 2005-09-24 17:00 qiezi 阅读(395) 评论(0)  编辑 收藏 引用 所属分类: C++asgard项目