C++ 0x keynote(以下簡稱0x)中描述了這樣一個看起來不錯的東西:
1、本地調(diào)用代碼:
// use local object:
X x;
A a;
std::string s("abc");
// …
x.f(a, s);
2、使用遠(yuǎn)程代理wrapper層:
// use remote object :
proxy<X> x;
x.connect("my_host");
A a;
std::string s("abc");
// …
x.f(a, s);
僅使用一個包裝層就完成遠(yuǎn)程調(diào)用?從目前的C++來看基本上不可能。 今天突然想到可以使用aspect c++來生成代碼,因為aspect c++在生成代碼時,也生成了一些簡單的元信息,可以在函數(shù)里面取得函數(shù)的原型、各參數(shù)的類型等。 根據(jù)0x的描述,我編寫了簡單的測試代碼:
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
class LoginService
{
public:
virtual bool login (const string& name, const string& password, string& session) = 0;
virtual void logout (const string& session) = 0;
};
class RemoteCall
{
public:
bool connect (const char* host, unsigned short port)
{
cout << "connect success" << endl;
return true;
}
bool send (const char* p, size_t len)
{
cout << "send: " << endl;
cout << string(p, len) << endl;
return true;
}
bool recv(char* p, size_t len)
{
return true;
}
};
class RemoteLoginService : public LoginService, public RemoteCall
{
public:
virtual bool login (const string& name, const string& password, string& session)
{
return false;
}
virtual void logout (const string& session)
{
}
};
int main(int argc, char *argv[])
{
RemoteLoginService rls;
rls.connect("localhost", 3957);
string session;
rls.login("lijie", "lijie", session);
rls.logout(session);
return 0;
}
現(xiàn)在的目標(biāo)是加入一個方面,讓RemoteLoginService具有遠(yuǎn)程調(diào)用功能。當(dāng)然由于此處RemoteCall并未實現(xiàn),所以只要能夠把這個調(diào)用正確序列化就算完成目標(biāo)。 這個方面完成后如下:
aspect Remote
{
pointcut remote_class() = "RemoteCall";
pointcut remote_call() = derived(remote_class()) && !remote_class();
pointcut virtual_methods() = "% ...::%(...)";
advice within(remote_call()) && execution(virtual_methods()): before(){
stringstream ss;
ss << "\tcall:" << JoinPoint::signature() << endl;
ss << "\targuments:";
for (size_t i=0; i<JoinPoint::args(); ++i)
{
string arg(tjp->argtype(i));
if (arg.find("basic_string") != arg.npos)
{
ss << *(string*)tjp->arg(i) << "|";
}
}
string send_str = ss.str();
tjp->target()->send (send_str.c_str(), send_str.size());
}
advice within(remote_call()) && execution(virtual_methods()): after(){
vector<char> buffer(1024, '\0');
tjp->target()->recv (&(*buffer.begin()), buffer.size());
// 解析接收的數(shù)據(jù),遠(yuǎn)程調(diào)用結(jié)果寫入tjp->result()指向的內(nèi)存
}
};
它匹配所有從RemoteCall上派生的類,為它的每個方法加入遠(yuǎn)程調(diào)用代碼以及調(diào)用結(jié)果處理代碼。 生成并編譯運(yùn)行,輸出如下:
connect success
send:
call:bool RemoteLoginService::login(const ::std::basic_string< char > &,const ::std::basic_string< char > &,::std::basic_string< char > &)
arguments:lijie|lijie||
send:
call:void RemoteLoginService::logout(const ::std::basic_string< char > &)
arguments:|
由于完整序列化了各個參數(shù)值,第一個目標(biāo)——生成遠(yuǎn)程調(diào)用代碼——算是完成了。
下一個目標(biāo),考慮服務(wù)端如何編寫?服務(wù)端需要開啟一個服務(wù),并注冊各個服務(wù)接口。
要達(dá)到這個目標(biāo),aspect c++需要提供類、方法級別的類型及名稱獲取,不過aspect c++在這方面沒有提供更多方便,現(xiàn)在只能在方法執(zhí)行時獲得方法的信息,它所生成的“元信息”過于簡單,而且為了效率考慮都實現(xiàn)為各個獨(dú)立的結(jié)構(gòu),結(jié)構(gòu)的成員也大都是static的,所以無法使用一個合適的接口來反射,期待以后能加入這些特性。
所以這第2個目標(biāo)實際上無法簡單地完成,除非在服務(wù)端手工添加服務(wù)注冊代碼,這個部分工作量稍小,但還是可以做到的。