10
\$\begingroup\$

We all know about the indices trick, right? Here's (hopefully) an improved version...

template <::std::size_t...> struct indices { };

namespace detail
{

template<class A, class B> struct catenate_indices;

template <::std::size_t ...Is, ::std::size_t ...Js>
struct catenate_indices<indices<Is...>, indices<Js...> >
{
  using indices_type = indices<Is..., Js...>;
};

template <::std::size_t, ::std::size_t, typename = void> struct expand_indices;

template <::std::size_t A, ::std::size_t B>
struct expand_indices<A, B, typename ::std::enable_if<A == B>::type>
{
  using indices_type = indices<A>;
};

template <::std::size_t A, ::std::size_t B>
struct expand_indices<A, B, typename ::std::enable_if<A != B>::type>
{
  static_assert(A < B, "A > B");
  using indices_type = typename catenate_indices<
    typename expand_indices<A, (A + B) / 2>::indices_type,
    typename expand_indices<(A + B) / 2 + 1, B>::indices_type
  >::indices_type;
};

}

template <::std::size_t A>
struct make_indices : detail::expand_indices<0, A>::indices_type
{
};

template <::std::size_t A, ::std::size_t B>
struct make_indices_range : detail::expand_indices<A, B>::indices_type
{
};

Usage:

#include <iostream>

#include "utility.hpp"

template <::std::size_t ...Is>
void show_indices(indices<Is...>)
{
  [](...){}((((::std::cout << Is << ::std::endl), 0))...);
}

int main()
{
  show_indices(make_indices_range<3, 9>());

  return 0;
}

Any suggestions?

\$\endgroup\$

1 Answer 1

11
\$\begingroup\$

You can get rid of the <type_traits> header by replacing your std::enable_if by a bool template specialization:

template <::std::size_t, ::std::size_t, bool>
struct expand_indices_impl;

template <::std::size_t A, ::std::size_t B>
struct expand_indices:
    expand_indices_impl<A, B, A==B>
{};

template <::std::size_t A, ::std::size_t B>
struct expand_indices_impl<A, B, true>
{
    using indices_type = indices<A>;
};

template <::std::size_t A, ::std::size_t B>
struct expand_indices_impl<A, B, false>
{
    static_assert(A < B, "A > B");
    using indices_type = typename catenate_indices<
        typename expand_indices<A, (A + B) / 2>::indices_type,
        typename expand_indices<(A + B) / 2 + 1, B>::indices_type
    >::indices_type;
};

Moreover, in C++ (and many other programming languages, see Python range for example), ranges tend to have the end-exclusive form [begin, end); therefore, make_indices_range<3, 9> should contain the indices from 3 to 8, and not from 3 to 9. It is easy to alter the behaviour:

template <::std::size_t A>
struct make_indices:
    detail::expand_indices<0, A-1>::indices_type
{};

template <::std::size_t A, ::std::size_t B>
struct make_indices_range:
    detail::expand_indices<A, B-1>::indices_type
{};

As proposed in the comments, make_indices_range also needs to provide an empty range when A == B:

template <::std::size_t A>
struct make_indices_range<A, A>:
    indices<>
{};
\$\endgroup\$
2
  • 1
    \$\begingroup\$ You did not provide for the special case of make_indices_range<A, A> in which case you need to inherit from indices<>. \$\endgroup\$ Commented Mar 1, 2014 at 14:46
  • \$\begingroup\$ @user1095108 Well, that's actually a good idea :) \$\endgroup\$
    – Morwenn
    Commented Mar 2, 2014 at 19:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.