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对象将只保留一个全局元信息的指针,这样应该更自然。
(以后遇到的问题只更新到这个文档中)