Как реализовать более удобный для C ++ / OO-совместимый механизм диспетчеризации сетевых методов?

Хорошо, первый вопрос:
Мне нужно знать, какие у меня есть альтернативы переключателю Incredible Evil (TM) в моем методе MethodCall для удаленной отправки объектов (RPC для бедняков).

    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 и т. д.). Кроме того, у меня есть карта реестра, которая отслеживает, какие ссылки на объекты являются живыми (имеется в виду, для какого идентификатора объекта целые числа входящие вызовы метода с другого конца являются действительными).

Аргументы обрабатываются с помощью стека четвертого стиля, поэтому у вас будет такой входящий поток, как:

3 5 math.plus() 7 math.multiply()

достичь эквивалента (3 + 5) * 7

vqueue<> .push () и getarg<> () обрабатывает помещение значений в исходящий поток через vqueue и чтение аргументов через 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), возможно, с использованием STL-карты intMethodId-to-ptrToMember (предоставляя нижестоящим элементам простой способ переопределить вещи) , Будут ли указатели на члены базового класса 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 но возьми добычу на serverRoot в https://github.com/gau-veldt/Minetest-Blockiverse/blob/master/blockiverse/server.hpp чтобы ясно увидеть, как злой метод переключения уже становится в дикой природе …

1

Решение

По крайней мере, одно возражение по этому вопросу приводит меня к выводу, что, по крайней мере, один человек может быть заинтересован в решении / ответе на этот вопрос.

Мне удалось удалить злые ключи и заменить их на то, что я маркирую как «отправленный вызов метода» или dmc в коде. Они отправляются при входе из сети для преобразования в вызовы методов, следовательно, из номенклатуры «отправленные вызовы методов»

Теперь иерархия использует функцию указателя на член, заключенную в адаптеры 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";}

};

Здесь важно отметить, что MethodCall больше не существует внутри serverRoot (или Account, clientRoot и последующих друзей), и что он должен что-то делать с dmcLabel [0] / dmcTabel [0] — он наследует настройки от bvnet: : Объектный ctor. Точно так же, если бы futureServerRoot создавал подкласс serverRoot, он бы унаследовал таблицу dmc от обоих cvnet :: Object и serverRoot. Таким образом, у меня теперь есть надлежащий механизм наследования в иерархии dmc, и нет электронной почты с ядерной бомбой от будущего сопровождающего (ну, в любом случае, по этой причине), пытающегося сделать класс futureServerRoot: public serverRoot;)

В целом полезный рефакторинг. Это исключило кучу котельной плиты (которая не понадобится и на последующих объектах). Шаблон dmc является менее громоздким в классах (реализации могут быть перемещены из встроенных классов или иным образом) и больше не застревает в злом монолитном переключении из ада.

Не идеально, но это большое улучшение по сравнению с тем, что у меня было раньше, и оно дает ответ на мой первоначальный вопрос.

0

Другие решения


По вопросам рекламы [email protected]