#include <iostream>
#include <tuple>
#include <functional>
#include <string>
#include <sstream>
using namespace std;
/**************************
* Filler code.....
* ************************/
template <typename T>
struct Rand;
template <>
struct Rand<int> { static inline int get(int i) { return 100 + i; } };
template <>
struct Rand<std::string>
{
static inline std::string get(int i)
{
std::stringstream ss;
ss << "Text (" << i << ")";
return ss.str();
}
};
template <>
struct Rand<double> { static inline double get(int i) { return (float)i + 0.001; } };
/**************************
* Generic helper TMP stuff
* ************************/
template <size_t...>
struct index_sequence {};
template <size_t Int1, size_t... Intn>
struct index_sequence_generator : index_sequence_generator<Int1-1, Int1-1, Intn...>{};
template <size_t... Intn>
struct index_sequence_generator<0, Intn...>
{
typedef index_sequence<Intn...> type;
};
template <size_t N>
using make_index_sequence = typename index_sequence_generator<N>::type;
template <typename... Args>
using index_sequence_for = make_index_sequence<sizeof...(Args)>;
template<typename...>
struct type_sequence {};
template <typename T>
class has_operator_func
{
template <typename C> static char test( decltype(&C::operator()) ) ;
template <typename C> static long test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
template <typename T>
struct function_type_forced
{
static_assert(has_operator_func<T>::value,
"function_traits may only act on objects with operator() or defined functions");
typedef decltype(&T::operator()) type;
};
template <typename T>
using force_function_type = typename function_type_forced<T>::type;
template <typename T>
struct function_traits : public function_traits<force_function_type<T>> {};
template <typename R, typename... Args>
struct function_traits_base
{
typedef R result_type;
enum { arg_num = sizeof...(Args) };
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
};
typedef type_sequence<Args...> arg_types;
};
template <typename C, typename R, typename... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits_base<R, Args...> {};
template <typename C, typename R, typename... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits_base<R, Args...> {};
template <typename R, typename... Args>
struct function_traits<R(Args...)> : public function_traits_base<R, Args...> {};
template <typename F>
using function_arg_sequence = typename function_traits<F>::arg_types;
/**************************
* Stuff to call the function invariably
* ************************/
template<typename F>
struct func_caller
{
static_assert(has_operator_func<F>::value || std::is_function<F>::value,
"Only functions, functors, etc. may be used with 'func_caller'");
typedef typename function_traits<F>::result_type result_type;
template<typename... Args, size_t... Ints>
static inline result_type real_call(const std::function<result_type(Args...)>& func, index_sequence<Ints...>)
{
return func(Rand<Args>::get(Ints)...);
}
template<typename... Args>
static inline result_type call_typed(const F& func, type_sequence<Args...>)
{
return real_call(std::function<result_type(Args...)>(func), index_sequence_for<Args...>{});
}
static inline result_type call(const F& func)
{
return call_typed(func, function_arg_sequence<F>{});
}
};
template <typename R, typename... Args>
struct func_caller<R(Args...)>
{
typedef R result_type;
template<size_t... Ints>
static inline R real_call(const std::function<R(Args...)>& func, index_sequence<Ints...>)
{
return func(Rand<Args>::get(Ints)...);
}
template <typename F>
static inline R call_typed(const F& func)
{
return real_call(std::function<R(Args...)>(func), index_sequence_for<Args...>{});
}
static inline R call(R (*func)(Args...))
{
return call_typed(func);
}
};
/* In a real world use, something like this might be useful
instead of calling func_caller immediately, so as to exit the compile easier...
In this example, on ideone.com, you still get pretty gnarly compile errors, though
it should be obvious what the problem is early on ;)
template <typename F>
struct call_func_proof
{
static_assert(has_operator_func<F>::value || std::is_function<F>::value,
"Only functions, functors, etc. may be passed to 'call_func'");
static inline auto call_func(const F& func) ->
decltype(func_caller<F>::call(func))
{
return func_caller<F>::call(func);
}
};
*/
template <typename F>
static inline auto call_func(const F& func) -> decltype(func_caller<F>::call(func))
{
return func_caller<F>::call(func);
}
template <typename FType, typename F>
static inline auto call_func_t(const F& func) -> decltype(func_caller<FType>::call_typed(func))
{
return func_caller<FType>::call_typed(func);
}
/**************************
* And example using it
* ************************/
void test_func(int i1, double d1, int i2, std::string str1, std::string str2, double d2)
{
std::cout << i1 << ", " << d1 << ", " << i2 << ", " << str1 << ", " << str2 << ", " << d2 << std::endl;
}
struct test_obj
{
void operator()(double d1, std::string str1, double d2, double d3, int i, std::string str2, double d4)
{
std::cout << d1 << ", " << str1 << ", " << d2 << ", " << d3 << ", " << i << ", " << str2 << ", " << d4 << std::endl;
}
void operator()()
{
std::cout << "O.o" << std::endl;
} // This throws an error with call_func, because the overloaded () can't be resolved. call_func_t is required
};
int main() {
call_func(
[](int i, std::string str1, std::string str2, double d)
{
std::cout << i << ", " << str1 << ", " << str2 << ", " << d << std::endl;
});
std::string stated_str = "My str";
call_func(
[&](int i, double d, std::string s)
{
std::cout << stated_str << ", " << i << ", " << d << ", " << s << std::endl;
});
call_func(test_func);
//call_func(test_obj{});
call_func_t<void(double, std::string, double, double, int, std::string, double)>(test_obj{});
using namespace std::placeholders;
call_func(std::function<void(double, int, std::string, double)>(
std::bind(test_func, 634, _1, _2, "This one's messed up", _3, _4)));
//compile error, with some fluff, but nicely does say
//Only functions, functors, etc. may be used with 'func_caller'
//Further down in the compile errors:
//"function_traits may only on objecst with operator() or defined functions"
//and:
//'operator()' is not a member of 'bool'
//call_func(true);
return 0;
}