black magic
模板是个好东西,以前用的很少,最近拿起Effective C++在看第7章模板的部分,又加上之前玩过Qt,于是有了造一个Signal&Slot的想法.
Qt中要使用Signal&Solt,那么这个对象必须要继承QObject这个类,还需要加上Q_OBJECT
这个宏,今天知道为什么了.下面就说说几个黑科技.
Triats
Trait是模板的一种应用,详见GENERIC <PROGRAMMING> - Andrei Alexandrescu.
这里面的几个例子讲的很清楚,还有两个对立的概念traits template
和traits class
.
类成员函数指针
参考How Qt Signals and Slots Work
class Test
{
public:
void print(int x)
{
std::cout << x << std::endl;
}
};
void (Test::*func)(int); // black magic
int main()
{
func = &Test::print;
Test t1;
Test *t2 = &t1;
(t1.*func)(10);
(t2->*func)(11);
}
SFINAE
参考替换失败不是一个错误
struct T
{
typedef int foo;
};
template<typename T>
void f(typename T::foo)
{
std::cout << "foo\n";
}
template<typename T> // black magic
void f(T)
{
std::cout << "int\n";
}
int main()
{
f<T>(10);
f<int>(11);
}
一个C++11实现的nano-signal-slot作为参考.
最后,先贴上一个回调
#include <iostream>
#include <list>
#include <functional>
template<typename Type>
class Call
{
public:
Call() = default;
Call(const Call&) = delete;
Call& operator=(const Call&) = delete;
~Call()
{
if(!callable.empty())
callable.clear();
}
template<typename F>
typename std::list<Type*>::iterator insert(F&& fp)
{
callable.push_back(fp);
return --callable.end();
}
template<typename... Args>
void emit(Args&&... args)
{
for(auto &x: callable)
x(std::forward<Args>(args)...);
}
template<typename F, typename... Args>
void emit_f(F&& f, Args&&... args)
{
for(auto &x: callable)
f(x(std::forward<Args>(args)...));
}
void remove(typename std::list<Type*>::iterator slot)
{
callable.erase(slot);
}
void remove(Type* slot)
{
for(auto iter = callable.begin(); iter != callable.end(); )
{
if(slot == *iter)
iter = callable.erase(iter);
else
++iter;
}
}
private:
std::list<Type*> callable;
};
int add(const int& lhs, const int& rhs, int& res)
{
return (res = lhs + rhs);
}
int multi(const int& lhs, const int& rhs, int& res)
{
return (res = lhs * rhs);
}
int main()
{
Call<decltype(add)> call;
call.insert(add);
int res = 0;
call.emit(10, 20, res);
std::cout << res << std::endl;
call.insert([](const int& lhs, const int& rhs, int& res) {
return (res = lhs - rhs);
});
auto print = [](int x) { std::cout << x << std::endl; };
call.remove(add);
auto iter = call.insert(multi);
call.emit_f(print, 10, 20, res);
call.remove(iter);
call.emit_f(print, 10, 20, res);
}
2016-05-12 16:03 填坑
昨天知道了Trait这个东西,加上近段时间想了解下Signal和Slot机制.于是,昨晚上加上今天下午花了些时间在弄这个东西.不管怎么样,东拼西凑的弄出来了,能用,代码放在GitHub.
使用了几个偏特化的结构体,默认connect
和disconnect
不受互斥量保护,但emit
有互斥量保护.
template<typename T>
struct Trait;
// specialization
template<typename Obj, typename Res, typename... Args>
struct Trait<Res (Obj::*)(Args...)>
{
typedef Obj Object;
typedef Res (Obj::*Func)(Args...);
enum { is_member_function = true , is_mt_safe = true };
static void call(Func f, Obj *o, Args&&... args)
{
(o->*f)(std::forward<Args>(args)...);
}
};
template<typename Res, typename... Args>
struct Trait<Res (*)(Args...)>
{
enum { is_member_function = false, is_mt_safe = true };
typedef Res (*Func)(Args...);
typedef Res Object;
static void call(Func f, Object *, Args&&... args)
{
(*f)(std::forward<Args>(args)...);
}
};
测试代码如下
int main()
{
Test t;
Signal<decltype(&Test::p)> s;
s.connect(&t, &Test::p);
s.connect(&t, &Test::show);
s.emit(10);
s.disconnect(&Test::show);
s.emit(10);
Signal<int(*)(int)> s1;
auto handle = s1.connect([](int x) -> int
{
std::cout << x << std::endl;
return x;
});
s1.emit(11);
s1.disconnect(handle);
s1.emit(12);
}
结果
╭─criss at innisfree in ~ using
╰─○ gg -Wunused -Wall trait_ng.cpp
╭─criss at innisfree in ~ using
╰─○ ./a.out
10
show => 10
10
11
╭─criss at innisfree in ~ using
╰─○
存在的问题:
- 使用成员函数时,当类对象被销毁后再调用emit,就会出现未定义行为
- 实现太烂,看不下去了 (其实还没有仔细找问题...)