C++ 0x keynote(以下簡稱0x)中描述了這樣一個看起來不錯的東西:
1、本地調用代碼:
// use local object:
X x;
A a;
std::string s("abc");
// …
x.f(a, s);
2、使用遠程代理wrapper層:
// use remote object :
proxy<X> x;
x.connect("my_host");
A a;
std::string s("abc");
// …
x.f(a, s);
僅使用一個包裝層就完成遠程調用?從目前的C++來看基本上不可能。 今天突然想到可以使用aspect c++來生成代碼,因為aspect c++在生成代碼時,也生成了一些簡單的元信息,可以在函數里面取得函數的原型、各參數的類型等。 根據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;
}
現在的目標是加入一個方面,讓RemoteLoginService具有遠程調用功能。當然由于此處RemoteCall并未實現,所以只要能夠把這個調用正確序列化就算完成目標。 這個方面完成后如下:
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());
// 解析接收的數據,遠程調用結果寫入tjp->result()指向的內存
}
};
它匹配所有從RemoteCall上派生的類,為它的每個方法加入遠程調用代碼以及調用結果處理代碼。 生成并編譯運行,輸出如下:
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:|
由于完整序列化了各個參數值,第一個目標——生成遠程調用代碼——算是完成了。
下一個目標,考慮服務端如何編寫?服務端需要開啟一個服務,并注冊各個服務接口。
要達到這個目標,aspect c++需要提供類、方法級別的類型及名稱獲取,不過aspect c++在這方面沒有提供更多方便,現在只能在方法執行時獲得方法的信息,它所生成的“元信息”過于簡單,而且為了效率考慮都實現為各個獨立的結構,結構的成員也大都是static的,所以無法使用一個合適的接口來反射,期待以后能加入這些特性。
所以這第2個目標實際上無法簡單地完成,除非在服務端手工添加服務注冊代碼,這個部分工作量稍小,但還是可以做到的。