namespace details { template<template<class...>class Z, class always_void, class...Ts> struct can_apply : std::false_type {}; template<template<class...>class Z, class...Ts> struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type {}; } template<template<class...>class Z, class...Ts> using can_apply = details::can_apply<Z, void, Ts...>;
this takes a template and argument list and tells you if you can apply them.
A template that evaluates foo.get<Bar>() :
template<class ObjectType, class Source> using get_template_result = decltype( std::declval<Source>().get<ObjectType>() );
Can we call the pattern above?
template<class ObjectType, class Source> using can_get_template = can_apply< get_template_result, ObjectType, Source >;
A package for placing a template in a type that allows us to evaluate it:
template<template<class...>class Z> struct z_template { template<class...Ts> using result = Z<Ts...>; };
A similar package that discards its arguments and returns the result always:
template<class Result> struct z_identity { template<class...>using result=Result; };
Evaluates get_template_result , if possible. If so, compares its type with ObjectType . Otherwise, ObjectType* is compared with ObjectType (guaranteed false):
template<class ObjectType, class Source> using get_template_gets_type = std::is_same<ObjectType, typename
As soon as we have it all, we can mark the shipment!
template<class ObjectType, class T0, class...Ts, class Source> ObjectType get_smart( Source&& source, std::true_type ) { return static_cast<T0&&>(std::forward<Source>(source)).get<ObjectType>(); } template<class ObjectType, class T0, class T1, class...Ts, class Source> ObjectType get_smart( Source&& source, std::false_type ) { return get_smart<ObjectType, T1, Ts...>(std::forward<Source>(source), get_template_gets_type<ObjectType, T1>{} ); } template<class ObjectType, class T0, class...Ts, class Source> ObjectType get_smart( Source&& source ) { return get_smart( std::forward<Source>(source), get_template_gets_type<ObjectType, T0>{} ); }
now get_smart<ObjectType, TypeA, TypeB>( something ) will search in the TypeA list, then TypeB until it finds a type that you can call .get<ObjectType>() , and returns ObjectType . Then he stops.
If this type is not found, it will not compile.
You are responsible for setting the r / l value for the type list TypeA TypeB and ObjectType . The list length is limited by the template recursion boundaries (usually 100 s).