Created
November 2, 2019 21:29
-
-
Save loliGothicK/366714c3c4e0a4fc9fff37f65fa487f4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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