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 (?)