编译期测试类

前面有几个note说过模板类的偏特化,但是那几个只是简单的利用偏特化的类模板来完成编译器的“if-else”操作,后来又比较深入的了解了下模板元编程(工具书Modern C++ Design),特别是Loki库,看得走火入魔。。。当然也跟着敲了些代码,结果就是把之前刚接触traits时写的signal模板类更新了两个版本,同时也发现些有趣的东西:编译期assertion和编译期函数返类型获取。当然,编译期的东西,大多数时候都是在玩类型。。。

这两个有趣的东西,是C++11开始引入的,所以之前都是自己动手完成。

static_assert 用法很简单

//static_assert(bool constexpr, message);
static_assert(false, "fail"); // 在编译时输出“fail”并结束编译

贴个编译期判断指针的代码

#include <iostream>
#include <vector>

template<typename T>
class type_trait
{
  private:
    template<typename U> struct ptr
    {
      typedef U ptr_type;
      enum { is_ptr = false, is_mptr = false };
    };
    template<typename U> struct ptr<U*>
    {
      typedef U ptr_type;
      enum { is_ptr = true, is_mptr = false };
    };
    template<typename U, typename V>
    struct ptr<U V::*>
    {
      typedef U ptr_type;
      enum { is_ptr = true, is_mptr = true };
    };
  public:
    enum { is_ptr = ptr<T>::is_ptr, is_mptr = ptr<T>::is_mptr };
    typedef typename ptr<T>::ptr_type pointer_type;
};

struct T
{
  void null(){};
};

int main()
{
  const bool iter = type_trait<std::vector<int>::iterator>::is_ptr;
  std::cout << std::boolalpha << iter << std::endl;
  const bool int_ptr = type_trait<int*>::is_ptr;
  std::cout << int_ptr << std::endl;
  const bool is_mptr = type_trait<void (T::*)()>::is_mptr;
  std::cout << is_mptr << std::endl;
}

result_of 用法也很简单

// template<typename> struct result_of;
// template<typename F, typename... Args>
// struct result_of<F(Args...)>;
// 例子
#include <iostream>
#include <functional>

template<typename F, typename... Args>
auto func(F&& f, Args&&... args)
-> std::function<typename std::result_of<F(Args...)>::type()>
{
  return std::bind(std::forward<F>(f), std::forward<Args>(args)...);
}

int main()
{
  auto f = [](int x, const char* s) -> const char*
  {
    std::cout << x << s << std::endl;
    return s;
  };
  auto fp = func(f, 1, "test");
  std::cout << fp() << std::endl;
}

下面是运行结果

./a.out   
1test
test

很明显,result_of获得了f的返回类型,并且使用这个类型构成了函数对象std::function<const char*()>
当然,如果你不需传入函数的返回值的话就没有必要使用result_of了。

下面是一个复杂点儿的例子

/*********************************************************
          File Name:task.cpp
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Wed 03 Aug 2016 03:46:54 PM CST
**********************************************************/

#include <iostream>
#include <functional>
#include <future>
#include <thread>
#include <queue>

using namespace std;

class Task
{
  public:
    Task(std::size_t threads = thread::hardware_concurrency())
    {
      cout << "threads: " << threads << endl;
      for(std::size_t i = 0; i < threads; ++i)
      {
        workers.emplace_back([this]
            {
              function<void()> task;
              for(;;)
              {
                {
                  unique_lock<mutex> l(this->task_lock);
                  task_condtion.wait(l, [this]{ return is_exit || !tasks.empty(); });
                  if(is_exit && tasks.empty())
                    return;
                  task = move(tasks.front());
                  tasks.pop();
                }
                task(); // processing task
              }
            }
          );
      }
    }
    ~Task()
    {
      {
        unique_lock<mutex> l(task_lock);
        is_exit = true;
        task_condtion.notify_all();
      }
      for(auto& x: workers)
        x.join();
      cout << "task finished.\n";
    }
    template<typename F, typename... Args>
    auto add_task(F&& f, Args&&... args)
    -> future<typename result_of<F(Args...)>::type>
    {
      typedef typename result_of<F(Args...)>::type R;
      // make a callable wrapper, using shared_ptr to share task with
      // task queue, otherwise, move local variable out of scope may
      // cause segfault.
      auto task = make_shared<packaged_task<R()>>(bind(forward<F>(f), forward<Args>(args)...));
      auto res = task->get_future();
      unique_lock<mutex> l(task_lock);
      tasks.emplace([task]() { (*task)(); });
      return res;
    }
    void start()
    {
      unique_lock<mutex> l(task_lock);
      task_condtion.notify_all();
    }
  private:
    vector<thread> workers;
    mutex task_lock;
    condition_variable task_condtion, wait_condition;
    bool is_exit = false;
    queue<function<void()>> tasks;
};

int main()
{
  auto f = [](int x) -> int { return x * 2; };
  std::future<int> res1, res2, res3, res4, res5;
  {
    Task task;
    res1 = task.add_task(f, 1);
    res2 = task.add_task(f, 2);
    res3 = task.add_task(f, 3);
    res4 = task.add_task(f, 4);
    res5 = task.add_task(f, 5);
    task.start();
  }
  cout << (res1.get() + res2.get() + res3.get() + res4.get() + res5.get()) << endl;
}

结果如下

./a.out 
threas: 4
task finished.
30

很明显,res*.get()的返回类型是通过result_of推断出来的。当然,这个例子可能会稍微修改下变成真正的线程池。

最后,这些编译期测试的类多数在type_traits头文件中。


2016-08-04更新

好吧,还是写了个线程池