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\$
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\$
  • 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\$ – user1095108 Mar 1 '14 at 14:46
  • \$\begingroup\$ @user1095108 Well, that's actually a good idea :) \$\endgroup\$ – Morwenn Mar 2 '14 at 19:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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