Class 向下转换函数指针的层次结构
Class Hierarchy with down cast function pointers
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);
}
完整版
我正在编写一个使用自定义用户函数的离散事件模拟引擎 (DESEngine)。由于这些自定义用户函数 特定于问题,我希望能够在不知道其签名名称的情况下调用它们 。 引擎读取 TURN_ON
在文本文件中查找映射到 unordered_map.
中的 TURN_ON
字符串别名的函数指针
基本上 DESEngine 会调用 UserEvents::Choose(std::string Name)
来调用由它的事件名称(字符串)给出的函数。
一切都很好,但可管理性变得越来越困难,因为我必须使用该引擎解决许多不同的问题(从微分方程求解到模拟简单计算机)。 (我的 UserEvents 中有各种不同的问题特定函数 class)
正因为如此,我决定让我的 class UserEvents
成为一个抽象 class 并且 为我遇到的每一种问题继承它,因此,自定义用户函数将存储在派生 class 中,而不是存储在基类中,但我希望 "Choose"
方法驻留在基类 class 中。 如何从基 class 派生的 classes 中调用函数?
我得到的错误,我明白为什么会得到它,但我不知道如何解决它是:
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;
};
下面是 Choose
的实现
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;
}
}
派生的例子class
#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);
}
解决该问题的一种方法是在 UserEvents
中定义 virtual
成员函数,并在构建地图时使用它们。
示例:
class UserEvents
{
public:
virtual void MEMDUMP_LOAD(const std::vector<boost::any> &Parameters) = 0;
...
};
然后覆盖 UserEvents_MVN
中的 virtual
函数。
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 );
...
}
更新,回应OP的评论
另一种选择是在地图中使用 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);});
...
}
我在 IRC 中寻求帮助,有人向我指出 CRTP - Curiously Recurring Template Pattern 它允许 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>
{
// ...
};
他们好心地给我发了一个 example
#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");
}
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);
}
完整版
我正在编写一个使用自定义用户函数的离散事件模拟引擎 (DESEngine)。由于这些自定义用户函数 特定于问题,我希望能够在不知道其签名名称的情况下调用它们 。 引擎读取 TURN_ON
在文本文件中查找映射到 unordered_map.
TURN_ON
字符串别名的函数指针
基本上 DESEngine 会调用 UserEvents::Choose(std::string Name)
来调用由它的事件名称(字符串)给出的函数。
一切都很好,但可管理性变得越来越困难,因为我必须使用该引擎解决许多不同的问题(从微分方程求解到模拟简单计算机)。 (我的 UserEvents 中有各种不同的问题特定函数 class)
正因为如此,我决定让我的 class UserEvents
成为一个抽象 class 并且 为我遇到的每一种问题继承它,因此,自定义用户函数将存储在派生 class 中,而不是存储在基类中,但我希望 "Choose"
方法驻留在基类 class 中。 如何从基 class 派生的 classes 中调用函数?
我得到的错误,我明白为什么会得到它,但我不知道如何解决它是:
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;
};
下面是 Choose
的实现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;
}
}
派生的例子class
#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);
}
解决该问题的一种方法是在 UserEvents
中定义 virtual
成员函数,并在构建地图时使用它们。
示例:
class UserEvents
{
public:
virtual void MEMDUMP_LOAD(const std::vector<boost::any> &Parameters) = 0;
...
};
然后覆盖 UserEvents_MVN
中的 virtual
函数。
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 );
...
}
更新,回应OP的评论
另一种选择是在地图中使用 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);});
...
}
我在 IRC 中寻求帮助,有人向我指出 CRTP - Curiously Recurring Template Pattern 它允许 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>
{
// ...
};
他们好心地给我发了一个 example
#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");
}