Можно ли использовать таблицу обратного вызова, которая содержит указатели на функции классов {sub, super}? - PullRequest
0 голосов
/ 25 мая 2019

В настоящее время у меня есть объект подключения, который выполняет обработку входящих запросов команд. Каждая команда представляет собой строку, которая сопоставляется с методом обратного вызова с использованием неупорядоченной карты. Урезанный пример, который иллюстрирует основную функцию:

#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>

class Conn;
typedef void (Conn::*ProcFn)();

class Conn
{
  private:
  void proc_ping();

  static const std::unordered_map<std::string, ProcFn> handlers;

  public:
  void simulate();
};

const std::unordered_map<std::string, ProcFn> Conn::handlers = {
  std::pair("ping", &Conn::proc_ping)
};

void Conn::proc_ping()
{
  std::cout << "ping!" << std::endl;
}

void Conn::simulate()
{
  auto pfn = handlers.at("ping");

  std::invoke(pfn, this);
}

int main()
{
  Conn c;

  c.simulate();
}

Это отлично работает, но я понял, что мне понадобится несколько разных сетевых интерфейсов, которые имеют разные основные роли (например, «интерфейс управления» и «интерфейс данных клиента»), поэтому я сделал копию класса Conn в ConnMgmt и ConnData , Однако быстро стало очевидно, что между обработчиками было достаточно совпадений, поэтому имело смысл создать общий класс для них.

Я хочу создать ConnBase, который содержит общие функциональные возможности ConnMgmt и ConnData.

Мой вопрос относится к таблице функций обратного вызова.

Я хочу иметь одну таблицу для сопоставления строк запроса команды с функциями обратного вызова, , но Я бы хотел, чтобы указатели методов могли ссылаться на методы в производных классах. То есть У меня есть один диспетчер для ConnBase и ConnMgmt или ConnBase и ConnData (в зависимости от того, какой интерфейс вызывается).

Это [нефункциональный] код, который иллюстрирует суть того, чего я хотел бы достичь:

#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>

class ConnBase;
typedef void (ConnBase::*ProcFn)();

class ConnBase
{
  protected:
  void proc_ping();
};

void ConnBase::proc_ping()
{
  std::cout << "ping!" << std::endl;
}

class ConnMgmt : public ConnBase
{
  protected:
  static const std::unordered_map<std::string, ProcFn> handlers;

  void proc_create_user();

  public:
  void simulate();
};

void ConnMgmt::proc_create_user()
{
  std::cout << "create user!" << std::endl;
}

const std::unordered_map<std::string, ProcFn> ConnMgmt::handlers = {
  std::pair("ping", &ConnBase::proc_ping),
  std::pair("create_user", &ConnMgmt::proc_create_user)
};

void ConnMgmt::simulate()
{
  auto pfn = handlers.at("ping");

  std::invoke(pfn, this);

  pfn = handlers.at("create_user");

  std::invoke(pfn, this);
}

int main()
{
  ConnMgmt c;

  c.simulate();
}

Этот код нарушается несколькими способами, но мне особенно интересно, если:

  • Является ли идея иметь таблицу ConnMgmt :: handlers, которая содержит указатели на методы как в ConnBase, так и в ConnMgmt, принципиально несостоятельной? Компилятор каркает, говоря, что указатель неверного типа.
  • Если есть способ разыграть его, который решит проблему, войду ли я в область неопределенного поведения (или хуже) ?. Я читал, что «должно» быть возможно использовать тип указателя метода для подклассов, но есть предостережения, относящиеся к виртуальным классам (и на этом примечании: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - 10 * *). функции обработчика будут виртуальными).
  • Учитывая то, что я хочу достичь (создать диспетчер строки-метода, где методы могут находиться в разных классах (хотя они всегда принадлежат к одной и той же иерархии классов)), существует ли какой-то идиоматический способ C ++ 17 сделать это

(повышение не допускается).

1 Ответ

1 голос
/ 25 мая 2019

Вы можете сделать то, что вам нужно, с несколькими изменениями.

Если вы измените тип ProcFn на

typedef void (ConnMgmt::*ProcFn)();

тогда вы почти готовы построить свой ConnMgmt::handlers стол. Вам необходимо указать типы для std::pair. Тогда оставшаяся проблема заключается в том, что ConnBase::proc_ping защищен и недоступен. Чтобы исправить это, нужно изменить ссылку на ConnMgmt::proc_ping.

const std :: unordered_map ConnMgmt :: handlers = { std :: pair ("ping", & ConnMgmt :: proc_ping), std :: pair ("create_user", & ConnMgmt :: proc_create_user) };

Каждый производный класс будет нуждаться в собственной таблице обработчиков и ProcFn typedefs.

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