black magic


模板是个好东西,以前用的很少,最近拿起Effective C++在看第7章模板的部分,又加上之前玩过Qt,于是有了造一个Signal&Slot的想法.

Qt中要使用Signal&Solt,那么这个对象必须要继承QObject这个类,还需要加上Q_OBJECT这个宏,今天知道为什么了.下面就说说几个黑科技.

Triats
Trait是模板的一种应用,详见GENERIC <PROGRAMMING> - Andrei Alexandrescu
这里面的几个例子讲的很清楚,还有两个对立的概念traits templatetraits 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

使用了几个偏特化的结构体,默认connectdisconnect不受互斥量保护,但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,就会出现未定义行为
  • 实现太烂,看不下去了 (其实还没有仔细找问题...)


转载请注明:Serenity » black magic