具有不同参数列表的模板对象的模板静态映射
A template static map of template objects with different parameter lists
我正在询问如何创建不同类型对象的模板映射,观察者示例只是描述问题的一种方便方式。
因此,我有一个观察者模式的简单实现:
template<typename ...A> class Observable
{
typedef void(*EventListener)(A...); /* Listener callback type */
public:
void addListener(EventListener listener);
void removeListener(EventListener listener);
void fireEvent(A... args);
};
在我的代码中有不同的事件集,每个事件集都由enum表示,即:
enum PCEvents {
JUMPED = 0, // void (*fn)(int, int)
WALKED = 1, // void (*fn)(int)
DIED = 2, // void (*fn)()
DIED_HORRIBLY = 3 // void (*fn)(const std::string&)
};
现在我想要一个伞形类,它封装了基于枚举的所有Observable。我不知道它是如何实现的,所以这里是它想要的实例化:
void gameOver(const std::string& deathCause)
{
std::cout << "You perished because of " << deathCause << std::endl;
}
int main()
{
// parameter list syntax is lax
PoorGuyObserver<PCEvents, void (*)(int, int), void (*)(int), void (*)(), void (*)(const std::string&)> observer;
observer.addListener(PCEvents::DIED_HORRIBLY, gameOver);
observer.fireEvent(PCEvents::DIED_HORRIBLY, "gazeebo");
}
我对c++14的解决方案很感兴趣。
我对您的问题的解释是,您希望将可观察描述为枚举值到函数签名的映射,并使用可变模板来生成它。
可变模板的一个棘手之处是,不能将常数值与类型名混合使用。尾随位必须是所有类型名称(typename...
(或所有常量(auto...
(。因此,我的第一步是制作一个助手类型:
template <typename TEnum, TEnum Id, typename... TArgs>
struct Event;
注意:如果您使用的是c++17,您可以将前两个参数组合为auto Id
,但您指定了c++14
下一步:为单个Event
实现可观测。通常,这将是递归变量模板的一部分,但这是我稍后将描述的一个棘手问题所需要的。
template <typename TEnum, TEnum Id, typename... TArgs>
struct ObservableImpl {
std::vector<std::function<void(TArgs...)>> subscribers;
template <TEnum FireId, typename = std::enable_if_t<FireId == Id>>
void fire(TArgs... args) {
for (auto subscriber : subscribers) {
subscriber(args...);
}
}
template <TEnum SubscribeId, typename = std::enable_if_t<SubscribeId == Id>>
void subscribe(std::function<void (TArgs...)> handler) {
subscribers.push_back(handler);
}
};
然后是递归部分。每个继承层继承自单个事件实现,并提升fire
和subscribe
。
template <typename... TEvents>
struct Observable;
// Recursive case, implement the current event, inherit from the next
template <typename TEnum, TEnum Id, typename... TArgs, typename... TEvents>
struct Observable<Event<TEnum, Id, TArgs...>, TEvents...> : public ObservableImpl<TEnum, Id, TArgs...>, Observable<TEvents...> {
using Observable<TEvents...>::subscribe;
using Observable<TEvents...>::fire;
using ObservableImpl<TEnum, Id, TArgs...>::subscribe;
using ObservableImpl<TEnum, Id, TArgs...>::fire;
};
// Terminal case, implement the last event
template <typename TEnum, TEnum Id, typename... TArgs>
struct Observable<Event<TEnum, Id, TArgs...>> : public ObservableImpl<TEnum, Id, TArgs...> {
using ObservableImpl<TEnum, Id, TArgs...>::subscribe;
using ObservableImpl<TEnum, Id, TArgs...>::fire;
};
在许多示例中,终止专门化是空的。但在我们的情况下,因为递归专门化促进了一个基方法,所以终止情况必须具有该基方法。这就是为什么我将单个事件实现拉到第二个类中,以节省一些类型。
之后,它就可以使用了:
enum class Events {
JUMPED = 0, // void (*fn)(int, int)
WALKED = 1, // void (*fn)(int)
DIED = 2, // void (*fn)()
DIED_HORRIBLY = 3 // void (*fn)(const std::string&)
};
int main(int argc, char** argv) {
Observable<
Event<Events, Events::JUMPED, int, int>,
Event<Events, Events::WALKED, int>,
Event<Events, Events::DIED>,
Event<Events, Events::DIED_HORRIBLY, std::string>> observable;
observable.subscribe<Events::JUMPED>([](int x, int y) { std::cout << "jumped(" << x << ", " << y << ")n"; });
observable.subscribe<Events::WALKED>([](int distance) { std::cout << "walked(" << distance << ")n"; });
observable.subscribe<Events::DIED>([]() { std::cout << "died()n"; });
observable.subscribe<Events::DIED_HORRIBLY>([](std::string how) { std::cout << "died_horribly(" << how << ")n"; });
observable.fire<Events::JUMPED>(1, 2);
observable.fire<Events::WALKED>(42);
observable.fire<Events::DIED>();
observable.fire<Events::DIED_HORRIBLY>("fire");
}
https://godbolt.org/z/VhvGPg
相关文章:
- 初始化之前使用的静态映射
- 值和类型的简洁双向静态 1:1 映射
- 如果包含映射的静态库与可执行文件和动态库链接,静态映射(变量)是否会被多次释放?
- 线程时访问静态映射时出现隔离错误
- 具有不同参数列表的模板对象的模板静态映射
- 静态映射超出堆栈
- C++ 已停止工作静态映射函数
- 我们如何在模板类中使用静态映射,模板类的每个实例化都应该使用相同的映射
- 如何从 C++ 中的单独类访问静态映射?
- 在 C++ 中使用静态映射的奇怪问题
- C++初始化对象的静态映射
- 使用静态映射在C++中缓存数据的推荐方法是什么
- C++中的静态映射
- 静态映射中动态分配的对象.删除必要的
- 静态映射初始化
- (VS2015)正在尝试用初始值设定项列表中的数据填充静态映射
- C++模板静态映射初始化
- 检查 C++ 中的空静态映射变量
- 静态映射为C++中的类成员
- 在不同函数内的类中使用静态映射: