#include <string>
#include <chrono>
#include <iostream>
#include <system_error>
#include <cerrno>
#include <signal.h>

constexpr unsigned int iter_count = 1000000;

template<typename F>
void bench(std::string name,
	   F func,
	   unsigned int iterations = iter_count){
  using namespace std::chrono;
  
  std::cout<<"Benchmarking \""
	   <<name<<"\" with "<<iterations<<" iterations"
	   <<std::endl;
  
  auto begin = steady_clock::now();
  
  for(unsigned int i = 0; i != iterations; i++)
    func();
  
  auto end = steady_clock::now();
  auto elapsed = duration_cast<milliseconds>(end - begin).count();
  
  std::cout<<elapsed<<" miliseconds to run \""<<name<<"\""<<std::endl;
}

int failing_func(){
  sigset_t sig;
  return sigprocmask(-1, &sig, nullptr);
}

int success_func(){
  return sigprocmask(0, nullptr, nullptr);
}

void with_exception_oftenfail(){
  int res = failing_func();
  if(res == -1) throw std::system_error(errno, std::system_category());
}

int with_error_code_oftenfail(){
  return failing_func();
}

void with_exception_rarefail(){
  int res = success_func();
  if(res == -1) throw std::system_error(errno, std::system_category());
}

int with_error_code_rarefail(){
  return success_func();
}

int main(){
  unsigned int black_hole = 0;
  bench("Exceptions (often error)", [&black_hole](){
      try{
	with_exception_oftenfail();
      } catch(std::exception& e){
	black_hole++;
      }
    });

  bench("Error codes (often error)", [&black_hole](){
      int res = with_error_code_oftenfail();
      if(res == -1) black_hole--;
    });

  bench("Exceptions (rare error)", [&black_hole](){
      try{
	with_exception_rarefail();
      } catch(std::exception& e){
	black_hole++;
      }
    });

  bench("Error codes (rare error)", [&black_hole](){
      int res = with_error_code_rarefail();
      if(res == -1) black_hole--;
    });
  
  //To prevent loop optimize-out  
  std::cout<<"Blackhole is:"<<black_hole<<std::endl;
}
