Skip to content

Instantly share code, notes, and snippets.

@loliGothicK
Created November 2, 2019 21:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save loliGothicK/366714c3c4e0a4fc9fff37f65fa487f4 to your computer and use it in GitHub Desktop.
Save loliGothicK/366714c3c4e0a4fc9fff37f65fa487f4 to your computer and use it in GitHub Desktop.
#ifndef MITAMA_RANGE_CONVERSION_HPP
#define MITAMA_RANGE_CONVERSION_HPP
#include <range/v3/all.hpp>
#include <utility>
#include <type_traits>
#include <tuple>
#include <mitama/mana/type_traits/is_tuple_like.hpp>
#include <boost/hana/functional/overload_linearly.hpp>
namespace mitama::_collect_fn_detail {
template <class Dictionary, class=void>
struct is_dictionary: std::false_type {};
template <class Dictionary>
struct is_dictionary<Dictionary, std::void_t<typename Dictionary::key_type, typename Dictionary::mapped_type>>: std::true_type {};
template <template <class...> class Container>
struct container_placeholder_t;
template <class From, class To, class=void>
struct is_convertible_container: std::false_type {};
template <class From, class To>
struct is_convertible_container<From, To, std::enable_if_t<std::is_convertible_v<From::value_type, To::value_type>>>
: std::true_type
{};
template <class From, template <class...> class To>
struct is_convertible_container<From, container_placeholder_t<To>,
std::enable_if_t<std::is_convertible_v<From::value_type, ranges::range_value_t<std::decay_t<From>>::value_type>>>
: std::true_type
{};
class collect_fn_tag {};
template <class T>
inline constexpr bool is_dictionary_v = is_dictionary<T>::value;
template <class, class, class=void>
struct _emplace_back: std::false_type {};
template <class T, class U>
struct _emplace_back<T,U,std::void_t<decltype(std::declval<T&>().emplace_back(std::declval<U>()))>>: std::true_type {};
template <class, class, class=void>
struct _emplace: std::false_type {};
template <class T, class U>
struct _emplace<T,U,std::void_t<decltype(std::declval<T&>().emplace(std::declval<U>()))>>: std::true_type {};
template <class, class, class=void>
struct _push_back: std::false_type {};
template <class T, class U>
struct _push_back<T,U,std::void_t<decltype(std::declval<T&>().push_back(std::declval<U>()))>>: std::true_type {};
template<class Container>
struct injected_collect_fn: private collect_fn_tag {
template<class Range>
constexpr Container
operator()(Range &&range) const {
auto decay_copy = [](auto&& v) -> std::decay_t<decltype(v)> { return std::forward<decltype(v)>(v); };
if constexpr (mitama::mana::is_tuple_like<ranges::range_value_t<std::decay_t<Range>>>::value) {
if constexpr (is_dictionary_v<Container>) {
Container ret{};
for (auto&& [key, value]: ranges::views::all(range)) {
ret.try_emplace(decay_copy(key), decay_copy(value));
}
return ret;
}
else {
static_assert([]{ return false; }(), "unexpected type specified");
}
}
else {
Container ret{};
for (auto&& item : ranges::views::all(range)) {
if constexpr (_emplace_back<Container, decltype(item)>::value)
{ ret.emplace_back(item); }
else if constexpr (_emplace<Container, decltype(item)>::value)
{ ret.emplace(item); }
else if constexpr (_push_back<Container, decltype(item)>::value)
{ ret.push_back(item); }
}
return ret;
}
}
};
template<template<class...> class Container>
struct generic_collect_fn: private collect_fn_tag {
template<class Range>
constexpr auto
operator()(Range &&range) const {
auto decay_copy = [](auto&& v) -> std::decay_t<decltype(v)> { return std::forward<decltype(v)>(v); };
if constexpr (mitama::mana::is_tuple_like<ranges::range_value_t<std::decay_t<Range>>>::value) {
if constexpr (is_dictionary_v<Container<std::decay_t<std::tuple_element_t<0, ranges::range_value_t<std::decay_t<Range>>>>,
std::decay_t<std::tuple_element_t<1, ranges::range_value_t<std::decay_t<Range>>>>>>) {
Container<std::decay_t<std::tuple_element_t<0, ranges::range_value_t<std::decay_t<Range>>>>,
std::decay_t<std::tuple_element_t<1, ranges::range_value_t<std::decay_t<Range>>>>> ret{};
for (auto&& [key, value]: ranges::views::all(range)) {
ret.try_emplace(decay_copy(key), decay_copy(value));
}
return ret;
}
else {
static_assert([]{ return false; }(), "unexpected type specified");
}
}
else {
Container<ranges::range_value_t<std::decay_t<Range>>> ret{};
for (auto&& item : ranges::views::all(range)) {
if constexpr (_emplace_back<Container<ranges::range_value_t<std::decay_t<Range>>>, decltype(item)>::value)
{ ret.emplace_back(item); }
else if constexpr (_emplace<Container<ranges::range_value_t<std::decay_t<Range>>>, decltype(item)>::value)
{ ret.emplace(item); }
else if constexpr (_push_back<Container<ranges::range_value_t<std::decay_t<Range>>>, decltype(item)>::value)
{ ret.push_back(item); }
}
return ret;
}
}
};
namespace _back_magic {
template <class Fn>
struct closure {};
template <class Range, class Fn>
auto operator|(Range&& range, closure<Fn>) {
return std::invoke(Fn{}, std::forward<Range>(range));
}
struct to_container {
template <class Range, class Fn>
friend auto operator|(Range&& range, closure<Fn>(*)(to_container)) {
return std::invoke(Fn{}, std::forward<Range>(range));
}
};
}
}
namespace mitama {
template<class Container>
inline constexpr auto
collect(_collect_fn_detail::_back_magic::to_container = {}) noexcept
-> _collect_fn_detail::_back_magic::closure<_collect_fn_detail::injected_collect_fn<Container>>
{ return {}; }
template<class Container, class Range>
inline constexpr auto
collect(Range&& range)
{ return std::invoke(_collect_fn_detail::injected_collect_fn<Container>{}, std::forward<Range>(range)); }
template<template<class...> class Container>
inline constexpr auto
collect(_collect_fn_detail::_back_magic::to_container = {}) noexcept
-> _collect_fn_detail::_back_magic::closure<_collect_fn_detail::generic_collect_fn<Container>>
{ return {}; }
template<template<class...> class Container, class Range>
inline constexpr auto
collect(Range&& range)
{ return std::invoke(_collect_fn_detail::generic_collect_fn<Container>{}, std::forward<Range>(range)); }
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment