How to improve the template recursion depth needed for this template?

I use the template described in this SO question in my code to compile different types of compile-time registration lists: C ++ type registration with a compile-time trick

For example, if you have a bunch of lua callback functions, and you don’t want to forget to register them in any lua state, you can declare them with a macro that puts a template type that knows its name and function pointer into a list, and then you have a single line that registers all the functions.

The limitation of this technique (as described in SO's answer) is that if you have n elements in the list, this requires a recursion depth of the O(n) pattern. This is not ideal, I actually already have several lua callback functions ...

I thought that the depth of recursion of O(n) inevitable for various reasons, however, as I recently learned from Yakka in this (unreleased) answer, some fundamental things that I naively thought were necessary by O(n) could actually be done in O(log n) depth. Extending the expansive expansion of expression statements

In particular, there is no reason to believe that the involved datastructures require O(log n) template depth for their operations.

The part I'm not sure about is the Rank trick. From the reference code this template

 template <int N> struct Rank : Rank<N - 1> {}; template <> struct Rank<0> {}; 

is a key ingredient. This is expensive in template depth, but to create an instance of Rank<N> , template depth n is required. An important property that it has is that if a function f defined that is overloaded using many different types of rank, overload resolution always chooses the overload of the highest rank because it is the “most derived instance”. For instance. if we have this code:

 bool f(Rank<0>) { return false; } int f(Rank<1>) { return 0; } float f(Rank<2>) { return 0; } double f(Rank<3>) { return 0; } 

it always happens that at any point in the code decltype(f(Rank<100>{})) has a type equal to the return value of the last specified overload. That is, these statements pass

 bool f(Rank<0>) { return false; } static_assert(std::is_same<bool, decltype(f(Rank<100>{}))>::value, "D:"); int f(Rank<1>) { return 0; } static_assert(std::is_same<int, decltype(f(Rank<100>{}))>::value, "D:"); float f(Rank<2>) { return 0; } static_assert(std::is_same<float, decltype(f(Rank<100>{}))>::value, "D:"); double f(Rank<3>) { return 0; } static_assert(std::is_same<double, decltype(f(Rank<100>{}))>::value, "D:"); 

Is there a way to do this that doesn't require the recursion depth of the O(n) pattern?

Perhaps using more carefully selected parameters to overload functions (?)

+6
source share
1 answer

To avoid a type error:

the depth of the template instance exceeds a maximum of 900 (use -ftemplate-depth = to increase the maximum), creating an instance of 'struct Rank <1148u>'

without increasing the maximum depth of the template globally, you can create intermediate templates:

 // To allow instantiation of Rank<1000> with template-depth at 256 template struct Rank<250>; template struct Rank<500>; template struct Rank<750>; template struct Rank<1000>; 

You may also have an assistant:

 namespace detail { template <template <std::size_t> class C, typename Seq, std::size_t BlockSize> struct Instantiate_Impl; template <template <std::size_t> class C, std::size_t... Is, std::size_t BlockSize> struct Instantiate_Impl<C, std::index_sequence<Is...>, BlockSize> { std::tuple<C<(Is * BlockSize)>...> dummy; }; } template <template <std::size_t> class C, std::size_t N, std::size_t BlockSize = 250> struct Instantiate : detail::Instantiate_Impl<C, std::make_index_sequence<1 + N / BlockSize>, BlockSize> {}; 

And then

 template struct Instantiate<Rank, 2000, 250>; // Rank<2000> is now instantiated. 
+6
source

All Articles