#include <map>
#include <functional>
template<typename... Args>
class Listner {
public:
template<typename T>
void attachListner(T *t) {
m_listenerCallbacks.insert(std::make_pair(reinterpret_cast<void*>(t), [&t](Args... args){t->callback(args...); }));
}
template<typename T>
void detachListner(T *t) {
m_listenerCallbacks.erase(reinterpret_cast<void*>(t));
}
void notify(Args... args) {
for (auto l : m_listenerCallbacks)
l.second(args...);
}
private:
std::map<void*, std::function<void(Args...)>> m_listenerCallbacks;
};
使用上面的Listner
类,我可以注册具有名为callback
的公共方法的对象。是否可以通过方法名称(Listner
)也可以作为模板参数传递的方式来概括callback
类。
答案 0 :(得分:1)
以下是基于可能适合您的示例代码的可能解决方案:
#include <map>
#include <functional>
template<typename>
struct Listner;
template<typename R, typename... Args>
struct Listner<R(Args...)> {
template<typename T, R(T::*M)(Args...) = &T::callback>
void attachListner(T *t) {
m_listenerCallbacks.insert(std::make_pair(static_cast<void*>(t), [t](Args... args){ (t->*M)(args...); }));
}
void notify(Args... args) {
for (auto l : m_listenerCallbacks)
l.second(args...);
}
private:
std::map<void*, std::function<void(Args...)>> m_listenerCallbacks;
};
struct A {
void f(int, char) {}
void f(int, double) {}
void f(int) {}
};
int main() {
A a;
Listner<void(int, char)> l;
l.attachListner<A, &A::f>(&a);
l.notify(0, 'c');
}
基本思想是你必须完全指定函数类型(参见Listner<void(int, char)>
)。唯一的参数列表是不够的
我们可以通过类模板的部分特化来实现:
template<typename>
struct Listner;
template<typename R, typename... Args>
struct Listner<R(Args...)> {
// ...
};
完成后,我们可以使用该类型将正确的重载检测为模板参数:
template<typename T, R(T::*M)(Args...) = &T::callback>
void attachListner(T *t) {
// ...
}
请注意,我设置了默认值(&T::callback
),以便您可以将其作为:
l.attachListner<A, &A::f>(&a);
如果您想使用其他成员函数,或者:
l.attachListner(&a);
如果A
有一个名为callback
的成员方法,并且您希望像以前那样使用它。
答案 1 :(得分:0)
首先:我强烈建议按值传递t
指针,而不是通过引用lambda函数;所以
m_listenerCallbacks.insert(
std::make_pair(reinterpret_cast<void*>(t),i
[=](Args... args){t->callback(args...); }));
// ----------------^ (not &)
总之...
是否可以通过方法名称(回调)也可以作为模板参数传递的方式来推广Listner类。
作为attachListner()
方法的类的模板参数?
在第一种情况下,我能想象的最好的(?)是一个基于C-macro的脏解决方案(我知道...... C-macro是邪恶的...但有时......)。
在以下示例中,假设您希望Listner
在所有注册的对象中调用bar()
方法;你必须按如下方式调用Listner
宏
Listner(bar);
这将创建一个Listner_bar
模板类,可以按如下方式使用
Listner_bar<int, std::string const &> l;
以下是一个完整的工作示例
#include <map>
#include <iostream>
#include <functional>
#define Listner(foo) \
\
template <typename ... Args> \
class Listner_##foo \
{ \
public: \
template <typename T> \
void attachListner (T * t) \
{ m_listenerCallbacks[reinterpret_cast<void*>(t)] \
= [=](Args... args){(t->*(&T::foo))(args...);}; } \
\
template <typename T> \
void detachListner (T * t) \
{ m_listenerCallbacks.erase(reinterpret_cast<void*>(t)); } \
\
void notify (Args ... args) \
{ for (auto const & l : m_listenerCallbacks) l.second(args...); } \
\
private: \
std::map<void*, std::function<void(Args...)>> m_listenerCallbacks; \
}
struct A
{
void bar (int i, std::string const & s)
{ std::cout << "A::bar - " << i << ", " << s << std::endl; }
};
struct B
{
void bar (int i, std::string const & s)
{ std::cout << "B::bar - " << i << ", " << s << std::endl; }
};
Listner(bar);
int main ()
{
Listner_bar<int, std::string const &> l;
A a;
B b;
l.attachListner(&a);
l.attachListner(&b);
l.notify(1, "one");
l.detachListner(&a);
l.notify(2, "two");
}
如果你想将方法名称作为模板参数传递给attachListner()
(因此不同对象的名称不同)...我猜想不是你想要的,但我能想到的最好的是传递指针方法到attachListner()
(没有C风格的宏,这次);
l.attachListner(&a, &A::foo);
其中a
是A
类型的对象,您希望将其称为foo()
方法。
以下是一个完整的工作示例
#include <map>
#include <iostream>
#include <functional>
template <typename ... Args>
class Listner
{
public:
template <typename T, typename M>
void attachListner (T * t, M m)
{ m_listenerCallbacks[reinterpret_cast<void*>(t)]
= [=](Args... args){(t->*m)(args...);}; }
template <typename T>
void detachListner (T * t)
{ m_listenerCallbacks.erase(reinterpret_cast<void*>(t)); }
void notify (Args ... args)
{ for (auto const & l : m_listenerCallbacks) l.second(args...); }
private:
std::map<void*, std::function<void(Args...)>> m_listenerCallbacks;
};
struct A
{
void foo (int i, std::string const & s)
{ std::cout << "A::foo - " << i << ", " << s << std::endl; }
};
struct B
{
void bar (int i, std::string const & s)
{ std::cout << "B::bar - " << i << ", " << s << std::endl; }
};
int main ()
{
Listner<int, std::string const &> l;
A a;
B b;
l.attachListner(&a, &A::foo);
l.attachListner(&b, &B::bar);
l.notify(1, "one");
l.detachListner(&a);
l.notify(2, "two");
}
- 编辑 - 正如skypjack所指出的,如果调用的方法没有超载,则第二个解决方案会起作用。 skypjack的答案提出了解决这个问题的方法。