Given a parameter pack with variadic arguments, how can one find the number of unique values in the pack. I am looking for something along the lines of
no_of_uniques<0,1,2,1,2,2>::value // should return 3
My rudimentary implementation looks something this
template <size_t ... all>
struct no_of_uniques;
// this specialisation exceeds -ftemplate-depth as it has no terminating condition
template <size_t one, size_t ... all>
struct no_of_uniques<one,all...> {
static const size_t value = no_of_uniques<one,all...>::value;
};
template <size_t one, size_t two, size_t three>
struct no_of_uniques<one,two,three> {
static const size_t value = (one==two && one==three && two==three) ? 1:
(one!=two && two==three) ? 2:
(one==two && one!=three) ? 2:
(one==three && two!=three) ? 2: 3;
};
template <size_t one, size_t two>
struct no_of_uniques<one,two> {
static const size_t value = one==two ? 1: 2;
};
template <size_t one>
struct no_of_uniques<one> {
static const size_t value = 1;
};
Here, I have specialised for up to three arguments but understandably the code grows exponentially with the number of arguments. I would like to have a meta solution for this using solely STL and no third party libraries like Boost.MPL.
A similar question albeit in the context of checking unique types, rather than finding number of unique values of parameter pack can be found here:
Check variadic templates parameters for uniqueness
In the process of finding the number of unique values of a parameter pack, we might need to sort the pack first, and an excellent implementation of that is provided in this other question
Quick sort at compilation time using C++11 variadic templates
Here's a simple O(n^2) way to do it
template <size_t...>
struct is_unique : std::integral_constant<bool, true> {};
template <size_t T, size_t U, size_t... VV>
struct is_unique<T, U, VV...> : std::integral_constant<bool, T != U && is_unique<T, VV...>::value> {};
template <size_t...>
struct no_unique : std::integral_constant<size_t, 0> {};
template <size_t T, size_t... UU>
struct no_unique<T, UU...> : std::integral_constant<size_t, is_unique<T, UU...>::value + no_unique<UU...>::value> {};
So using your example:
no_unique<0, 1, 2, 1, 2, 2>::value; // gives 3
Most of this is machinery I already wrote for a different question, stripped of the "counting" part.
A pack with a sizeof shortcut:
template<class... Ts> struct pack {
static constexpr size_t size = sizeof...(Ts);
};
Add a type to a pack of types, but only if it doesn't exist already:
template<class T, class PT> struct do_push;
template<class T, class...Ts>
struct do_push<T, pack<Ts...>>{
using type = std::conditional_t<std::disjunction_v<std::is_same<Ts, T>...>,
pack<Ts...>,
pack<T, Ts...>
>;
};
template<class T, class PT> using push = typename do_push<T, PT>::type;
Now make a pack of unique types:
template<class P, class PT = pack<> >
struct unique_types_imp { using type = PT; };
template<class PT, class T, class... Ts>
struct unique_types_imp <pack<T, Ts...>, PT>
: unique_types_imp <pack<Ts...>, push<T, PT>> {};
template<class P>
using unique_types = typename unique_types_imp<P>::type;
Finally:
template<size_t S>
using size_constant = std::integral_constant<size_t, S>;
template<size_t... all>
struct no_of_uniques{
static constexpr size_t value = unique_types<pack<size_constant<all>...>>::size;
};
Using Boost.Mp11, this is a short one-liner (as always):
template <size_t... Ns>
using no_of_uniques = mp_size<mp_unique<mp_list<mp_size_t<Ns>...>>>;
Following the same logic as described below. We lift the values into types, put them in a type list, get the unique types out of that, and then get the length.
I'll generalize to types - since metaprogramming works better in types. The algorithm for counting uniqueness is an empty argument list has 0 unique types, and a non-empty list has 1 unique type + the number of unique types in the tail of that list after having removed the initial type.
In fact, let's generalize further than that - let's write a metafunction that takes a list of types and returns the unique types in it. Once you have the unique types, it's easy to count them.
template <class... > struct typelist { };
template <class >
struct uniq;
template <class TL>
using uniq_t = typename uniq<TL>::type;
template <>
struct uniq<typelist<>> {
using type = typelist<>;
};
template <class T, class... Ts>
struct uniq<typelist<T, Ts...>>
: concat<typelist<T>, uniq_t<filter_out_t<T, typelist<Ts...>>>>
{ };
Now we just need to fill in concat and filter_out_t. The latter is basically a glorified concat anyway:
template <class... > struct concat;
template <> struct concat<> { using type = typelist<>; };
template <class... Ts>
struct concat<typelist<Ts...>> {
using type = typelist<Ts...>;
};
template <class... Ts, class... Us, class... Args>
struct concat<typelist<Ts...>, typelist<Us...>, Args...>
: concat<typelist<Ts..., Us...>, Args...>
{ };
template <class T, class TL>
struct filter_out;
template <class T, class TL>
using filter_out_t = typename filter_out<T, TL>::type;
template <class T, class... Ts>
struct filter_out<T, typelist<Ts...>>
: concat<
std::conditional_t<std::is_same<T, Ts>::value, typelist<>, typelist<Ts>>...
>
{ };
Now, given a list of types, we can figure out the unique ones. To backport to the original problem, we just need a a size metafunction:
template <size_t N>
using size_t_ = std::integral_constant<size_t, N>;
template <class > struct length;
template <class T> using length_t = typename length<T>::type;
template <class... Ts>
struct length<typelist<Ts...>>
: size_t_<sizeof...(Ts)>
{ };
And then wrap everything up in one alias:
template <size_t... Ns>
using no_of_uniques = length_t<uniq_t<typelist<size_t_<Ns>...>>>;
Related
I've created a simple template class called tuple_tag which is identical to std::tuple but only acts as a tag.
// tuple_tag
template <typename...> struct tuple_tag {};
// tuple_tag_element
template <size_t I, typename T>
struct tuple_tag_element;
template <size_t I, typename Head, typename... Tail>
struct tuple_tag_element<I, tuple_tag<Head, Tail...>>
: tuple_tag_element<I - 1, tuple_tag<Tail...>> {};
template <typename Head, typename... Tail>
struct tuple_tag_element<0, tuple_tag<Head, Tail...>>
: std::type_identity<Head> {};
// tuple_tag_element_t
template <size_t I, typename T>
using tuple_tag_element_t = tuple_tag_element<I, T>::type;
// tuple_tag_size
template <typename T>
struct tuple_tag_size;
template <typename T> requires (std::is_reference_v<T> || std::is_const_v<T>)
struct tuple_tag_size<T> : tuple_tag_size<std::remove_cvref_t<T>> {};
template <typename... Ts>
struct tuple_tag_size<tuple_tag<Ts...>>
: std::integral_constant<size_t, sizeof...(Ts)> {};
// tuple_tag_size_v
template <typename T>
inline constexpr size_t tuple_tag_size_v = tuple_tag_size<T>::value;
Here:
using new_type_1 = to_tuple_type<tuple_tag<int, double>>::type;
// new_type_1 = std::tuple<int, double>;
using new_type_2 = to_tuple_tag_type<std::tuple<int, double>>::type;
// new_type_2 = tuple_tag<int, double>;
Where to_tuple_type takes a type template parameter tuple_tag<...> which will be converted into type std::tuple<...>, and to_tuple_tag_type takes a type template parameter std::tuple<...> which will be converted into type tuple_tag<...>.
What I am trying to achieve here is to pass all type template parameters from tuple_tag into std::tuple and vice-versa.
This is my prototype for to_tuple_type where it fails:
template <typename TupleTag>
struct to_tuple_type {
using type = std::tuple<...>;
};
Where type alias will be expanded into:
using type = std::tuple<tuple_tag_element_t<Index, TupleTag>...>;
...
using type = std::tuple<
tuple_tag_element_t<0, TupleTag>,
tuple_tag_element_t<1, TupleTag>,
...,
tuple_tag_element_t<N - 1, TupleTag>
>;
Where N is equal to tuple_tag_size_v<TupleTag>.
What I could only think of is to use std::index_sequence but I don't know where do I introduce the pack.
3 steps. First, make a pack
using indexes=std::make_index_sequence<tuple_tag_size<TupleTag>;
then have a helper that expands the pack. I like this one:
template<auto x>
using constant_t=std::integral_constant<decltype(x),x>;
template<auto x>
constexpr constant_t<x> constant={};
template<std::size_t...Is>
constexpr auto all_indexes( std::index_sequence<Is...> ){
return [](auto f){
return f(constant<Is>...);
};
}
now we can
template<class T>
struct tag_t{using type=T;};
template<class T>
constexpr tag_t<T> tag={};
using type=typename decltype(all_indexes(indexes{})([](auto...Is){
return tag<std::tuple<tuple_tag_element_t<Is, TupleTag>...>;
}))::type;
if that has no tpyos.
There is a simple solution that applies partial template specialization:
// to_tuple_type
template <typename Tup>
struct to_tuple_type;
template <typename... Ts>
struct to_tuple_type<tuple_tag<Ts...>> : std::type_identity<std::tuple<Ts...>> {};
// to_tuple_type_t
template <typename Tup>
using to_tuple_type_t = to_tuple_type<Tup>::type;
// to_tuple_tag_type
template <typename Tup>
struct to_tuple_tag_type;
template <typename... Ts>
struct to_tuple_tag_type<std::tuple<Ts...>> : std::type_identity<tuple_tag<Ts...>> {};
// to_tuple_tag_type_t
template <typename Tup>
using to_tuple_tag_type_t = to_tuple_tag_type<Tup>::type;
Suppose we have a variadic templated class like
template<class...Ts>
class X{
template<size_t I>
constexpr bool shouldSelect();
std::tuple<TransformedTs...> mResults; // this is want I want eventually
};
where the implementation of shouldSelect is not provided, but what it does is that, given an index i referring to the ith element of the variadic Ts, tells you whether we should select it to the subset.
I want to do a transformation on Ts such that only classes Ts at indexes that results in shouldSelect returning true should be selected. Is there an easy way to do this?
For example, if shouldSelect returns true for I = 1,2,4, and Ts... = short, int, double, T1, T2, then I want to get a TransformedTs... that is made up of int, double, T2. Then I can use this TransformedTs... in the same class.
If you're able to use C++17, this is pretty easy to implement using a combination of if constexpr and expression folding.
Start with some helper types, one with parameters to track the arguments to X::shouldSelect<I>(), and the other with a type to test.
template <typename T, size_t I, typename...Ts>
struct Accumulator {
using Type = std::tuple<Ts...>;
};
template <typename T>
struct Next { };
Then an operator overload either adds the type to the accumulator, or not with if constexpr:
template <typename TAcc, size_t I, typename... Ts, typename TArg>
decltype(auto) operator +(Accumulator<TAcc, I, Ts...>, Next<TArg>) {
if constexpr (TAcc::template shouldSelect<I>()) {
return Accumulator<TAcc, I + 1, Ts..., TArg>{};
} else {
return Accumulator<TAcc, I + 1, Ts...>{};
}
}
Finally, you can put it all together with a fold expression and extract the type with decltype:
template <template <typename... Ts> class T, typename... Ts>
constexpr decltype(auto) FilterImpl(const T<Ts...>&) {
return (Accumulator<T<Ts...>, 0>{} + ... + Next<Ts>{});
}
template<typename T>
using FilterT = typename decltype(FilterImpl(std::declval<T>()))::Type;
Usage:
using Result = FilterT<X<int, double, bool, etc>>;
Demo: https://godbolt.org/z/9h89zG
If you don't have C++17 available to you, it's still possible. You can do the same sort of conditional type transfer using a recursive inheritance chain to iterate though each type in the parameter pack, and std::enable_if to do the conditional copy. Below is the same code, but working in C++11:
// Dummy type for copying parameter packs
template <typename... Ts>
struct Mule {};
/* Filter implementation */
template <typename T, typename Input, typename Output, size_t I, typename = void>
struct FilterImpl;
template <typename T, typename THead, typename... TTail, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<THead, TTail...>, Mule<OutputTs...>, I, typename std::enable_if<( T::template shouldSelect<I>() )>::type >
: FilterImpl<T, Mule<TTail...>, Mule<OutputTs..., THead>, (I + 1)>
{ };
template <typename T, typename THead, typename... TTail, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<THead, TTail...>, Mule<OutputTs...>, I, typename std::enable_if<( !T::template shouldSelect<I>() )>::type >
: FilterImpl<T, Mule<TTail...>, Mule<OutputTs...>, (I + 1)>
{ };
template <typename T, typename... OutputTs, size_t I>
struct FilterImpl<T, Mule<>, Mule<OutputTs...>, I>
{
using Type = std::tuple<OutputTs...>;
};
/* Helper types */
template <typename T>
struct Filter;
template <template <typename... Ts> class T, typename... Ts>
struct Filter<T<Ts...>> : FilterImpl<T<Ts...>, Mule<Ts...>, Mule<>, 0>
{ };
template <typename T>
using FilterT = typename Filter<T>::Type;
Demo: https://godbolt.org/z/esso4M
One thing that really annoys me about C++ is that an empty struct/class takes up space.
So, I have this idea that std::tuple (or some variant, since it's (and the compiler's) implementation is highly implementation dependent) might be able to save the day, which it sort of does, but there are issues due to packing and alignment. Because of how compilers will align the items in the struct, having a empty next to a non-empty next to an empty next to a non-empty will be larger than 2 empties next to 2 non-empties.
Because of this, I need a way to reorder the types based on some criteria. Sorting the entire list based on size isn't necessary (and may in some cases be detrimental) so I need some generic way to reorder the tuple's type list but still access it as if the type list was in the original order.
I looked around a bit and haven't found anything like this and I'm at a loss. Ideas on how to accomplish this?
Example
struct A{};
struct B{};
// Need to be reordered based on some criteria.
std::tuple<A, int, B, float> x;
// In this case move all of the empty objects together like:
// std::tuple<A, B, int, float> x;
// but still have get<1>(x) return the `int` and get<2>(x) return `B`.
static_assert(std::is_same<decltype(get<0>()), A>::value, "0 should be type A");
static_assert(std::is_same<decltype(get<1>()), int>::value, "1 should be type int");
static_assert(std::is_same<decltype(get<2>()), B>::value, "2 should be type float");
static_assert(std::is_same<decltype(get<3>()), float>::value, "3 should be type B");
The reason this cannot be done by hand is that this could be part of a template and the elements in tuple may be empty or not, based on the parameters:
template <typename A, typename B, typename C, typename D>
class X
{
// Need to have this auto arranged given some criteria
// like size or move all of the empties together.
tuple<A, B, C, D> x;
public:
template<int i>
auto get() -> typename std::tuple_element<i, decltype(x)>
{
return get<i>(x);
}
};
// What are these types? Who knows. This could be buried in some
// template library somewhere.
X<T1, T2, T3, T4> x;
Building on what Barry did.
So from here I'd need a mapping meta-function to use the original
indices, how would I do that?
First, some helpers to facilitate index mapping. And because I'm lazy, I modified typelist slightly.
template <typename... Args>
struct typelist {
static constexpr std::size_t size = sizeof...(Args);
};
template<class T, std::size_t OldIndex, std::size_t NewIndex>
struct index_map_leaf {
using type = T;
static constexpr std::size_t old_index = OldIndex;
static constexpr std::size_t new_index = NewIndex;
};
template<class... Leaves>
struct index_map : Leaves... {};
Given a properly built index_map, converting from old index to new index is simple, leveraging template argument deduction and overload resolution:
template<std::size_t OldIndex, std::size_t NewIndex, class T>
index_map_leaf<T, OldIndex, NewIndex>
do_convert_index(index_map_leaf<T, OldIndex, NewIndex>);
template<std::size_t OldIndex, class IndexMap>
using converted_index_t = decltype(do_convert_index<OldIndex>(IndexMap()));
converted_index_t<OldIndex, IndexMap>::new_index is, well, the new index.
To build the index map, we do it in in three steps. We start by transforming the types into type-index pairs.
template<class... Ts, std::size_t... Is>
typelist<index_map_leaf<Ts, Is, 0>...>
do_build_old_indices(typelist<Ts...>, std::index_sequence<Is...>);
template<class TL>
using build_old_indices =
decltype(do_build_old_indices(TL(), std::make_index_sequence<TL::size>()));
Next, we partition the pairs. We need a metafunction that applies another metafunction to its arguments' nested typedef type rather than the arguments themselves.
// Given a metafunction, returns a metafunction that applies the metafunction to
// its arguments' nested typedef type.
template<class F>
struct project_type {
template<class... Args>
using apply = typename F::template apply<typename Args::type...>;
};
Given this, partitioning a typelist of index_map_leafs is simply partition_t<LeafList, project_type<F>>.
Finally, we transform the partitioned list, adding the new indices.
template<class... Ts, std::size_t... Is, std::size_t...Js>
typelist<index_map_leaf<Ts, Is, Js>...>
do_build_new_indices(typelist<index_map_leaf<Ts, Is, 0>...>,
std::index_sequence<Js...>);
template<class TL>
using build_new_indices =
decltype(do_build_new_indices(TL(), std::make_index_sequence<TL::size>()));
Bringing it all together,
template<class TL, class F>
using make_index_map =
apply_t<quote<index_map>, build_new_indices<partition_t<build_old_indices<TL>,
project_type<F>>>>;
With a little utility to convert the arguments of an arbitrary template to a type list:
template<template<class...> class T, class... Args>
typelist<Args...> do_as_typelist(typelist<T<Args...>>);
template<class T>
using as_typelist = decltype(do_as_typelist(typelist<T>()));
We can do the partitioning only once, by constructing the reordered tuple type directly from the index_map.
template<class Tuple, class F>
struct tuple_partitioner {
using map_type = make_index_map<as_typelist<Tuple>, F>;
using reordered_tuple_type = apply_t<project_type<quote<std::tuple>>,
as_typelist<map_type>>;
template<std::size_t OldIndex>
using new_index_for =
std::integral_constant<std::size_t,
converted_index_t<OldIndex, map_type>::new_index>;
};
For example, given
using original_tuple = std::tuple<int, double, long, float, short>;
using f = quote<std::is_integral>;
using partitioner = tuple_partitioner<original_tuple, f>;
The following assertions hold:
static_assert(partitioner::new_index_for<0>() == 0, "!");
static_assert(partitioner::new_index_for<1>() == 3, "!");
static_assert(partitioner::new_index_for<2>() == 1, "!");
static_assert(partitioner::new_index_for<3>() == 4, "!");
static_assert(partitioner::new_index_for<4>() == 2, "!");
static_assert(std::is_same<partitioner::reordered_tuple_type,
std::tuple<int, long, short, double, float>>{}, "!");
Demo.
P.S. Here's my version of filter:
template<typename A, typename F>
using filter_one = std::conditional_t<F::template apply<A>::value,
typelist<A>, typelist<>>;
template<typename F, typename... Args>
concat_t<filter_one<Args, F>...> do_filter(typelist<Args...>);
template <typename TL, typename F>
using filter_t = decltype(do_filter<F>(TL()));
First, let's start with the basics. We need a way to turn a template template (std::tuple) into a metafunction class:
template <template <typename...> class Cls>
struct quote {
template <typename... Args>
using apply = Cls<Args...>;
};
And a typelist:
template <typename... Args>
struct typelist { };
And something to go between them:
template <typename F, typename TL>
struct apply;
template <typename F, typename... Args>
struct apply<F, typelist<Args...>> {
using type = typename F::template apply<Args...>;
};
template <typename F, typename TL>
using apply_t = typename apply<F, TL>::type;
So that given some typelist, we can just have:
using my_tuple = apply_t<quote<std::tuple>, some_typelist>;
Now, we just need a partitioner on some criteria:
template <typename TL, typename F>
struct partition {
using type = concat_t<filter_t<TL, F>,
filter_t<TL, not_t<F>>
>;
};
Where concat:
template <typename... Args>
struct concat;
template <typename... Args>
using concat_t = typename concat<Args...>::type;
template <typename... A1, typename... A2, typename... Args>
struct concat<typelist<A1...>, typelist<A2...>, Args...> {
using type = concat_t<typelist<A1..., A2...>, Args...>;
};
template <typename TL>
struct concat<TL> {
using type = TL;
};
filter:
template <typename TL, typename F>
struct filter;
template <typename TL, typename F>
using filter_t = typename filter<TL, F>::type;
template <typename F>
struct filter<typelist<>, F> {
using type = typelist<>;
};
template <typename A, typename... Args, typename F>
struct filter<typelist<A, Args...>, F> {
using type = concat_t<
std::conditional_t<F::template apply<A>::value,
typelist<A>,
typelist<>>,
filter_t<typelist<Args...>, F>
>;
};
And not_:
template <typename F>
struct not_ {
template <typename Arg>
using apply = std::conditional_t<F::template apply<Args>::value,
std::false_type,
std::true_type>;
};
Which, given some_typelist of types that you want to put in your tuple becomes:
using my_tuple = apply_t<
quote<std::tuple>,
partition_t<
some_typelist,
some_criteria_metafunc_class
>>;
I'm reading through Eric Niebler's post on his tiny metaprogramming library. In trying to implement the pieces that he omits / lists as challenges, I am left with the following implementation of transform:
template <typename F, typename... As>
using meta_apply = typename F::template apply<As...>;
template <typename... >
struct typelist_transform;
// unary
template <typename... T, typename F>
struct typelist_transform<typelist<T...>, F>
{
using type = typelist<meta_apply<F,T>...>;
};
// binary
template <typename... T, typename... U, typename F>
struct typelist_transform<typelist<T...>, typelist<U...>, F>
{
using type = typelist<meta_apply<F,T,U>...>;
};
This works, but seems very unsatisfactory to me - I need a partial specialization of typelist_transform for every number of "arguments" into F. Is there a better way to implement this metafunction?
With a slightly modified interface (taking the metafunction first, rather than last, and adding a couple members to typelist to get its size and the N-th member rather than using standalone metafunctions):
template <typename... Ts>
struct typelist {
template<size_t N>
using get = std::tuple_element_t<N, std::tuple<Ts...>>;
static constexpr size_t size = sizeof...(Ts);
};
template<class, class, class...>
struct typelist_transform_helper;
template<size_t... Ints, class F, class...Lists>
struct typelist_transform_helper<std::index_sequence<Ints...>, F, Lists...>{
template <class MF, size_t N, typename... Ls>
using apply_to_Nth = meta_apply<MF, typename Ls::template get<N>...>;
using type = typelist<apply_to_Nth<F, Ints, Lists...>...>;
};
template<class F, class L1, class... Lists>
struct typelist_transform : typelist_transform_helper<std::make_index_sequence<L1::size>, F, L1, Lists...> {
// static assert on size of all lists being equal if wanted
};
Demo.
One problem with the above is that tuple_element usually requires N recursive template instantiations, making the total number of template instantiations quadratic in the length of the list. Given our use case (we access every single index), we can do better:
template<class L>
struct typelist_indexer {
template<size_t N, class T> struct helper_base { using type = T; };
template<class S, class> struct helper;
template<size_t... Ns, class... Ts>
struct helper<std::index_sequence<Ns...>, typelist<Ts...>> : helper_base<Ns, Ts>... {};
template<size_t N, class T>
static helper_base<N, T> do_get(helper_base<N, T>);
using helper_type = helper<std::make_index_sequence<L::size>, L>;
template<size_t N>
using get = typename decltype(do_get<N>(helper_type()))::type;
};
and then
template<size_t... Ints, class F, class...Lists>
struct typelist_transform_helper<std::index_sequence<Ints...>, F, Lists...>{
template <class MF, size_t N, typename... Ls>
using apply_to_Nth = meta_apply<MF, typename typelist_indexer<Ls>::template get<N>...>;
using type = typelist<apply_to_Nth<F, Ints, Lists...>...>;
};
Now the number of template instantiations is linear in the size of the list, and in my tests this result in a significant compile time improvement. Demo.
Is there a way to automatically select between multiple non-template functions based on a template parameter?
Example:
class Aggregate
{
public:
std::string asString();
uint32_t asInt();
private:
// some conglomerate data
};
template <typename T>
T get(Aggregate& aggregate)
{
// possible map between types and functions?
return bind(aggregate, typeConvert[T])(); ??
// or
return aggregate.APPROPRIATE_TYPE_CONVERSION();
}
The solution would be nice to throw a compiler error if there is no good conversion available, i.e.
get<double>(aggregate); // compile error
I do not want to use template specialization, i.e
template<>
int get(Aggregate& aggregate)
{
return aggregate.asInt();
}
because it leads to code duplication when your get() function has more then one line of code
The pedestrian way is to define each possible option separately:
template <typename T> T get(Aggregate &); // leave undefined
template <> uint32_t get(Aggregate & a) { return a.asInt(); }
// ...
Absent any more systematic structure that encodes which function serves which conversion, I think this is the best you can do. It may be worth redefining Aggregate, though, to be more introspectible.
You may do something like (require C++11) : (https://ideone.com/UXrQFm)
template <typename T, typename... Ts> struct get_index;
template <typename T, typename... Ts>
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename... Ts>
struct get_index<T, Tail, Ts...> :
std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};
template <typename T, typename Tuple> struct get_index_in_tuple;
template <typename T, typename ... Ts>
struct get_index_in_tuple<T, std::tuple<Ts...>> : get_index<T, Ts...> {};
class Aggregate
{
public:
std::string asString();
uint32_t asInt();
private:
// some conglomerate data
};
template <typename T>
T get(Aggregate& aggregate)
{
using types = std::tuple<uint32_t, std::string>;
auto funcs = std::make_tuple(&Aggregate::asInt, &Aggregate::asString);
return (aggregate.* (std::get<get_index_in_tuple<T, types>::value>(funcs)))();
}