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?