#include <unordered_map>
#include <string>
#include <utility>
#include <typeinfo>
#include <stdexcept>
#include <iostream>

struct BeliefCondFunc
{
    using cb_type = bool(*)();
    using map_val = std::pair<cb_type, std::type_info const*>;
    static std::unordered_map<std::string, map_val> const FuncMap;
    
    static bool Greater(int A, int B)
    {
        return A > B;
    }

    static bool Between(float A, float B, float C)
    {
        return A > B && A < C;
    }
    
    template<typename ...Args>
    static map_val make_map_val(bool (*func)(Args...)) {
    	
    	return std::make_pair(reinterpret_cast<cb_type>(func), 
    	                      &typeid(decltype(func)));
    }
    
    template<typename ...Args>
    static bool call(std::string const& key, Args&&... args){
      using prototype = bool(*)(Args...);
      
      auto& func_n_typeid = FuncMap.at(key);
      
      if (typeid(prototype) != *func_n_typeid.second )
        throw std::domain_error("Prototype mismatch");
      
      return reinterpret_cast<prototype>(func_n_typeid.first)(std::forward<Args>(args)...);
    };
};

std::unordered_map<std::string, BeliefCondFunc::map_val> const BeliefCondFunc::FuncMap {
  {"Greater", make_map_val(&BeliefCondFunc::Greater) },
  {"Between", make_map_val(&BeliefCondFunc::Between) }
};

int main() {
	BeliefCondFunc::call("Greater", 1, 2);
	
	try {
		BeliefCondFunc::call("Lesser", 1, 2);
	} catch (std::out_of_range&) {
		std::cout << "No such function\n";
	}
	
	try {
		BeliefCondFunc::call("Between", 1, 2);
	} catch (std::domain_error&) {
		std::cout << "Wrong number of arguments\n";
	}
	
	return 0;
}