I found myself needing to iterate (generically) over an array, or a tuple of N T's.

Code demo: http://coliru.stacked-crooked.com/a/9b8ab7c01b79d086

I came up with the following implementation, which I'm reasonably happy with it, as it seems to to handle the cases I need without introducing any overhead.

#include <array>
#include <iostream>
#include <tuple>
#include <vector>

namespace functional
{

namespace detail {

template <typename Tuple, typename F, std::size_t... Indices>
void
tuple_for_each_impl(Tuple &&tuple, F &&f, std::index_sequence<Indices...>)
{
  using swallow = int[];
  (void)swallow{1, (f(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...};
}

} // ns detail

template <typename Tuple, typename F>
void
for_each(Tuple &&tuple, F &&f)
{
  constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
  detail::tuple_for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f), std::make_index_sequence<N>{});
}

} // ns functional

struct tuple_tag {};
struct iterator_tag {};

template<typename U, typename TAG>
struct burrito
{
  U value;
  using TAG_TYPE = TAG;

  template<typename T>
  burrito(T const& t) : value(t) {}

  template<typename T>
  burrito(T t0, T t1) : value(std::make_pair(t0, t1)) {}

  template<typename FN>
  void iterate(FN const fn) const
  {
    bool constexpr IS_TUPLE = std::is_same<tuple_tag, TAG_TYPE>();
    if constexpr (IS_TUPLE) {
      functional::for_each(this->value, fn);
    } else {
      for (auto it{value.first}; it < value.second; ++it) {
        fn(*it);
      }
    }
  }
};

template<typename M, typename FN>
void testfn(M const& m, FN const& fn)
{
  m.iterate(fn);
}

template<typename T>
auto
make_burrito(T t0, T t1)
{
  auto const p = std::make_pair(t0, t1);
  return burrito<decltype(p), iterator_tag>{p};
}

template<typename C>
auto
make_burrito(C const& c)
{
  return make_burrito(c.cbegin(), c.cend());
}

template<typename ...T>
auto
make_burrito(std::tuple<T...> const& t)
{
  using U = std::tuple<T...>;
  return burrito<U, tuple_tag>{t};
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// demo
int main()
{
  std::tuple<int, int> const tup0 = std::make_tuple(-3, -4);
  std::array<int, 10> const arr0 = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
  std::vector<int> const v = {32, 42, 52, 62};

  auto const m0 = make_burrito(tup0);
  auto const m1 = make_burrito(arr0.begin(), arr0.end());
  auto const m2 = make_burrito(v);

  auto const fn = [](auto const& i) {
    std::cerr << std::to_string(i) << "\n";
  };

  std::cerr << "printing tuple\n";
  testfn(m0, fn);
  std::cerr << "|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-" << "\n";

  std::cerr << "printing array\n";
  testfn(m1, fn);
  std::cerr << "|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-" << "\n";

  std::cerr << "printing vec\n";
  testfn(m2, fn);
  std::cerr << "|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-" << "\n";
}

I'd like to be able to hide the instantiation of the burrito from the caller entirely, but still define my function arguments as a burrito.

Like this:

template<typename B, typename FN>
void something(B const& burrito_like, FN const& fn) {
  burrito_like.iterate(fn);
}

With the caller doing:

auto const tup = ...; // somehow caller creates a tuple
something(tup, []() {});

This doesn't work though (obviously) and the compiler complains about tuple not implement iterate(FN const&);

I looked into implicit conversion constructors, but I don't see how I would specify the different TAG parameters from within the implicit constructor.

So my question is, is something like this possible?

0

There are 0 answers