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