??xml version="1.0" encoding="utf-8" standalone="yes"?>
在“asgard目遗留问题”中Q我单提C几个问题Qƈ且想了一些解x案?BR>
其中Q最首要解决的是W?条“服务对象的大小”和W?条“全局元信息”,q?条将影响到调用机制、call对象的生成。一个调用将生成一个call对象Q由U程池来处理Q同步调用将由异步调用来模拟?BR>
在call对象中,保存了所有in/out参数的包装对象。当处理同步调用Ӟ׃out参数可能是一个栈上对象(或简单类型,q里l称对象Q,所以需要另一个包装类——outret模板c,它保存out参数的引用?BR>
当同步调用发生时Q生成一个call对象Q当然out参数的引用已l包含在里面Q,把这个call对象交给U程池处理,调用的线E阻塞等待调用结束后被唤醒,q就是所谓的异步调用模拟同步调用。由于异步调用被包装h了,所以在调用者看来跟同步调用没什么区别。当然这个动作ƈ非必要,完全可以不用模拟,而采用真正的同步调用Q只是看到ICE是这么实现的Q心痒痒而已?BR>
asgard的目标是把现有的pȝ功能包装成ؓ服务Q所以在通用斚w我考虑得比较多?BR>
比如服务端要开放下面这样一个服务:
{
Method <string(inout<string>, in<string>)> strcat;
Method <string(inout<string>, in<string>, in<uint>)> strncat;
};
q且把C标准库中的strcat和strncat作ؓq?个方法的实现?BR>
看一下存在哪些问题?
1、函数第1个参数如果直接映ؓstringQ在服务端将出现~冲区溢出?BR>
2、C标准库中的strcatq回值是一个指针,它指向strcat的第一个参敎ͼ搞这个标准库的h是不是没惌q个q回值多么没用啊Q?直接q回一个操作的长度不是更好Q)Q在服务端发回客LӞq个q不需要被发回来,因ؓstrcat的第1个参数已l能带回操作后的内容了?BR>
3、strncat的第3个参数表C第1个缓冲区参数的长度,如果能把它和W?个参数合h用一个buffer对象表示Q就能省事了?BR>
理想情况下,我们的服务对象这h写:
{
Method <void(inout< buffer<char> >, in<string>)> strcat;
Method <void(inout< buffer<char> >, in<string>)> strncat;
};
我们的目的是把老的代码包装成新鲜时髦的服务Q当然不用保留老式代码中的指针Q以及用指针和长度2个值来表示一个缓冲区的做法。buffercd构造时要接受一个size_t参数Q指定缓冲区的大?BR>
q在服务端将产生映射问题Q由于这个Method定义的Ş式和C标准库中的函数Ş式不一致?BR>
我想应该dC个适配器模板类Q比如:
this->strncat.setFunction (adapter<char*(char*, const char*, size_t), convert<void, 0>(inout< buffer<char> >, in<string>, length<in<uint>, 1>)> (::strncat));
convert<void, 0>表示把第0个参敎ͼq里指返回|转成voidcdQlength<in<uint>, 1>表示q个参数cd是int<uint>Q它是从W?个参C提取的长度,大致是使用q种规则Q语法可能以后会有变动?BR>
q点内容是我几个月前在考虑的,也是我想做这个项目的动机Q不q直到最q一D|间才从可行性方面仔l考虑?BR>
通过前面几个模板的练习,现在已经大致知道哪些东西是可以用模板做出来,哪些不能使用模板Q这应该是最大的收获了。很多东西单靠模板或是虚函数都不好完成,但结合v来就能生意想不到的效果?BR>
又仔l想了一下,上面的代码应该还可以修改化:
this->strncat.setFunction (adapter<convert<void, 0>(inout< buffer<char> >, in<string>, length<in<uint>, 1>)> ( ) (::strncat));
使用一个仿函数来做Q函数指针的cd可以从operator ()的参敎ͼ模板参数Q中推导出来?img src ="http://www.shnenglu.com/cpunion/aggbug/481.html" width = "1" height = "1" />
]]>
通过W?、第4条的仔细研究Q已l渐渐完善、明了动态部分和静态部分的关系Q得Method包装cL完成的功能渐渐接q于一个函敎ͼ而元信息则脱d体的对象提升到全局Q当然还有些问题没有解冻I?BR>
1、参数名U的问题?/STRONG>
Z与SOAP{基于XML的协议兼容,必须开始就把参数名U考虑在内?BR>
代码l过CQ+~译器编译以后,cd、变量名U等都不复存在,唯一留下的是RTTIQ显然不能解册个问题。所以只能在定义时把它加入?BR>
METHOD (void(in<int>, inout<string>, out<short>), method1, index, info, result);
END_SERVICE()
如果使用q种方式Qindex, info, result分别表示变量名字Q在宏里面{成字W串Q看h好像不太舒服Q而且宏不支持参数个数变化?BR>
METHOD (void(in<int>, inout<string>, out<short>), method1, "(index, info, result)");
METHOD (int(in<int>, inout<string>), method2, "result(index, info)");
END_SERVICE()
q种可能E稍舒服一点,在Method构造函数或其它地方解析q个字符Ԍ赋给各个参数。不q它的缺Ҏ把编译期应该查出来的错误Qgq到q行期。如果在~译期来做,又会使接口描q变得很复杂?BR>
只是Z得到参数的名字,p增加q么些麻烦?BR>
c++0x只是一个库的标准,估计XTI也不会加入这些特性,而且c++0x很遥q,所以暂时以q种方式来做?BR>
暂时的解军_法:
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()
~点是参数名UC的错误,要gq到q行期才能解x?BR>
2、服务对象的大小?BR>
如果客户端要调用其中一个方法,生成一个TestServiceQ则构造成本太高,特别是一个服务中有多个方法的时候。一个服务容U了多个ҎQ而每个方法包含一个vectorQ以及各个参敎ͼq还没考虑以后的扩展?BR>
所以应该修改调用方式,让它只只需要生成调用所需的最()对象?BR>
q部分考虑q不成熟Q暂时可以不它Q而以Ҏ作ؓ考虑的对象?BR>
暂时惛_的解军_法:
Method对象中的parameters容器和各个参敎ͼ只在调用operator ()或async_callӞ才真正生成出来?BR>
q样的话QMethod对象中仅保存一个空的vector?BR>
甚至q个vector也可以只是一个空指针Q当调用那几个函数时Q才生成一个?BR>
暂时把这个过E命名ؓCreate On Call(COC)?BR>
COC的好处是显而易见的Q每个对象将只有8字节Q虚表指针+数据对象的指针,“数据对象”是实际调用时才生成的对象,包括参数vector容器、回调函数指针(可能由动态生成一个委托对象,以适应q泛cd的回调函敎ͼ、对象锁Q防止干扰到前一个调用)。初始化成本接近0Q虚函数表的初始化忽略不计)?BR>
当调用operator()或async_callӞ以下UCALLQ,调用create_parameters虚函敎ͼ动态生成一个vector。这P没有调用到的Method不会象原来一样媄响到服务对象的构建性能?BR>
q就要求把Method的“元”信息提到全局Q当然更W合“元”的本意Q原来由服务对象查询Method以获得“元”信息的q程Q现在看来也是不合理的?BR>
3、in模板可以省略?/STRONG>
in是默认的参数cdQ返回值则默认为outcdQ这都是不需要明指定的?BR>
解决办法Q?BR>
q个问题是比较好解决的,在InOutTypeTraits模板cMQؓ各个偏特化版本定义一个typecdQInOutTypeTraitsQTQ?:type的类型ؓinQTQ,InOutTypesQinQTQ>::type的类型ؓinQTQ,InOutTypesQinoutQTQ>::type的类型ؓinoutQTQ,InOutTypesQoutQTQ>::type的类型ؓoutQTQ,InList模板cMq行q种转换?BR>
4、异步调用队列?/STRONG>
在第2点中介绍道:
每个对象只?字节Q虚表指针+数据对象的指针,“数据对象”是实际调用时才生成的对象,包括参数vector容器、回调函数指针(可能由动态生成一个委托对象,以适应q泛cd的回调函敎ͼ、对象锁Q防止干扰到前一个调用)。初始化成本接近0Q虚函数表的初始化忽略不计)?BR>
提到了对象锁Q这是一U低效的做法Q可以用异步调用队列来替代它?BR>
解决办法Q?/STRONG>
当开始一个调用时Q时生成上面所说的“数据对象”,交由一个调用队列去完成。这Ӟ׃Method对象基本不管理数据,所以它成了一个空I作用是保存类型信息?BR>
异步调用最好的实现是整个pȝ都由异步调用构成Q而同步调用是由异步调用模拟而成。原本打绕q这U方式,用最单的Ҏ来做Q现在好像又l回来了?BR>
上面q个做法Q很好地把元信息和真实数据分开了,所以打改成这U结构?BR>
5、全局元信息?BR>
通过W?条的研究Q已l得Method对象成ؓ一个空I而“数据对象”在没有调用时又不生成,使得自省l构必须重新做?BR>
考察了java{语a的自省,也打把元信息的位置提升到全局Q而每个Method对象只保留一个全局元信息的指针Q这样应该更自然?BR>
Q以后遇到的问题只更新到q个文中)
]]>
async_call函数的原型是Q?BR>
void async_call (int v0, char v1, string v2, FUNC_TYPE func);
q是模板cL据in/out来生的?BR>
在异步调用中Q参数是和操作保存在一LQ因交给U程处理。前面已l说q,Method模板cM保有这些参敎ͼMethod的定义如下(以例子中4个参数的特化版本来说明)Q?BR>
struct Method <void(A,B,C,D)> : public Base < typename Loki::TL::MakeTypelist< A,B,C,D >::Result >
{
A a;
B b;
C c;
D d;
Method ()
{
parameters.push_back (&a);
parameters.push_back (&b);
parameters.push_back (&c);
parameters.push_back (&d);
}
};
相应圎ͼBasecM用这个特化版本:
struct Base <TYPES, IN_TYPES, 3> : public IMethod
{
typedef typename FuncTypeTraits <TYPES>::Result FUNC_TYPE;
void async_call (
typename Loki::TL::TypeAt <IN_TYPES, 0>::Result::OriginalType v0,
typename Loki::TL::TypeAt <IN_TYPES, 1>::Result::OriginalType v1,
typename Loki::TL::TypeAt <IN_TYPES, 2>::Result::OriginalType v2,
FUNC_TYPE func = 0)
{
}
};
TYPES模板参数中保存了所有的参数cdQIN_TYPES模板参数中保存了所有的in参数cdQ但它们不知道如何来对应hQasync_call也不知道如何把几个参数glparametersQ在IMethod中定义,见上一)?BR>
如果我们在生成IN_TYPES的时候,把它在TYPES中的位置Q烦引)也一起交l它Q就能解册个问题?BR>
InListW二个模板参数是一个常量,当我们把TYPES交给它时Q以上面的代码ؓ例,会使用T_COUNTgؓ4的偏特化版本。这时候,会首先推导出IN_TYPES中的W一个类型intQ它在IN_TYPES中的索引?Qƈ接着调用T_COUNT值是3的偏特化版本Q这样递归推导Q直到调用T_COUNTgؓ0的偏特化版本Q这个过E就l束了。在q个递归q程中,能够得到各个cd以及对应的“烦引”|int: 4, char: 3, string: 2?BR>
注意q个索引值和实际的烦引值是有差别的Q实际的索引值应该是4-T_COUNTQ所以上面的对应关系应该是:int: 0, char: 1, string: 2?BR>
最初传递给InList的TYPES?个元素,当它递归调用Ӟq个值就会依ơ递减Q后面的递归调用q不知道应该?d掉T_COUNT作ؓ索引Q因?q没有传递过来。简单的解决办法是再加上一个模板参敎ͼ让它往下传递,当然q种方式q不好看Q好在我们不是真的必这么做?BR>
注意Q在BasecMQ它是知道TYPES的个数的Q那么只要用q个数减d面生成的IN_TYPE的“烦引”,p得到q个cd在TYPES中的真正索引。(q部分真是有点罗嗦)
修改InList模板c,让它生成?FONT style="BACKGROUND-COLOR: #a9a9a9" color=#a9a9a9> [ incd以及incd在TYPES中的“烦引”] 构成的新Typelist?BR>
首先要增加一个辅助模板类Q?BR>
struct TypeReversedIndex
{
typedef T type;
enum {value = INDEX};
};
它能够保存一个类型,以及一个整数。取名ؓTypeReversedIndexQ意思是说它要保存一个Type和一个ReversedIndexQ反的烦引)?BR>
InList模板cM要修改,OutList依旧是免费赠送:
struct InList
{
typedef typename If <
InOutTypeTraits <typename T::Head>::isin,
typename Loki::Typelist < TypeReversedIndex <typename T::Head, T_COUNT>, typename InList <typename T::Tail>::Result >,
typename InList <typename T::Tail>::Result
>::Result Result;
};
template <class T>
struct InList < T, 0 >
{
typedef typename Loki::TL::MakeTypelist <>::Result Result;
};
template < class T, int T_COUNT = Loki::TL::Length <T>::value >
struct OutList
{
typedef typename If <
InOutTypeTraits <typename T::Head>::isout,
typename Loki::Typelist < TypeReversedIndex <typename T::Head, T_COUNT>, typename OutList <typename T::Tail>::Result >,
typename OutList <typename T::Tail>::Result
>::Result Result;
};
template <class T>
struct OutList < T, 0 >
{
typedef typename Loki::TL::MakeTypelist <>::Result Result;
};
Basecd可以写出来了Q?BR>
struct Base <TYPES, IN_TYPES, 3> : public IMethod
{
typedef typename FuncTypeTraits <TYPES>::Result FUNC_TYPE;
typedef IN_TYPES type;
enum {TYPES_COUNT = typename Loki::TL::Length<TYPES>::value};
void async_call (
typename Loki::TL::TypeAt <IN_TYPES, 0>::Result::type::OriginalType v0,
typename Loki::TL::TypeAt <IN_TYPES, 1>::Result::type::OriginalType v1,
typename Loki::TL::TypeAt <IN_TYPES, 2>::Result::type::OriginalType v2,
FUNC_TYPE func = 0)
{
((typename Loki::TL::TypeAt <IN_TYPES, 0>::Result::type*)
parameters[TYPES_COUNT - typename Loki::TL::TypeAt <IN_TYPES, 0>::Result::value])->setValue (v0);
((typename Loki::TL::TypeAt <IN_TYPES, 1>::Result::type*)
parameters[TYPES_COUNT - typename Loki::TL::TypeAt <IN_TYPES, 1>::Result::value])->setValue (v1);
((typename Loki::TL::TypeAt <IN_TYPES, 2>::Result::type*)
parameters[TYPES_COUNT - typename Loki::TL::TypeAt <IN_TYPES, 2>::Result::value])->setValue (v2);
}
};
parameters中存攄是IParameter*cdQ这里用了强制转型Qƈ调用in/inout模板cȝsetValueҎl它赋倹{?BR>
Z试l果Q我为IParameter加上了void print () const虚函敎ͼq在in/inout/out模板cM实现它,打印出类型,in/inoutcMq将打印出参数倹{?BR>
{
public:
virtual void print () const = 0;
};
struct in : public IParameter
{
typedef T OriginalType;
T value;
void setValue (T v){
value = v;
}
void print () const {
cout << typeid(*this).name() << ": " << value << endl;
}
};
template <class T>
struct out : public IParameter
{
typedef T OriginalType;
virtual void print () const {
cout << typeid(*this).name() << endl;
}
};
template <class T>
struct inout : public IParameter
{
typedef T OriginalType;
T value;
void setValue (T v){
value = v;
}
virtual void print () const {
cout << typeid(*this).name() << ": " << value << endl;
}
};
q在Base::async_call中调用parameters中所有对象的print函数来输Z些调试信息:
parameters[i]->print ();
单测试了2U类型,不能保证所有代码都是正的Q毕竟是手工写出来的也没l过查,模板cd没有实例化的时候某些错误是不会报告的?BR>
试代码如下Q?BR>
{
cout << "===========================================" << endl;
cout << "test_func(" << v0 << ", " << v1 << ", " << v2 << ", " << v3 << ")" << endl;
}
void test_func1 (int v0, char v1, short v2, string v3)
{
cout << "===========================================" << endl;
cout << "test_func1(" << v0 << ", " << v1 << ", " << v2 << ", " << v3 << ")" << endl;
}
int main()
{
{
Method < void(in<int>, in<char>, inout<string>, out<short>) > m;
m.async_call(3, 'a', "test");
cout << "===========================================" << endl;
m.async_call(3, 'a', "test", test_func);
cout << "===========================================" << endl;
}
{
Method <string(in<int>, out<char>, inout<short>)> m;
m.async_call(3, 4);
cout << "===========================================" << endl;
m.async_call(3, 4, test_func1);
cout << "===========================================" << endl;
}
return 0;
}
全部代码太长Q就不一一|列于此了,可以点击q里下蝲?BR>
]]>
]]>
?ؕ七八p地讲了一些,有一个遗留问题,函数原型的推对{?BR>
要描q如下:
要生这3U函数Ş式。参数类型如何{换,是以后的话题Q本主要解军_步调用的函数原Ş推导问题。本也不讨论Method的模板参敎ͼ即那个函数类型)q回cd不ؓvoid的情c?BR>
W一UŞ式,同步调用Q比较好处理Q参C数和模板参数的数量相同?BR>
?UŞ式,如何让编译器Ҏin/out来推导出函数原型Q?BR>
我们需要编译器做这L处理Qasync_call的参数类型中Qincd的参数将保留Qoutcd的参C需要,inoutcd也需要保留?BR>
要用到的Loki头文Ӟ
首先看看in/inout/out的声明。ؓ了简化,q里L了跟cd推导无关的部分?/P>
Ҏ上面Method的定义,Method < void(in
也就是说Q只要我们能使用静态推导方式,获得A,B,C,Dq四个参C所有的incdQ把它交lBase作ؓ模板参数成了?/P>
q里需要一个辅助的模板c,用来在编译时帮助推导Q?BR> q个pd的名字叫“ؓC++实现一个IDL”,实际上应该叫“ؓC++实现一个Remoting”可能更好一些,说是IDLQ主要是想通过宏,使用单的cd定义辑ֈ自动生成调用代码的目的?/P>
一、首先来看看调用习惯?/STRONG> 从调用习惯入手,主要是因为看到目前有很多?工具包在调用上都有很多不便之处。假如能在一开始就从这点出发,p把调用接口设计得更好一些?/P>
先来看看服务端如何开放一个服务?/P>
2、 服务验证方式: 看v来挺多的Q其实很难接触到q些Q只需要用宏来定义一个服务,可以通过模板的类型推|自动生成q些复杂的定义?/P>三、调用过E?BR> 四、异步调?异步分派(AMI/AMD)?BR> AMI和AMD对于静态定义的服务是有影响的,比如下面一个服务: 五、其它?BR>
注意Q这里是以Method < void(in
class InOutTypeTraits
{
Loki::CompileTimeError <false> Not_Supported_Type;
};
template <class T>
struct InOutTypeTraits < in<T> >
{
enum {isin=1, isout=0};
};
template <class T>
struct InOutTypeTraits < out<T> >
{
enum {isin=0, isout=1};
};
template <class T>
struct InOutTypeTraits < inout<T> >
{
enum {isin=1, isout=1};
};
template <>
struct InOutTypeTraits < NullType >
{
enum {isin=0, isout=0};
};
通过另一个模板类InList来帮我们产生所有的incdQ它的结果是一个Typelist。ؓ了方便以后用,我把outcd产生器也做了一个OutList?BR>
struct If
{
typedef _IF Result;
};
template <class _IF, class _ELSE>
struct If <0, _IF, _ELSE>
{
typedef _ELSE Result;
};
template <class A = NullType, class B = NullType, class C = NullType, class D = NullType,
class E = NullType, class F = NullType, class G = NullType, class H = NullType
>
struct InList
{
typedef typename If <
InOutTypeTraits <A>::isin,
typename Typelist < A, typename InList<B,C,D,E,F,G>::Result >,
typename InList<B,C,D,E,F,G,H>::Result
>::Result Result;
};
template <class A>
struct InList <A, NullType, NullType, NullType, NullType, NullType, NullType, NullType>
{
typedef typename If <
InOutTypeTraits <A>::isin,
typename MakeTypelist <A>::Result,
typename MakeTypelist <>::Result
>::Result Result;
};
template <class A = NullType, class B = NullType, class C = NullType, class D = NullType,
class E = NullType, class F = NullType, class G = NullType, class H = NullType
>
struct OutList
{
typedef typename If <
InOutTypeTraits<A>::isout,
typename Typelist < A, typename OutList<B,C,D,E,F,G>::Result >,
typename OutList<B,C,D,E,F,G,H>::Result
>::Result Result;
};
template <class A>
struct OutList <A, NullType, NullType, NullType, NullType, NullType, NullType, NullType>
{
typedef typename MakeTypelist <A>::Result Result;
};
它的原理是,ҎIf模板cL判断一个类型是不是incdQ是的话把它加入到Typelist中,不是排除它?BR>
InList
现在Base模板cd接受一个模板参敎ͼ它是一个TypelistcdQ这个不详细讲了Q把它的定义写出来:
struct Base
{
Loki::CompileTimeError <false> Only_Use_Partial_Specialisation_Version;
};
template <class T>
struct Base <T, 0>
{
typedef void(*FUNC_TYPE)();
template <class FUNC_TYPE>
void async_call (FUNC_TYPE func)
{
}
void async_call ()
{
}
};
template <class T>
struct Base <T, 1>
{
typedef void(*FUNC_TYPE)(
typename TypeAt <T, 0>::Result::OriginalType);
void async_call (
typename TypeAt <T, 0>::Result::OriginalType v0,
FUNC_TYPE func)
{
}
void async_call (typename TypeAt <T, 0>::Result::OriginalType v0)
{
}
};
template <class T>
struct Base <T, 2>
{
typedef void(*FUNC_TYPE)(
typename TypeAt <T, 0>::Result::OriginalType,
typename TypeAt <T, 1>::Result::OriginalType);
void async_call (
typename TypeAt <T, 0>::Result::OriginalType v0,
typename TypeAt <T, 1>::Result::OriginalType v1,
FUNC_TYPE func)
{
}
void async_call (
typename TypeAt <T, 0>::Result::OriginalType v0,
typename TypeAt <T, 1>::Result::OriginalType v1)
{
}
};
template <class T>
struct Base <T, 3>
{
typedef void(*FUNC_TYPE)(
typename TypeAt <T, 0>::Result::OriginalType,
typename TypeAt <T, 1>::Result::OriginalType,
typename TypeAt <T, 2>::Result::OriginalType);
void async_call (
typename TypeAt <T, 0>::Result::OriginalType v0,
typename TypeAt <T, 1>::Result::OriginalType v1,
typename TypeAt <T, 2>::Result::OriginalType v2,
FUNC_TYPE func)
{
}
void async_call (
typename TypeAt <T, 0>::Result::OriginalType v0,
typename TypeAt <T, 1>::Result::OriginalType v1,
typename TypeAt <T, 2>::Result::OriginalType v2)
{
}
};
template <class T>
struct Base <T, 4>
{
typedef void(*FUNC_TYPE)(
typename TypeAt <T, 0>::Result::OriginalType,
typename TypeAt <T, 1>::Result::OriginalType,
typename TypeAt <T, 2>::Result::OriginalType,
typename TypeAt <T, 3>::Result::OriginalType);
void async_call (
typename TypeAt <T, 0>::Result::OriginalType v0,
typename TypeAt <T, 1>::Result::OriginalType v1,
typename TypeAt <T, 2>::Result::OriginalType v2,
typename TypeAt <T, 3>::Result::OriginalType v3,
FUNC_TYPE func)
{
}
void async_call (
typename TypeAt <T, 0>::Result::OriginalType v0,
typename TypeAt <T, 1>::Result::OriginalType v1,
typename TypeAt <T, 2>::Result::OriginalType v2,
typename TypeAt <T, 3>::Result::OriginalType v3)
{
}
};
q部分有点多Q其实还是比较清晰的。注意这个Base的版本已l不是上面所讲的那个了?BR>
函数原Ş推导问题p完了。上面的代码不一定还能编译,昨天是能~译的,被我修改了一些,Z解释Q又Ҏ昨天那样子?img src ="http://www.shnenglu.com/cpunion/aggbug/349.html" width = "1" height = "1" />
]]>
{
// 发布为SOAP服务Q先生成一个服务容器?BR> // 服务发布在localhost?911上,localhost用来l定loopback|卡?/SPAN>
SOAPProxy soap_service (7911, “localhost?;
TestService test_service;
soap_service.addService (“test_service? &test_service);
TestService service1;
soap_service.addService (“HelloService? &service1);
try{
soap_service.run ();
} catch (SocketException& e)
{
} catch (SignalException& e)
{
}
return 0;
}
我希望就是这么简单,客户端调用有多种方式Q?BR>1、 用服务的IDL定义Q直接调用:
{
SOAPProxy soap_service (7911, “localhost?;
try{
TestService test_service (“test_service? &soap_service);
test_service.method1 (/*
*/);
} catch (SocketException& e)
{
}
return 0;
}
q种方式比较单,调用时会查是否已l连接,然后发送调用请求,q处理调用结果?/P>
{
SOAPProxy soap_service (7911, “localhost?;
TestService test_service;
soap_service.getService (“test_service? &test_service);
if (test_service)
{
try{
test_service.method1 (/*
*/);
} catch (SocketException& e)
{
}
}
return 0;
}
3、 服务发现方式:
{
SOAPProxy soap_service (7911, “localhost?;
vector <string> services_name = soap_service.getServiceNames ();
// 
IService* test_service = soap_service.getService (“test_service?;
if (test_service)
{
vector <string> methods = test_service->getMethodNames ();
IMethod* method = test_service->getMethod (“method1?;
vector <Type*> types = method->getParameterType s()
method->addArg (3);
method->addArg (4);
// 
method->invoke ();
// 
}
return 0;
}
二、基本需求?BR>
单分析一下,上面一共涉及了哪些cdQ?BR>IProxy:
q是所有Proxycȝ基类(和接?Q它可以容纳多个服务对象Q提供服务绑定、服务查询、服务发现、服务验证?BR>IService:
所有Servicecȝ基类Q可以容U_个方?Method)Q提供方法查询、服务验证?BR>IMethod:
所有Method模板cȝ基类Q容U_个参敎ͼ包括q回|可通过查询参数cd获得Ҏ的类型定义?BR>IParameter:
所有参数的基类Q包含一个参数类型描q和一个参数倹{?BR>IType:
所有类型的基类Q预定义了一些基本类型,可自定义cd?/P>
以下只简单分析一下同步调用,异步调用是以后的扩充话题?BR>ҎW二节的3U不同调用过E,要描q如下:
q种方式看v来多了一些操作,不过验证的好处是能够减少调用时的异常?
使用q种方式Q调用端不需要服务的cd定义?
同步调用Ӟ调用端线E需要等待调用结果,服务端线E也要等待调用结束返回,才处理下一个调用?BR>Z在服务调用期间让U程能做更多的事Q?BR>调用端把调用交给U程池完成,q在调用完成后采用某U机刉知U程处理l果Q或者直接由U程池中的调用线E调用结果处理函数。这U方式称为AMI(异步Ҏ调用)?BR>服务端主U程则把接收到的消息解析后,攑օ处理队列Q由U程池去处理调用q程。当调用完成后,l果攑օl果队列Q由ȝE处理成消息Q发送回调用端。这U方式称为AMD(异步Ҏ分派)?BR>调用端和服务端依旧是使用通讯协议来沟通,双方都不知道Ҏ是否采用了异步方式?/P>
{
Method <void(in<int>, out<int>)> method1;
}
在同步调用时Q它的调用方式:
int a;
service.method1 (3, a); // 或者 service.method1 (3, &a)Q打兼容这2U方式?/SPAN>
异步调用Ӟ调用方式Q?/P>
TestService test_service;
test_service.method1.async_call(3, method1_result); // 调用完成后,让调用线E去调用method1_result通知调用l果?BR>// 或者像下面
IMethod* result = test_service.method1.async_call (3);
while (!result->done()) // q有很多好办法,q里只是Z单?/SPAN>
{
sleep (1);
}
cout << result->getArg(1)->toInt();
正如上面演示的一P异步调用的结果有2U方式去处理?BR>一是由U程池调用完以后Q接着调用一个函C通知l果。它不需要轮询,不过涉及CU程问题Q增加了一些复杂性?BR>另一U方式调用结束后Q原调用U程在某个适当的“时机”去查询调用l果。这个时机可以是定时查询Q也可以是被U程消息通知而去处理?/P>
q一加上前一,应该是提C全部的要炏V?BR>目前剩下的唯一一个难点,可能是在处理异步调用ӞMethod的定义?BR>正如上面演示的,一个方法在同步调用和异步调用时Q就?U调用方式:
test_service.method1.async_call(3, method1_result);
IMethod* result = test_service.method1.async_call (3);
特别圎ͼ它如何根据in和个数和out的个敎ͼ产生?个参C数匹配的异步调用函数Q?BR>再来回顾一下method1的定义:
昄有一定的复杂性,不过我认是可以处理掉的。拿3个参数的偏特化版本来说明Q?/P>
class Method <Out<Ret>(A,B,C)> : public MethodBase <Out<Ret>,A,B,C>
{
};
template <class Ret=NullType, class A=NullType, class B=NullType, class C=NullType, class D=NullType, IN_COUNT=InCount< A,B,C,D>::value >
class MethodBase
{
};
通过对MethodBasecȝINQCOUNT参数定义偏特化,卛_定义些不同的版本?BR>
当然仅仅是知道了IN参数的个敎ͼq没有提取出IN参数的类型,所以还不能生成函数的原型,或许需要把typelist加进来了Qloki中的那个Q?BR>
q是后面要考虑的内容,今天先想到这?/P>
q段旉考虑实现一个纯CQ+的分布式服务包装库,要描q如下:
有如下类和函敎ͼ
x它们作ؓ服务发布出去Q以SOAP或其它方式。发布ؓ一个TestServiceQƈ让它携带多一些信?
CQ+有许多工兗库来做到这点,但是Q都需要生成一堆代码,很是不爽?BR>
其它语言Q比如python, java, c#{,都可以通过自省机制Q抛开IDL在语a内实现?BR>
CQ+q不能做这个,它只是缺够的cd信息。比如上面的例子Q如果要发布为服务,那么臛_应该把它的参数、返回值搞得明些Q否则要么会造成不必要的参数传递,要么会生错误(把OUT参数取值可不是安全的)?BR>
比如上面出现的int, int&, int*Q在作ؓin参数Ӟ我们是想传递它的|cd为int。而int*和string*作ؓout参数Ӟ我们惌它传递指针或引用Q当调用q回Ӟ我们l它赋倹{?BR>
CQ+语言的类型极Z富,却没有描qC个参数到底是inq是out。java也没有,但它可以正常序列化一个null|在CQ+中,q可能存在一些麻烦?BR>
再考虑一下char*cdQ假如它是in参数Q那么它是要传递一个字W还是一个字W串QCQ+语言没有对它q行描述?BR>
所以要实现一个分布式服务包装Q或代理Q库Q必让发布者提供这些信息?BR>
我们知道Q要查询一个远E服务,必须查询相应L端口Q获取服务信息。最单的服务信息包括Q服务列表,每个服务中的Ҏ列表Q方法的cdQ包括参数和q回值类型,in/out信息{)?BR>
实际上,我们是要为CQ+增加一些简单的自省能力。上面那个服务发布接口,实际上离q个要求q有很远Q再来看一下:
可以惌Q它是没有一点自省能力的Q我们如何向它查询,它的名字Q它的方法列表?Ҏ的类型?它如何与Testcȝ成员函数以及test_func函数兌Q?/P>
二、方向?BR>
要让上面那个服务h自省能力Q要做的扩充其实q不多。考虑下面的代码:
q几个Method可以用自己写的委托类来做?BR>
1、假如我们在TestService的构造函数里l它分配一个“TestService”名字,q且ServicecdC查询名字的接口,那么它就知道它自q名字了?BR>
2、假如在TestService的构造函数里为各个Method分配名字Qƈ且注册到TestServiceQ那么它p够查询方法列表?BR>
3、方法的cdQ通过模板方式Q把各个参数cd攉hQ给个字W串名称可以了?BR>
使用宏来实现Q大概可以写成这P
通过上面q几个宏Q我们能够生成TestService声明?BR>
不过Q有几个问题Q罗列如下,q一一解决它:
1、如何把函数指针传给它?如何把方法名UCl它Q?BR>q个只是CQ+语言为我们增加了一些麻烦,我们无法在定义成员的地方调用它的构造函敎ͼ不过qƈ不会造成多大障碍?BR>上面的METHOD宏如果只是生成类的声明,那么函数指针可以省略。我把它加上的原因是Q它可以被我用Ctrl+C, Ctrl+Vq种世界上最先进的技术原h贝下来,q且通过单修改的Ҏ实现q种世界上最先进的重用?BR>
上面的代码经q修改,l果成q样Q?/P>
看上d应得非常整齐Q修改v来也比较单。上面那部分被扩充ؓ如下代码Q?/P>
基本上需要的东西都在q里了?BR>
2、客L的问题?BR>
上面q种映射Q直接拿到客L会有问题QTestcdtest_func函数我们q不打算交给客户端,所以用函数指针会出现链接错误?BR>
实际上客L不需要这个,我们惛_法把它拿掉就行了。客L实际需要生成的代码如下Q?BR>
q是上面提到的,C++l我们带来的ȝ。这ơ需要另一l宏来完成它Q?/P>
METHOD*和METHOD_DEFINE*宏的参数都有一些多余的信息Q没有去掉是因ؓ攑֜一起容易看到写错的地方。(q个技巧来源于前几天看的一BLOGQ很报歉没有C地址Q?BR>
3、用的问题?BR>
如何才能比较方便C用?我考虑了下面这U方式:

Method::operator ()的各个参数都可以接受相容的cdQ像上面一P因ؓ在TestService中我们已l定义了它要传输的值的cd?BR>
a.NONE是什么?其实是ؓ异步调用考虑的。假如指定某个OUT参数为NONEQ则q个参数的值ƈ不真正的OUTQ而是保存在Method中。实际上Method中保存每个参数的倹{?BR>
b.Method与Service如何发生关系Q?BR>从TestService的定义中我们知道QMethod向Service注册自己以实现自省,但它同时也会保存Service的指向?BR>我们的Proxy实际上是一个承模板,上面q没有把它指出来。它的定义是Q?/P>

所以我们的TestService其实也是模板c,它将使用XProxy中定义的序列化类。XProxy实现Service基类中序列化虚函C及调用虚函数?BR>
当一个Method调用Ӟ它会调用Service的序列化Q由于被重写了,所以调用的是XProxy中的序列化方法。这个方法会把这个Method的各in/inout参数序列化,然后执行q程调用Q再把调用结果反序列化给inout/out参数?BR>
4、其它想法?BR>
在考虑上面的定义方式时Q我也考虑了其它方式,主要是返回值处理的ҎQ简q如下?BR>
前面我们假设了一D将被开放ؓq程服务的代码:
在前面的做法中,我们的服务描q是攑֜那一l宏里面Q好处是不用改这D代码,坏处是代码定义的地方和描述不在一P协调可能会有一些不ѝ?BR>
我也考虑了另一U做法:
对于实现代码Q只需要修改返回gؓvoid的函敎ͼ把return;修改为return VOID;Qƈ且ؓ没有写此语句的分支加上此句?BR>
VOID是一个特D类型的静态变量,专ؓvoidq回值的函数讑֮?BR>
q种做法修改了原有的代码Q不q在定义服务时可以节省一些工作:
它所需要的函数cdQ将由函数指针推对{?BR>
在GQ+~译器下Q可以用typeof来获得函数指针的cd而不需要真得获得函数指针|不过目前仅仅在GQ+下可用。(Z说一下,typeof已经列入c++0xQ?BR>
最l我攑ּ了这个想法,毕竟它要修改现有的代码,某些情况下这是不可能的,而且typeof目前也不能跨~译器?BR>
三、实现?BR>
老实说我现在q没有一份完整的或半完整的实玎ͼ大部分想法还在头脑中Q测试代码倒是写了不少Q主要是用来试上述x能否实现Q我惛_部分情况都已l测试了Q只需要有旉来把它实现出来?BR>
q是我近期要做的事之一Q争取月内把它做完Ş?/P>