首先回答一下问题: 我需要知道我在远程“对象调度”(穷人的RPC)MethodCall方法中对Incredibly Evil(TM)开关有什么替代方法。
virtual void methodCall(unsigned int method) {
bvnet::scoped_lock lock(ctx.getMutex());
bvnet::value_queue &vqueue=ctx.getSendQueue();
switch(method) {
case 0: /* GetType */
/* emit object type to output queue as string */
vqueue.push(string(getType()));
break;
}
当然,有人问我用这种方法想要完成什么。
我已经建立了一个协议(网络),它实现了一个轻量级的分布式对象系统,允许一端或另一端使用对象引用和方法索引在一端或另一端进行方法调用,每个都是通过网络传递的整数(所以不能使用指针,ptr-to-method等)。此外,我在每个都有一个注册表映射,跟踪哪些对象引用是活着的(意味着对象id整数来自另一端的传入方法调用是有效的。)
使用第四种堆栈处理参数,因此您可以使用以下内容:
3 5 math.plus() 7 math.multiply()
达到相当于(3 + 5)* 7
vqueue<> .push()和getarg<>()处理通过vqueue将vlaues放入传出流并通过getarg<>()使用<>读取参数通过电线模拟支持的类型
这个动物是基类,它的目的是提供注册到特定连接会话的对象(每个端点都有自己的注册表,只要创建一个新对象就可以插入该注册表以引用它。
/**
** @brief ABC for remotable objects.
**
** Base used for objects exchangeable via object references.
**
** Since secure referencing requires a way to
** track object lifetime a registry reference
** is required for construction.
*/
class object {
protected:
session &ctx; /**< @brief for objects to attach to the session's registry */
public:
/** @brief construction of an object @param sess reference to session to attach */
object(session &sess) :
ctx(sess) {
LOCK_COUT
cout << "object [" << this << "] ctor" << endl;
UNLOCK_COUT
ctx.register_object(this);
}
/** @brief base dtor to automatically unregister the object */
virtual ~object() {
LOCK_COUT
cout << "object [" << this << "] dtor" << endl;
UNLOCK_COUT
ctx.unregister(this);
}
/**
* @brief Get object's identity.
* @return Object identity string
*
* Overriden by superclass to announce it's identity.
*/
virtual const char *getType() {return "baseObject";}
/**
* @brief Method call switchboard.
*
* Overidden by superclass to implement methods callable
* by the remote. Currently the superclasses are using
* big switchbanks which looks plain evil but at this
* point I'm not sure of what to refactor with.
*
* @todo
* Base class to implement some sort of glue to take out the switch boilerplate?
* @todo
* some sort of static enum to get rid of the magic number
* method call #s from remote POV?
* @todo
* automatically declare the methods for method calling
* via some sort of macro or metacode?
*
*/
virtual void methodCall(unsigned int idx)=0;
};
丑陋在于实现实际接口的派生对象。这是在MethodCall()中的切换:
class Account : public bvnet::object {
private:
s64 userId;
public:
Account(bvnet::session &sess,s64 who)
: bvnet::object(sess),userId(who) {}
virtual ~Account() {}
virtual const char *getType() {return "userAccount";}
virtual void methodCall(unsigned int method) {
bvnet::scoped_lock lock(ctx.getMutex());
bvnet::value_queue &vqueue=ctx.getSendQueue();
switch(method) {
case 0: /* GetType */
/* emit object type to output queue as string */
vqueue.push(string(getType()));
break;
}
}
};
所以再一个问题是,是否有另一种方法可以实现对C ++更友好的东西的编号方法调度以及某些未来的对象决定他想做的恶作剧
MyFutureAccount : public Account {...}
(并且害怕那些愤怒的电子邮件,我会从那个想要做到这一点的维护人员那里获得最大化附近的盖革计数器......)
似乎我可能需要在bvnet :: Object的构造函数中做一些工作并设置C ++内部(vtable)的某种形式,可能使用intMethodId-to-ptrToMember的STL映射(为下游提供了一种简单的方法覆盖东西)。 bvnet :: Object基类成员指针是否仍然可以在下游派生类中按预期工作?还有那么多问号。不确定我是否在正确的轨道上或在这个潜在的解决方案上咆哮错误的树...
而不是粘贴更多(我无法预测其他人想要看到的东西,我可以指向你的github,因为它是LGPL3开源的:https://github.com/gau-veldt/Minetest-Blockiverse/tree/master/blockiverse
最相关的文件是https://github.com/gau-veldt/Minetest-Blockiverse/blob/master/blockiverse/protocol.hpp但是在https://github.com/gau-veldt/Minetest-Blockiverse/blob/master/blockiverse/server.hpp的serverRoot中获取一个战利品,以清楚地看到切换方法在野外已经变得多么邪恶......
答案 0 :(得分:0)
至少有一个关于这个问题的upvote导致我推断出至少有一个人可能对这个问题的解决方案/答案感兴趣。
我确实设法删除了邪恶的开关并将其替换为我标记为&#34;调度方法调用&#34;或代码中的dmc。它们在从网络进入时被调度以转换为方法调用,因此命名方法调用#34;
现在层次结构使用指向std :: function适配器的指向成员函数的指针,它是std :: map的值类型:
typedef std::function<void(value_queue&)> dmcMethod;
typedef std::map<unsigned int,dmcMethod> call_map;
乐趣从bvnet :: object(基类)开始,其中初始化调用映射以引用基类dmc_GetType(dmc函数调用可覆盖的GetType并将值发布到传出值队列):
/**
** @brief ABC for remotable objects.
**
** Base used for objects exchangeable via object references.
**
** Since secure referencing requires a way to
** track object lifetime a registry reference
** is required for construction.
*/
typedef void(bvnet::object::*dmc)(value_queue&);
class object {
private:
/**
* @brief Displateched Method Call
*
* Implements dispatched method call (dmc)
*
* Superclass-installed dmc methods in dmcTable are
* callable by the remote. A superclass sets up his
* dmc methods in his ctor by accessing object's dmcTable
*
* As a plus the lock and value queue boilerplate has been
* been moved to the base class dispatcher and the dmc methods
* will be in locked context and given the value queue as a
* parameter. He also has an exception now to trap calls to
* an unimplemented method index.
*
* @todo
* some sort of static enum to get rid of the magic number
* method call #s from remote POV? I'll have the ctors store
* method labels in the base class dmcName map while it is
* setting up the dmc methods.
*
* @todo
* automatically declare the methods for method calling
* via some sort of macro or metacode?
*
*/
friend class session;
void methodCall(unsigned int idx) {
bvnet::scoped_lock lock(ctx.getMutex());
bvnet::value_queue &vqueue=ctx.getSendQueue();
auto dmcFunc=dmcTable.find(idx);
if (dmcFunc!=dmcTable.end()) {
(dmcFunc->second)(this,vqueue);
} else {
// called a method that doesn't exist
throw method_notimpl(getType(),idx);
}
}
protected:
session &ctx; /**< @brief for objects to attach to the session's registry */
call_map dmcTable; /**< @brief method mapper for method call and OO mechanism */
name_map dmcLabel; /**< @brief name labels of dmc methods */
void dmc_GetType(value_queue&); /**< @brief the GetType dispatched method call (dmc) */
private:
const string methodLabel(const unsigned int idx) {
const auto &s=dmcLabel.find(idx);
if (s!=dmcLabel.end()) {
return s->second;
}
return std::to_string(idx);
}
public:
/** @brief construction of an object @param sess reference to session to attach */
object(session &sess) :
ctx(sess) {
LOCK_COUT
cout << "object [" << this << "] ctor" << endl;
UNLOCK_COUT
ctx.register_object(this);
// dmtTable[0] is the GetType method
dmcTable[0]=&object::dmc_GetType;
dmcLabel[0]="GetType";
}
/** @brief base dtor to automatically unregister the object */
virtual ~object() {
LOCK_COUT
cout << "object [" << this << "] dtor" << endl;
UNLOCK_COUT
ctx.unregister(this);
}
/**
* @brief Get object's identity.
* @return Object identity string
*
* Overriden by superclass to announce it's identity.
*/
virtual const char *getType() {return "baseObject";}
};
这是一个新的serverRoot对象的例子,一旦被修改为使用dmc机制(单片MethodCall和开关疯狂消失了):
class serverRoot : public bvnet::object {
private:
BigInt cli_pub_mod;
BigInt cli_pub_exp;
Key *clientKey;
bool clientValid;
string challenge;
SQLiteDB db;
unsigned int randbits[8];
protected:
void dmc_LoginClient(value_queue &vqueue);
void dmc_AnswerChallenge(value_queue &vqueue);
void dmc_GetAccount(value_queue &vqueue);
public:
serverRoot(bvnet::session &sess)
: bvnet::object(sess) {
dmcLabel[1]="LoginClient";
dmcTable[1]=(dmc)&serverRoot::dmc_LoginClient;
dmcLabel[2]="AnswerChallenge";
dmcTable[2]=(dmc)&serverRoot::dmc_AnswerChallenge;
dmcLabel[3]="GetAccount";
dmcTable[3]=(dmc)&serverRoot::dmc_GetAccount;
clientValid=false;
challenge="";
clientKey=NULL;
}
virtual ~serverRoot() {
if (clientKey!=NULL)
delete clientKey;
}
virtual const char *getType() {return "serverRoot";}
};
这里要注意的重要一点是,serverRoot(或Account或clientRoot及其后的朋友)中不再有MethodCall,也不再需要对dmcLabel [0] / dmcTabel [0]执行任何操作 - 它继承了设置来自bvnet :: Object的ctor。出于同样的原因,一些futureServerRoot将子类化为serverRoot,它将从bvnet :: Object和serverRoot的ctors继承dmc表。因此,我现在在dmc层次结构中有一个适当的继承机制,并且没有来自未来维护者的核弹电子邮件(好不是因为这个原因)尝试做类futureServerRoot:public serverRoot;)
总而言之,这是一个有用的重构器。它消除了一堆锅炉板(后续物体也不需要)。 dmc样板在类中不那么笨重(实现可能被移出类内联或其他类)并且不再陷入地狱的邪恶单片开关中。
不完美,但它与以前的情况相比有很大改动,它提供了我原来问题的答案。