I tried to implement the C++14 alias template make_integer_sequence
, which simplifies the creation of the class template integer_sequence
.
template< class T, T... I> struct integer_sequence
{
typedef T value_type;
static constexpr size_t size() noexcept { return sizeof...(I) ; }
};
template< class T, T N>
using make_integer_sequence = integer_sequence< T, 0,1,2, ... ,N-1 >; // only for illustration.
To implement make_integer_sequence
we need a helper structure make_helper
.
template< class T , class N >
using make_integer_sequence = typename make_helper<T,N>::type;
Implementing make_helper
isn't too difficult.
template< class T, T N, T... I >
struct make_helper
{
typedef typename mpl::if_< T(0) == N,
mpl::identity< integer_sequence<T,I...> >,
make_helper< T, N-1, N-1,I...>
>::type;
};
To test make_integer_sequence
I made this main function:
int main()
{
#define GEN(z,n,temp) \
typedef make_integer_sequence< int, n > BOOST_PP_CAT(int_seq,n) ;
BOOST_PP_REPEAT(256, GEN, ~);
}
I compiled the program with GCC 4.8.0, on a quad-core i5 system with 8GBs of RAM. Successful compilation took 4 seconds.
But, when I changed the GEN macro to:
int main() {
#define GEN(z,n,temp) \
typedef make_integer_sequence< int, n * 4 > BOOST_PP_CAT(int_seq, n) ;
BOOST_PP_REPEAT(256, GEN, ~ );
}
The compilation was unsuccessful and outputted the error message:
virtual memory exhausted.
Could somebody explain this error and what caused it?
EDIT:
I simplified the test to:
int main()
{
typedef make_integer_sequence< int, 4096 > int_seq4096;
}
I then successfully compiled with GCC 4.8.0 -ftemplate-depth=65536.
However this second test:
int main()
{
typedef make_integer_sequence< int, 16384 > int_seq16384;
}
Did not compile with GCC 4.8.0 -ftemplate-depth=65536, and resulted in the error:
virtual memory exhausted.
So, my question is, how do I decrease template deep instantiation?
Regards, Khurshid.
This is basically me hacking around Xeo's solution: Making community wiki - if appreciative, please upvote Xeo.
...just modified until I felt it couldn't get any simpler, renamed and added
value_type
andsize()
per the Standard (but only doingindex_sequence
notinteger_sequence
), and code working with GCC 5.2-std=c++14
could run otherwise unaltered under older/other compilers I'm stuck with. Might save someone some time / confusion.Notes:
the "magic" of Xeo's solution is in the declaration of
_merge_and_renumber
(concat
in his code) with exactly two parameters, while the specilisation effectively exposes their individual parameter packsthe
typename
...::type
in...avoids the error:
I found very fast and needless deep recursion version of implementation of
make_index_sequence
. In my PC it compiles with N = 1 048 576 , with 2 s. (PC : Centos 6.4 x86, i5, 8 Gb RAM, gcc-4.4.7 -std=c++0x -O2 -Wall).Here's a
log N
implementation that doesn't even need an increased max-depth for template instantiations and compiles pretty fast:You are missing a
-1
here:in particular:
Next, the first branch shouldn't be
integer_sequence<T>
, but ratherinteger_sequence<T, I...>
.which should be enough to get your original code to compile.
In general, when writing serious
template
metaprogramming, your main goal should be to keep the depth oftemplate
instantiation down. A way to think about this problem is imagining you have an infinite-thread computer: each independent calculation should be shuffled off onto its own thread, then shuffled together at the end. You have a few operations that take O(1) depth, like...
expansion: exploit them.Usually, pulling of logarithmic depth is enough, because with a
900
depth, that allows2^900
sized structures, and something else breaks first. (To be fair, what is more likely to happen is 90 different layers of2^10
sized structures).Simple implementation O(N). Probably not what you want for large N, but my application is only for calling functions with indexed arguments, and I wouldn't expect an arity of more than about 10. I haven't filled in the members of integer_sequence. I'm looking forward to using a standard library implementation and nuking this :)
Here is another slightly more general variation of Xeo's logarithmic answer which provides
make_integer_sequence
for arbitrary types. This is done by usingstd::integral_constant
in order to avoid the dreaded "template argument involves template parameter" error.Demo: coliru