Иерархия классов с указателями на функцию приведения вниз

TL; DR Как я могу получить Base::Choose принять Derived::CustomUserFunction в качестве аргумента?
Ошибка, которую я получаю, и я понимаю, почему я ее получаю, но я не знаю, как ее устранить:
Argument of type "Derived::* (...)" is incompatible with parameter of type "Base::* (...)"

#include <iostream>
class Base {
public:
void Base::Choose(void(Base::*FctPtr)()) {
(this->*FctPtr)();
}

virtual void Base::CustomUserFunction() = 0;
};

class Derived : public Base {
public:
virtual void Derived::CustomUserFunction() {
std::cout << "CustomFunction\n";
}
};

int main()  {
Derived D;
D.Choose(&D.CustomUserFunction);
}

Полная версия

Я пишу Discreet Event Simulation Engine (DESEngine), который использует пользовательские пользовательские функции. Поскольку эти пользовательские функции пользователя конкретная проблема, Я хочу иметь возможность позвонить им не зная их подписи. Двигатель читает TURN_ON в текстовом файле и ищет указатель функции, сопоставленный с TURN_ON псевдоним строки в unordered_map.

В основном DESEngine будет призывать UserEvents::Choose(std::string Name) это вызвало бы функцию, заданную его именем события (строка).

Все работало отлично, но управляемость становится жесткой так как мне приходится использовать движок для множества разных задач (от решения дифференциальных уравнений до моделирования простых компьютеров). (И в моем классе UserEvents у меня были всевозможные функции для решения проблем)

Из-за этого я решил сделать свой класс UserEvents абстрактный класс и наследовать его для каждой проблемы, которую я имею, таким образом, пользовательские функции будут храниться в производном классе, не в базе, но я хочу "Choose" метод проживать в базовом классе. Как я могу вызвать функции в производных классах из базового класса?

Ошибка, которую я получаю, и я понимаю, почему я ее получаю, но я не знаю, как ее устранить:
Argument of type "UserEvents_MVN::* (...)" is incompatible with parameter of type "UserEvents::* (...)"

class UserEvents
{
public:
// Receives an event name and parameters
struct EventWithParams { std::string Name; std::vector<boost::any> Params; };

// Lets an outside class (DESEngine) configure which event to run before calling UserEvents::Choose()
// TODO: Make getters/setters so Events can be passed through a better way
EventWithParams Event;

// Select which function(parameters) to call based on the EventWithParams Event (above)
int UserEvents::Choose();

protected:
void UserEvents::UserFunctionPointerMap_AddFunction
(std::string Alias, void(UserEvents::*FunctionPointer)(const std::vector<boost::any>&));

virtual void UserEvents::BuildUFPAliasMap() = 0;

private:

// Here we have an unordered map that assigns User Function (pointer) to each Key (string or Alias or Event Name)
std::unordered_map<std::string, void(UserEvents::*)(const std::vector<boost::any>&)> UserFunctionPointerAliasMap;
};

Вот реализация выбора

int UserEvents::Choose()
{
try
{
(this->*UserFunctionPointerAliasMap.at(Event.Name))(Event.Params);
return 0;
}
catch (const std::out_of_range e)
{
throw std::exception::exception("Unknown User Event");
return -1;
}
}

Пример производного класса

#pragma once
#include "..\\UserEvents.h"
class UserEvents_MVN : public UserEvents
{
public:
UserEvents_MVN();
~UserEvents_MVN();

protected:

void UserEvents_MVN::MEMDUMP_LOAD(const std::vector<boost::any> &Parameters);

void UserEvents_MVN::MEMDUMP(const std::vector<boost::any> &Parameters);

void UserEvents_MVN::InstructionDecode(const std::vector<boost::any> &Parameters);private:

virtual void UserEvents_MVN::BuildUFPAliasMap();
};

И как я строю индекс указателей на функции (UserFunctionPointerAliasMap)

void UserEvents_MVN::BuildUFPAliasMap()
{
UserFunctionPointerMap_AddFunction("MEMDUMP_LOAD",  &UserEvents_MVN::MEMDUMP_LOAD );
UserFunctionPointerMap_AddFunction("MEMDUMP",   &UserEvents_MVN::MEMDUMP );
UserFunctionPointerMap_AddFunction("DECODE",    &UserEvents_MVN::InstructionDecode);
}

-2

Решение

Одним из способов решения проблемы является определение virtual функции-члены в UserEvents и использовать их при построении карты.

Пример:

class UserEvents
{
public:

virtual void MEMDUMP_LOAD(const std::vector<boost::any> &Parameters) = 0;

...
};

а затем переопределить virtual функтин в UserEvents_MVN,

class UserEvents_MVN : public UserEvents
{
public:
UserEvents_MVN();
~UserEvents_MVN();

protected:

void MEMDUMP_LOAD(const std::vector<boost::any> &Parameters) override;

...
};

и построить карту, используя:

void UserEvents_MVN::BuildUFPAliasMap()
{
UserFunctionPointerMap_AddFunction("MEMDUMP_LOAD",  &UserEvents::MEMDUMP_LOAD );
...
}

Обновление в ответ на комментарий ОП

Другой вариант заключается в использовании std::function на карте.

Скажем ваш map определяется как:

using FunctionMap = std::map<std::string, std::function<void(UserEvent*, std::vector<boost::any>&)>>;
FunctionMap myFunctionMap;

Затем вы можете использовать lambda функции, чтобы конкретизировать myFunctionMap,

void UserEvents_MVN::BuildUFPAliasMap()
{
UserFunctionPointerMap_AddFunction("MEMDUMP_LOAD",
[](UserEvent* event, std::vector<boost::any>& parameters))
{ dynamic_cast<UserEvents_MVN*>(event)->MEMDUMP_LOAD(parameters);});
...
}
0

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

Я обратился за помощью в IRC, на который кто-то указал мне CRTP — Любопытно повторяющийся шаблон который позволяет методам в Base получить доступ к членам Derived, и это то, к чему я стремлюсь.

// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
// methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
// ...
};

И они любезно послали мне пример

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

template <typename T>
class Dispatch
{
std::unordered_map<std::string, void (T::*)()> commands;

protected: // used by application type only
void Add(std::string name, void (T::*fptr)())
{
commands[name] = fptr;
}

public: // becomes part of the public interface for application type
/* This part is the only reason to use CRTP/inheritance. The application
type could simply create this method itself and just call functions of
a Dispatch<App> member to do all the work, but this approach saves us
from typing that out. */
void Choose(std::string s)
{
auto fptr = commands.at(s);
auto target = static_cast<T*>(this);
(target->*fptr)();
}
};

class App : public Dispatch<App>
{
public:
App()
{
// for demo purposes
Add("foo", &App::Test);
}

void Test()
{
std::cout << "dispatched!\n";
}
};

int main()
{
App a;
a.Choose("foo");
}
0