Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting access to the back of a template parameter pack in O(1)

In a recent mail on the boost developers mailing list Eric Niebler mentioned that it is possible to get the last element of a template parameter pack in O(1) instantiations.

How can this be done?

like image 973
pmr Avatar asked Oct 24 '12 13:10

pmr


1 Answers

You can check https://github.com/ericniebler/proto-0x/blob/master/boost/proto/utility.hpp, as mentioned in the post, on how the get_nth metafunctions are implemented. The essence, after much simplification:

#include <type_traits>
#include <cstdlib>

// A template to store a variadic list.
template <typename...>
struct List;

// Concatenate two variadic lists together. Should be O(1).
template <typename Left, typename Right>
struct Concat;

template <typename... Lefts, typename... Rights>
struct Concat<List<Lefts...>, List<Rights...>> {
    typedef List<Lefts..., Rights...> Type;
};

// Construct List<void, void, void, ...> with 'n' elements.
// Uses "bisection" method, which should instantiate O(log n) templates
template <size_t n>
struct RepeatVoid : Concat<typename RepeatVoid<n/2>::Type,
                           typename RepeatVoid<n - n/2>::Type> {};

template <>
struct RepeatVoid<0> {
    typedef List<> Type;
};

template <>
struct RepeatVoid<1> {
    typedef List<void> Type;
};

template <typename>
struct GetNthAux;

// This would create a function of the form:
//
//   T eval(cv void*, cv void*, cv void*, ..., cv void*, T*, ...)
//
// where there are 'n' const volatile void*. These will be used to absorb
// the first 'n' template arguments. The actual one can be extracted from
// the return type.
//
template <typename... Ts>
struct GetNthAux<List<Ts...>> {
    template<typename T>
    static constexpr T eval(const volatile Ts*..., T*, ...);
};

// Run the whole thing.
template<size_t n, typename... Ts>
using GetNth = decltype(
    GetNthAux<typename RepeatVoid<n>::Type>::eval((Ts*)nullptr...)
);

// Example:
int main() {
    static_assert(std::is_same<GetNth<0, int, float, char>, int>::value, "");
    static_assert(std::is_same<GetNth<1, int, float, char>, float>::value, "");
    static_assert(std::is_same<GetNth<2, int, float, char>, char>::value, "");
}

Actually it is O(log N), not O(1), because of the time taken in RepeatVoid.

But this method does perform much better than linear methods like the typical implementation of tuple_element<>. I have tried to compare the performance of GetNth with tuple_element<> on getting the item 899 of a list, and the latter is an order of magnitude faster:

                         tuple_element<>   GetNth<>
g++ 4.7 with libstdc++   0.7s              0.1s
clang++ 3.1 with libc++  0.9s              0.1s
like image 71
kennytm Avatar answered Oct 15 '22 08:10

kennytm