Skip to content

Latest commit

 

History

History
320 lines (255 loc) · 7.79 KB

198.md

File metadata and controls

320 lines (255 loc) · 7.79 KB
Info

Example

int main() {
  std::variant<int, std::string_view, double> v = 3.14;
  std::visit([](const auto& e) { std::cout << e; }, v); // prints 3.14

  std::tuple t = {42, "text", 3.14};
  std::apply([](const auto&... args) { ((std::cout << args << ' '), ...); }, t); // prints 42 text 3.14

  std::array a{1, 2, 3};
  std::for_each(std::cbegin(a), std::cend(a), [](const auto& e) { std::cout << e << ' '; }); // prints 1 2  3
}

https://godbolt.org/z/hnP4YT

Puzzle

  • Can you implement a generic iterate function which can iterate over any object?

    • Double points for applying concepts for overloading
auto iterate(auto&& t, auto&& expr) -> void {
  // TODO
}

int main() {
  using namespace boost::ut;
  using std::literals::string_view_literals::operator""sv;

  "iterate"_test = [] {
    std::stringstream str{};
    const auto out = [&str](const auto& value) { str << value << ' '; };

    "tuple"_test = [&] {
      std::tuple t = {42, "text", 3.14};
      iterate(t, out);
      expect("42 text 3.14 "sv == str.str());
    };

    "array"_test = [&] {
      std::array a{1, 2, 3};
      iterate(a, out);
      expect("1 2 3 "sv == str.str());
    };

    "vector"_test = [&] {
      std::vector v{1, 2, 3};
      iterate(v, out);
      expect("1 2 3 "sv == str.str());
    };

    "variant"_test = [&] {
      std::variant<int, std::string_view, double> v = 42;
      iterate(v, out);
      expect("42 "sv == str.str());
    };

    "array of tuples"_test = [&] {
      using tuple_t = std::tuple<int, std::string_view, double>;
      std::array at{tuple_t{1, "1", 1.}, tuple_t{2, "2", 2.}, tuple_t{3, "3", 3.}};
      iterate(at, out);
      expect("1 1 1 2 2 2 3 3 3 "sv == str.str());
    };

    "vector of variants"_test = [&] {
      using variant_t = std::variant<int, std::string_view, double>;
      std::vector<variant_t> vv = {42, "text", 3.14};
      iterate(vv, out);
      expect("42 text 3.14 "sv == str.str());
    };
  };
}

https://godbolt.org/z/WejovP

Solutions

namespace detail {
  template <class T>
  concept gettable = requires (T t) {
    std::get<0>(t);
  };

  template <class T>
  concept container = requires (T t) {
    t.begin();
    t.end();
    t.capacity();
  };

  template <class T>
  concept visitable = gettable<T> and requires (T t) {
    t.index();
  };

  template <class T, class TExpr>
  auto iterate(T&& t, TExpr&& expr) -> void {
    std::invoke(std::forward<TExpr>(expr), std::forward<T>(t));
  }

  template <container T>
  auto iterate(T&& t, auto&& expr) -> void;

  template <visitable T, class TExpr>
  auto iterate(T&& t, TExpr&& expr) -> void;

  template <gettable T>
  auto iterate(T&& t, auto&& expr) -> void {
    std::apply([&] <class... TElems> (TElems&&... elems) {
      (detail::iterate(std::forward<TElems>(elems), expr), ...);
    }, std::forward<T>(t));
  }

  template <container T>
  auto iterate(T&& t, auto&& expr) -> void {
    for (auto&& elem : std::forward<T>(t)) {
      detail::iterate(std::forward<decltype(elem)>(elem), expr);
    }
  }

  template <visitable T, class TExpr>
  auto iterate(T&& t, TExpr&& expr) -> void {
    std::visit([&] <class TElem> (TElem&& elem) {
      detail::iterate(std::forward<TElem>(elem), std::forward<TExpr>(expr));
    }, std::forward<T>(t));
  }
}

template <class T, class TExpr>
auto iterate(T&& t, TExpr&& expr) -> void {
  return detail::iterate(std::forward<T>(t), std::forward<TExpr>(expr));
}

https://godbolt.org/z/T1vWKh

template<typename Vis, typename Var>
concept CanIterateWithVisit = requires(Vis vis, Var var) {
    var.index();
    var.valueless_by_exception();
    std::get<0>(var);
};

template<typename UnaryF, typename C>
concept CanIterateWithForEach = requires(C c, UnaryF unaryF, C::const_iterator citer) {
    c.begin();
    c.end();
    std::for_each(citer, citer, unaryF);
};

template<typename Func, typename T>
concept CanIterateWithApply = requires(T t, Func func) {
    std::tuple_size<T>{};
    std::get<0>(t);
};

auto iterate(auto&& t, auto&& expr) -> void {
  using tType = std::remove_reference_t<decltype(t)>;
  using exprType = std::remove_reference_t<decltype(expr)>;

  if constexpr( CanIterateWithVisit<decltype(expr), tType>)
    std::visit(expr, t);
  else if constexpr( CanIterateWithForEach<decltype(expr), tType>)
    std::for_each( std::cbegin(t), std::cend(t), [&](const auto& arg) { iterate(arg, expr); } );
  else if constexpr( CanIterateWithApply< exprType, tType>)
    std::apply([&](const auto& ...args) { (expr(args),...); }, t);
  else
    expr(t);
}

https://godbolt.org/z/qdT53d

template <typename T>
concept For_Eachable = requires (const T& t) {
  std::for_each(t.cbegin(), t.cend(), [](const auto&) {});
};

template <template <typename...> typename, typename>
constexpr auto IsContainer = false;

template <template <typename...> typename C, typename... Ts>
constexpr auto IsContainer<C, C<Ts...>> = true;

void iterate(const auto& t, const auto& expr) {
  using T = decltype(t);

  if constexpr ( IsContainer<std::variant, std::decay_t<T>> ) {
    std::visit(expr, t);
  }
  else if constexpr ( IsContainer<std::tuple, std::decay_t<T>> ) {
    const auto f_apply = [&](const auto&... es) { (..., expr(es)); };

    std::apply(f_apply, t);
  }
  else if constexpr ( For_Eachable<T> ) {
    const auto f_for_each = [&](const auto& e) { iterate(e, expr); };

    std::for_each(t.cbegin(), t.cend(), f_for_each);
  }
  else {
    expr(t);
  }
}

https://godbolt.org/z/9noMrx

namespace detail {
    template<typename T>
    concept gettable = requires(T t) {
        std::get<0>(t);
    };

    template<typename T>
    concept indexable = requires(T t) {
        t.index();
    };

    template<typename T>
    concept container = requires(T t) {
        t.begin();
        t.end();
        t.size();
        t.capacity();
    };

    template<typename T>
    concept apply_iterable = gettable<T>;

    template<typename T>
    concept for_iterable = container<T>;

    template<typename T>
    concept visit_iterable = gettable<T> && indexable<T>;
}

template<detail::apply_iterable TIterable, typename TExpr>
auto iterate(TIterable&& t, TExpr&& expr) -> void {
    std::apply(
        [&](const auto&... args ){
            (iterate(args, expr), ...);
        },
        t);
}

template<detail::for_iterable TIterable, typename TExpr>
auto iterate(TIterable&& t, TExpr&& expr) -> void {
    for(const auto& val : t) {
        iterate(val, expr);
    }
}

template<detail::visit_iterable TIterable, typename TExpr>
auto iterate(TIterable&& t, TExpr&& expr) -> void {
    std::visit(
        [&](const auto& var){
            expr(var);
        },
        t);
}

template<typename TValue, typename TExpr>
auto iterate(TValue&& t, TExpr&& expr) -> void {
    expr(t);
}

https://godbolt.org/z/Enosbe

auto iterate(auto&& t, auto&& expr) -> void {
  expr(t);
}

template<class... Ts>
auto iterate(std::tuple<Ts...> t, auto&& expr) -> void {
  std::apply([expr](const auto&... args) { (expr(args), ...); }, t);
}

template<class... Ts>
auto iterate(std::variant<Ts...> v, auto&& expr) -> void {
  std::visit(expr, v);
}

template<class T, auto N>
auto iterate(std::array<T, N> a, auto&& expr) -> void {
  std::for_each(std::cbegin(a), std::cend(a), [expr](const auto& e){
    iterate(e, expr);
  });
}

template<class T>
auto iterate(std::vector<T> v, auto&& expr) -> void {
  std::for_each(std::cbegin(v), std::cend(v), [expr](const auto& e){
    iterate(e, expr);
  });
}

https://godbolt.org/z/96bjP5