In template meta-programming, integer sequences and ranges are very useful. I've made a couple of utility classes that do various operations on compile-time integer packs.
The implementation is non-linearly-recursive, allowing faster compilation and a greater number of template arguments.
###Major functionality overview
integer_pack<T, T...>, a pack of integers of typeT.index_pack<std::size_t...>, an alias ofinteger_pack<std::size_t, std::size_t...>.make_integer_sequence<T, T n>, make aninteger_pack<>type with values being in the range \$[ 0, n - 1 )\$.make_integer_range<T, T from, T to, T step>, make aninteger_pack<>with values in the range \$[ from, to ]\$ using an increment ofstep.integer_pack_extract<index_pack<>, integer_pack<>>, creates an integer pack containing the integers at the indices of the argument index pack from the argument integer pack.
##Sample usage
#include "integer_pack.h"
int main()
{
using namespace ct;
integer_pack_size_v<integer_pack<int, -1, -3, 5, -6>>; // 4
integer_pack_negate_t<integer_pack<int, -1, -3, 5, -6>>; // 1, 3, -5, 6
make_integer_sequence<int, 5>; // 0, 1, 2, 3, 4
make_integer_sequence<int, -5>; // 0, -1, -2, -3, -4
make_integer_range<int, -2, 3>; // -2, -1, 0, 1, 2, 3
make_integer_range<int, 3, -2>; // 3, 2, 1, 0, -1, -2
make_index_range<0, 8, 2>; // 0, 2, 4, 6, 8
make_integer_range<int, -9, 3, 3>; // -9, -6, -3, 0, 3
integer_pack_at_v<2, integer_pack<int, -1, -3, 5, -6>>; // 5
}
##Review goals
- Improving implementation in terms of compilation time or in terms of code size without affecting compilation time.
- Currently,
integer_pack_fold<>supports a maximum integer pack size of 10'000 on my compiler. I'd like to increase this limit without having to explicitly give more static memory to the compiler. - Suggestions on other useful operations that haven't been implemented and general style, efficiency, etc.
##Implementation
At the top of every namespace, there's a comment indicating which feature is inside. Now brace yourself for lots of template meta-programming fun...
#ifndef CT_INTEGER_PACK_H
#define CT_INTEGER_PACK_H
#include <cstddef>
#include <type_traits>
// integer_pack, index_pack
namespace ct
{
template<class T, T... ints>
struct integer_pack : std::integral_constant<std::size_t, sizeof...(ints)>
{
static_assert(std::is_integral_v<T>,
"integer_pack: first template argument must be an integer type");
using backing_type = T;
using type = integer_pack<T, ints...>;
};
template<std::size_t... ints>
using index_pack = integer_pack<std::size_t, ints...>;
}
// integer_pack_size, integer_pack_size_v
namespace ct
{
template<class IntegerPack>
struct integer_pack_size;
template<class T, T... ints>
struct integer_pack_size<integer_pack<T, ints...>>
: std::integral_constant<std::size_t, sizeof...(ints)>
{};
template<class IntegerPack>
constexpr auto integer_pack_size_v = integer_pack_size<IntegerPack>::value;
}
// integer_pack_negate, make_integer_sequence, make_index_sequence
namespace ct
{
template<class IntegerPack>
struct integer_pack_negate;
template<class T, T... ints>
struct integer_pack_negate<integer_pack<T, ints...>>
: integer_pack<T, (-ints)...>
{};
template<class IntegerPack>
using integer_pack_negate_t = typename integer_pack_negate<IntegerPack>::type;
namespace impl
{
template<class T, class LIntegerPack, class RIntegerPack>
struct integer_pack_merge;
template<class T, T... l_ints, T... r_ints>
struct integer_pack_merge
<
T, integer_pack<T, l_ints...>, integer_pack<T, r_ints...>
> : integer_pack<T, l_ints..., sizeof...(l_ints) + r_ints...>
{};
template<class T, T n, class = void>
struct integer_sequence_generate
: integer_pack_merge
<
T,
typename integer_sequence_generate<T, n / 2>::type,
typename integer_sequence_generate<T, n / 2 + n % 2>::type
>
{};
template<class T, T n>
struct integer_sequence_generate<T, n, std::enable_if_t<(n == 1)>>
: integer_pack<T, n - 1>
{};
template<class T, T n>
struct integer_sequence_generate<T, n, std::enable_if_t<(n == 0)>>
: integer_pack<T>
{};
template<class T, T n>
struct integer_sequence_generate<T, n, std::enable_if_t<(n < 0)>>
{
using type = typename integer_pack_negate
<
typename integer_sequence_generate<T, -n>::type
>::type;
};
}
template<class T, T n>
using make_integer_sequence = typename impl::integer_sequence_generate<T, n>::type;
template<std::size_t n>
using make_index_sequence = make_integer_sequence<std::size_t, n>;
}
// make_integer_range, make_index_range
namespace ct
{
namespace impl
{
template
<
class T,
T from,
T to,
T step,
T n_vals = (from < to ? to - from : from - to)
>
struct integer_range_generate
{
private:
static_assert(n_vals % step == 0,
"integer_range_generate: unreachable integer range; invalid step value");
template<class IntegerPack, bool is_increasing>
struct integer_range_generate_impl;
template<T... ints>
struct integer_range_generate_impl<integer_pack<T, ints...>, true>
: integer_pack<T, (from + step * ints)...>
{};
template<T... ints>
struct integer_range_generate_impl<integer_pack<T, ints...>, false>
: integer_pack<T, (from - step * ints)...>
{};
public:
using type = typename integer_range_generate_impl
<
make_integer_sequence<T, 1 + n_vals / step>, (from < to)
>::type;
};
template<class T, T n, T step, T n_vals>
struct integer_range_generate<T, n, n, step, n_vals>
: integer_pack<T, n>
{};
}
template<class T, T from, T to, T step = 1>
using make_integer_range = typename impl::integer_range_generate
<
T, from, to, step
>::type;
template<std::size_t from, std::size_t to, std::size_t step = 1>
using make_index_range = make_integer_range<std::size_t, from, to, step>;
}
// integer_pack_at
namespace ct
{
namespace impl
{
template<class T, T... ints>
constexpr auto at(std::size_t const i, integer_pack<T, ints...>) noexcept
{
constexpr T values[] = { ints... };
return values[i];
}
}
template<std::size_t i, class IntegerPack>
struct integer_pack_at
: std::integral_constant
<
typename IntegerPack::backing_type, impl::at(i, IntegerPack{})
>
{
static_assert(integer_pack_size<IntegerPack>::value != 0,
"integer_pack_at: empty integer pack");
static_assert(i < integer_pack_size<IntegerPack>::value,
"integer_pack_at: index out of bounds");
};
template<std::size_t i, class IntegerPack>
constexpr auto integer_pack_at_v = integer_pack_at<i, IntegerPack>::value;
}
#endif // CT_INTEGER_PACK_H