#include <iostream>
#include <sstream>
#include <typeinfo>
#include <cstdlib>
#include <cxxabi.h>
/* general type-list */
template<typename ... Types>
struct type_list { typedef type_list type; };
namespace detail {
std::string demangle(char const * const name) {
int st;
char * const p = abi::__cxa_demangle(name, 0, 0, &st);
if(st != 0) return "<demangle error>";
std::string s(p);
std::free(p);
return s;
}
/* remove lval reference */
template<typename T>
struct remove_lref { typedef T type; };
template<typename T>
struct remove_lref<T&> { typedef T type; };
/* represents a candidate set */
template<typename ... FunctionTypes>
struct candidate_set;
template<typename R, typename ... Args, typename ... FunctionTypes>
struct candidate_set<R(Args...), FunctionTypes...>
: candidate_set<FunctionTypes...> {
typedef R result_type(Args...);
using candidate_set<FunctionTypes...>::call;
static result_type &call(Args...);
};
template<typename R, typename ... Args>
struct candidate_set<R(Args...)> {
typedef R result_type(Args...);
static result_type &&call(Args...);
};
template<> struct candidate_set<> { };
/* return rvalue for T, T&&
return lvalue for T& */
template<typename T>
T &&make();
/* prepend type... */
template<typename Type, typename List>
struct prepend_type;
template<typename Type, typename ... Types>
struct prepend_type<Type, type_list<Types...>> {
typedef type_list<Type, Types...> type;
};
/* append type... */
template<typename Type, typename List>
struct append_type;
template<typename Type, typename ... Types>
struct append_type<Type, type_list<Types...>> {
typedef type_list<Types..., Type> type;
};
/* remove_type ... */
template<typename Remove, typename List>
struct remove_type;
template<typename Remove, typename ... Tail>
struct remove_type<Remove, type_list<Remove, Tail...> >
:remove_type<Remove, type_list<Tail...> >
{ };
template<typename Remove, typename Head, typename ... Tail>
struct remove_type<Remove, type_list<Head, Tail...>> {
typedef typename prepend_type<Head,
typename remove_type<Remove, type_list<Tail...>>::type>::type type;
};
template<typename Remove>
struct remove_type<Remove, type_list<>> {
typedef type_list<> type;
};
/* returns void if the call to the candidate set fails, and the selected
* function type (lvalue ref to it) otherwise. */
template<typename ... Args, typename ... Candidates>
decltype(candidate_set<Candidates...>::call(make<Args>()...))
check_ambiguity(type_list<Args...>, type_list<Candidates...>);
void check_ambiguity(...);
/* implementation of order_candidates */
template<typename Args, typename FunctionTypes, typename Order, typename LastCalled = char>
struct order_candidates_impl {
static void function_call_suppression();
typedef typename remove_lref<
decltype(function_call_suppression(),
check_ambiguity(
make<Args>(),
make<FunctionTypes>()))>::type winner_type;
typedef order_candidates_impl<
Args,
typename remove_type<winner_type, FunctionTypes>::type,
typename append_type<winner_type, Order>::type, winner_type> next;
typedef typename next::type type;
typedef typename next::remaining_types remaining_types;
};
template<typename Args, typename FunctionTypes, typename Order>
struct order_candidates_impl<Args, FunctionTypes, Order, void> {
typedef typename remove_type<void, Order>::type type;
typedef FunctionTypes remaining_types;
};
/* get nice type string... */
template<typename T>
inline std::string get_type_name() {
return demangle(typeid(T).name());
}
} // details::
template<typename Args, typename FunctionTypes>
struct order_candidates
: detail::order_candidates_impl<Args, FunctionTypes, type_list<>>
{ };
template<typename List>
struct type_list2string;
template<typename Type, typename ... Types>
struct type_list2string<type_list<Type, Types...> > {
static void print(std::ostream &os) {
os << detail::get_type_name<Type>();
if(sizeof...(Types) != 0)
os << ", ";
type_list2string<type_list<Types...>>::print(os);
}
};
template<>
struct type_list2string<type_list<> > {
static void print(std::ostream &os) { }
};
template<typename ... FunctionTypes, typename ... T>
inline std::string call_candidate_set(T &&...) {
typedef order_candidates<type_list<T...>, type_list<FunctionTypes...>>
candidates_order;
std::ostringstream order;
order << " [ ";
type_list2string<typename candidates_order::type>::print(order);
order << " ] [ ";
type_list2string<typename candidates_order::remaining_types>::print(order);
order << " ]";
return order.str();
}
// TEST!
#include <iostream>
#include <cstddef>
struct Base { Base() { } Base(int) { } };
struct Derived : Base {
Derived() { }
Derived(int) { }
operator short();
};
int main() {
int lvalue_int = 0;
std::string results[] = {
// void(int), void(bool)
call_candidate_set<void(int), void(bool)>('#'),
// void(int)
call_candidate_set<void(int), void(bool), void(short)>('#'),
// void(Derived), void(Base), void(int)
call_candidate_set<void(Base), void(Derived), void(int)>(Derived()),
// void(char const*), void(string)
call_candidate_set<void(std::string), void(char const*)>("haha"),
// void(int)
call_candidate_set<void(int), void(int&)>(10),
// <empty>
call_candidate_set<void(int), void(int const&)>(10),
// void(int&&), void(int const&)
call_candidate_set<void(int const&), void(int&&)>(10),
// void(int const&)
call_candidate_set<void(int const&), void(int&&)>(lvalue_int),
// void(int&), void(int const&)
call_candidate_set<void(int&), void(int const&)>(lvalue_int),
// <empty>
call_candidate_set<void(char const(&)[3]), void(char const*)>("12"),
// void(char), void(int), void(long)
call_candidate_set<void(char), void(int), void(long), void(Base), void(Derived)>('#'),
call_candidate_set<void(int&&), void(int const&)>(10),
call_candidate_set<void(double), void(long double), void(float), void(int)>(1.0f)
};
for(std::size_t i = 0; i < sizeof results / sizeof *results; i++)
std::cout << results[i] << std::endl;
}