Вызов метода производного класса из того же метода родительского класса - PullRequest
0 голосов
/ 11 февраля 2020

Моя проблема заключается в следующем: я пишу расширяемый протокол связи между сервером и клиентом. Объекты, которые необходимо передать, используют шаблон проектирования .

. Вот формат класса:

class command {

  public:
    using id = uint8_t;
    using buffer = std::vector<char>;

    enum command_t: id {a_command, another_command};

    command() = delete;
    command(id id): id_(id) {}

    static command* unserialize(buffer);
    virtual buffer const serialize() const = 0;

    virtual void execute() = 0;

  protected:
    /* Not possible, see further where I explain. Keeping it here for
       my example */
    virtual static command* do_unserialize(buffer::iterator, buffer::iterator) = 0;
    id id_;

};

class a_command;
class another_command;

Каждая команда будет подклассом command , command_t id будет связан с каждой командой. В приведенном выше примере a_command и another_command также имеют соответствующий класс.

command::serialize реализован в производных классах. Что он делает, так это записывает всю необходимую информацию о классе в байтовом массиве и возвращает его. Конечно, то же самое относится и к защищенному command::do_unserialize - он берет байтовый массив и преобразует его в команду правильного типа . Но вот проблема:

Первый байт буфера будет всегда быть command::id, связанным с хорошим подклассом. Когда сервер / клиент получит данные, он прочтет идентификатор команды, а затем ему потребуется иметь возможность десериализовать их до правильного типа команды . Вот почему он должен будет вызывать функцию stati c command::unserialize, а не один из подклассов do_unserialize.


Быстрое и грязное исправление будет выглядеть как command::unserialize this:

command* command::unserialize(buffer b) {

    auto it{b.begin()};

    command::id const id{*it++};

    switch(static_cast<command::command_t>(id)) {
        case command::command_t::a_command:
            return a_command::do_unserialize(it, b.end());
        case command::command_t::another_command:
            return another_command::do_unserialize(it, b.end());
        default: 
            throw std::invalid_argument("command::unserialize: unknown command ID");
    }   
}

*** На самом деле не даже, вы не можете иметь виртуальную функцию-член c. Поэтому я понятия не имею, как это можно реализовать.

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

РЕДАКТИРОВАТЬ: Рабочий пример может быть переместить do_unserialize в конструктор подкласса и вернуть указатель на вновь созданный объект.

class derived_command: public command {
  public:
    derived_command(buffer::iterator beg, buffer::iterator end) {
        // do_unserialize logic
    }
};

// unserialize
switch (id) {
    case command::command_t::derived_class: return new derived_class(b.begin(), b.end());
}
// ...

Мой вопрос, таким образом, звучит так: есть ли способ динамически связывать новые команды только из поля command::id? Способ вывести подкласс для использования из его идентификатора? Иначе, мой дизайн имеет недостатки? Есть ли лучший способ сделать то, что я пытаюсь сделать?

Спасибо!

...