Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
2 contributors

Users who have contributed to this file

@ericniebler @CaseyCarter
548 lines (495 sloc) 21.1 KB
/// \file
// Range v3 library
//
// Copyright Eric Niebler 2013-present
//
// Use, modification and distribution is subject to the
// Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// Project home: https://github.com/ericniebler/range-v3
//
#ifndef RANGES_V3_RANGE_CONVERSION_HPP
#define RANGES_V3_RANGE_CONVERSION_HPP
#include <meta/meta.hpp>
#include <range/v3/range_fwd.hpp>
#include <range/v3/action/concepts.hpp>
#include <range/v3/functional/pipeable.hpp>
#include <range/v3/iterator/common_iterator.hpp>
#include <range/v3/range/concepts.hpp>
#include <range/v3/range/traits.hpp>
#include <range/v3/utility/static_const.hpp>
#ifndef RANGES_NO_STD_FORWARD_DECLARATIONS
// Non-portable forward declarations of standard containers
RANGES_BEGIN_NAMESPACE_STD
RANGES_BEGIN_NAMESPACE_CONTAINER
template<typename Value, typename Alloc /*= allocator<Value>*/>
class vector;
RANGES_END_NAMESPACE_CONTAINER
RANGES_END_NAMESPACE_STD
#else
#include <vector>
#endif
#include <range/v3/detail/disable_warnings.hpp>
namespace ranges
{
/// \cond
namespace detail
{
struct to_container
{
template<typename MetaFn>
struct fn;
template<typename MetaFn, typename Fn>
struct closure;
template<typename MetaFn, typename Rng>
using container_t = meta::invoke<MetaFn, Rng>;
template<typename Rng, typename MetaFn>
friend auto operator|(Rng && rng,
closure<MetaFn, fn<MetaFn>> (*)(to_container))
-> CPP_broken_friend_ret(container_t<MetaFn, Rng>)( //
requires invocable<fn<MetaFn>, Rng>)
{
return fn<MetaFn>{}(static_cast<Rng &&>(rng));
}
template<typename MetaFn, typename Pipeable>
friend auto operator|(closure<MetaFn, fn<MetaFn>> (*)(to_container),
Pipeable pipe)
-> CPP_broken_friend_ret(
closure<MetaFn, composed<Pipeable, fn<MetaFn>>>)( //
requires(is_pipeable_v<Pipeable>))
{
return closure<MetaFn, composed<Pipeable, fn<MetaFn>>>{
compose(static_cast<Pipeable &&>(pipe), fn<MetaFn>{})};
}
};
// A simple, light-weight transform iterator that applies ranges::to
// to each element in the range. Used by ranges::to to convert a range
// of ranges into a container of containers.
template<typename Rng, typename Cont>
struct to_container_iterator
{
private:
using I = range_cpp17_iterator_t<Rng>;
using ValueType = range_value_t<Cont>;
I it_;
public:
using difference_type = typename std::iterator_traits<I>::difference_type;
using value_type = ValueType;
using reference = ValueType;
using pointer = typename std::iterator_traits<I>::pointer;
using iterator_category = typename std::iterator_traits<I>::iterator_category;
to_container_iterator() = default;
template<typename OtherIt>
to_container_iterator(OtherIt it)
: it_(std::move(it))
{}
friend bool operator==(to_container_iterator const & a,
to_container_iterator const & b)
{
return a.it_ == b.it_;
}
friend bool operator!=(to_container_iterator const & a,
to_container_iterator const & b)
{
return !(a == b);
}
reference operator*() const
{
return to_container::fn<meta::id<ValueType>>{}(*it_);
}
to_container_iterator & operator++()
{
++it_;
return *this;
}
to_container_iterator operator++(int)
{
auto tmp = *this;
++it_;
return tmp;
}
CPP_member
auto operator--() -> CPP_ret(to_container_iterator &)(
requires derived_from<iterator_category, std::bidirectional_iterator_tag>)
{
--it_;
return *this;
}
CPP_member
auto operator--(int) -> CPP_ret(to_container_iterator &)(
requires derived_from<iterator_category, std::bidirectional_iterator_tag>)
{
auto tmp = *this;
++it_;
return tmp;
}
CPP_member
auto operator+=(difference_type n) -> CPP_ret(to_container_iterator &)(
requires derived_from<iterator_category, std::random_access_iterator_tag>)
{
it_ += n;
return *this;
}
CPP_member
auto operator-=(difference_type n) -> CPP_ret(to_container_iterator &)(
requires derived_from<iterator_category, std::random_access_iterator_tag>)
{
it_ -= n;
return *this;
}
CPP_broken_friend_member
friend auto operator+(to_container_iterator i, difference_type n) //
-> CPP_broken_friend_ret(to_container_iterator)(
requires derived_from<iterator_category,
std::random_access_iterator_tag>)
{
return i += n;
}
CPP_broken_friend_member
friend auto operator-(to_container_iterator i, difference_type n) //
-> CPP_broken_friend_ret(to_container_iterator)(
requires derived_from<iterator_category,
std::random_access_iterator_tag>)
{
return i -= n;
}
CPP_broken_friend_member
friend auto operator-(difference_type n, to_container_iterator i) //
-> CPP_broken_friend_ret(to_container_iterator)(
requires derived_from<iterator_category,
std::random_access_iterator_tag>)
{
return i -= n;
}
CPP_broken_friend_member
friend auto operator-(to_container_iterator const & i,
to_container_iterator const & j) //
-> CPP_broken_friend_ret(difference_type)(
requires derived_from<iterator_category,
std::random_access_iterator_tag>)
{
return i.it_ - j.it_;
}
CPP_member
auto operator[](difference_type n) const -> CPP_ret(reference)(
requires derived_from<iterator_category, std::random_access_iterator_tag>)
{
return *(*this + n);
}
};
template<typename Rng, typename Cont>
using to_container_iterator_t =
enable_if_t<(bool)range<Rng>, to_container_iterator<Rng, Cont>>;
// clang-format off
template<typename Cont>
CPP_concept_fragment(has_allocator_type_, (Cont),
ranges::type<typename Cont::allocator_type>
);
template<typename Cont>
CPP_concept_bool has_allocator_type =
CPP_fragment(detail::has_allocator_type_, Cont);
template<typename Rng>
CPP_concept_bool range_and_not_view =
defer::range<Rng> && !defer::view_<Rng>;
template<typename Rng, typename Cont>
CPP_concept_fragment(convertible_to_cont_impl_frag_, (Rng, Cont),
constructible_from<range_value_t<Cont>, range_reference_t<Rng>> &&
constructible_from<
Cont,
range_cpp17_iterator_t<Rng>,
range_cpp17_iterator_t<Rng>>
);
template<typename Rng, typename Cont>
CPP_concept_bool convertible_to_cont_impl_ =
range_and_not_view<Cont> && move_constructible<Cont> &&
CPP_fragment(detail::convertible_to_cont_impl_frag_, Rng, Cont);
template<typename Rng, typename Cont>
CPP_concept_fragment(convertible_to_cont_cont_impl_frag_, (Rng, Cont),
range_and_not_view<range_value_t<Cont>> &&
// Test that each element of the input range can be ranges::to<>
// to the output container.
invocable<
to_container::fn<meta::id<range_value_t<Cont>>>,
range_reference_t<Rng>> &&
constructible_from<
Cont,
to_container_iterator_t<Rng, Cont>,
to_container_iterator_t<Rng, Cont>>
);
template<typename Rng, typename Cont>
CPP_concept_bool convertible_to_cont_cont_impl_ =
range<Cont> && (!view_<Cont>) && move_constructible<Cont> &&
CPP_fragment(detail::convertible_to_cont_cont_impl_frag_, Rng, Cont);
namespace defer
{
template<typename Cont>
CPP_concept has_allocator_type =
CPP_defer(detail::has_allocator_type, Cont);
template<typename Rng, typename Cont>
CPP_concept convertible_to_cont_impl_ =
CPP_defer(detail::convertible_to_cont_impl_, Rng, Cont);
template<typename Rng, typename Cont>
CPP_concept convertible_to_cont_cont_impl_ =
CPP_defer(detail::convertible_to_cont_cont_impl_, Rng, Cont);
}
template<typename Rng, typename Cont>
CPP_concept_bool convertible_to_cont =
defer::has_allocator_type<Cont> && // HACKHACK
defer::convertible_to_cont_impl_<Rng, Cont>;
template<typename Rng, typename Cont>
CPP_concept_bool convertible_to_cont_cont =
defer::has_allocator_type<Cont> && // HACKHACK
defer::convertible_to_cont_cont_impl_<Rng, Cont>;
namespace defer
{
template<typename Rng, typename Cont>
CPP_concept convertible_to_cont =
CPP_defer(detail::convertible_to_cont, Rng, Cont);
template<typename Rng, typename Cont>
CPP_concept convertible_to_cont_cont =
CPP_defer(detail::convertible_to_cont_cont, Rng, Cont);
}
template<typename C, typename I, typename R>
CPP_concept_bool to_container_reserve =
reservable_with_assign<C, I> &&
sized_range<R>;
template<typename MetaFn, typename Rng>
using container_t = meta::invoke<MetaFn, Rng>;
template<typename Rng, typename MetaFn>
CPP_concept_bool convertible_to_cont_cont_or_cont =
defer::convertible_to_cont_cont<Rng, container_t<MetaFn, Rng>> ||
defer::convertible_to_cont<Rng, container_t<MetaFn, Rng>>;
template<typename MetaFn, typename Rng>
CPP_concept_bool convertible_to_cont_and_not_cont_cont =
!defer::convertible_to_cont_cont<Rng, container_t<MetaFn, Rng>> && //
defer::convertible_to_cont<Rng, container_t<MetaFn, Rng>>;
namespace defer
{
template<typename Rng, typename MetaFn>
CPP_concept convertible_to_cont_cont_or_cont =
CPP_defer(detail::convertible_to_cont_cont_or_cont, Rng, MetaFn);
}
// clang-format on
struct RANGES_STRUCT_WITH_ADL_BARRIER(to_container_closure_base)
{
CPP_template(typename Rng, typename MetaFn, typename Fn)( //
requires ranges::defer::input_range<Rng> && //
defer::convertible_to_cont_cont_or_cont<Rng, MetaFn>) //
friend constexpr auto
operator|(Rng && rng, to_container::closure<MetaFn, Fn> fn)
{
return static_cast<Fn &&>(fn)(static_cast<Rng &&>(rng));
}
template<typename MetaFn, typename Fn, typename Pipeable>
friend constexpr auto operator|(to_container::closure<MetaFn, Fn> sh,
Pipeable pipe)
-> CPP_broken_friend_ret(
to_container::closure<MetaFn, composed<Pipeable, Fn>>)(
requires(is_pipeable_v<Pipeable>))
{
return to_container::closure<MetaFn, composed<Pipeable, Fn>>{
compose(static_cast<Pipeable &&>(pipe), static_cast<Fn &&>(sh))};
}
};
template<typename MetaFn, typename Fn>
struct to_container::closure
: to_container_closure_base
, Fn
{
closure() = default;
constexpr explicit closure(Fn fn)
: Fn(static_cast<Fn &&>(fn))
{}
};
template<typename MetaFn>
struct to_container::fn
{
private:
template<typename Cont, typename I, typename Rng>
static Cont impl(Rng && rng, std::false_type)
{
return Cont(I{ranges::begin(rng)}, I{ranges::end(rng)});
}
template<typename Cont, typename I, typename Rng>
static auto impl(Rng && rng, std::true_type)
{
Cont c;
auto const rng_size = ranges::size(rng);
using size_type = decltype(c.max_size());
using C = common_type_t<range_size_t<Rng>, size_type>;
RANGES_EXPECT(static_cast<C>(rng_size) <= static_cast<C>(c.max_size()));
c.reserve(static_cast<size_type>(rng_size));
c.assign(I{ranges::begin(rng)}, I{ranges::end(rng)});
return c;
}
public:
template<typename Rng>
auto operator()(Rng && rng) const -> CPP_ret(container_t<MetaFn, Rng>)( //
requires input_range<Rng> && //
convertible_to_cont_and_not_cont_cont<MetaFn, Rng>)
{
static_assert(!is_infinite<Rng>::value,
"Attempt to convert an infinite range to a container.");
using cont_t = container_t<MetaFn, Rng>;
using iter_t = range_cpp17_iterator_t<Rng>;
using use_reserve_t =
meta::bool_<(bool)to_container_reserve<cont_t, iter_t, Rng>>;
return impl<cont_t, iter_t>(static_cast<Rng &&>(rng), use_reserve_t{});
}
template<typename Rng>
auto operator()(Rng && rng) const -> CPP_ret(container_t<MetaFn, Rng>)( //
requires input_range<Rng> && //
convertible_to_cont_cont<Rng, container_t<MetaFn, Rng>>)
{
static_assert(!is_infinite<Rng>::value,
"Attempt to convert an infinite range to a container.");
using cont_t = container_t<MetaFn, Rng>;
using iter_t = to_container_iterator<Rng, cont_t>;
using use_reserve_t =
meta::bool_<(bool)to_container_reserve<cont_t, iter_t, Rng>>;
return impl<cont_t, iter_t>(static_cast<Rng &&>(rng), use_reserve_t{});
}
};
template<typename MetaFn, typename Fn>
using to_container_closure = to_container::closure<MetaFn, Fn>;
template<typename MetaFn>
using to_container_fn = to_container_closure<MetaFn, to_container::fn<MetaFn>>;
template<template<typename...> class ContT>
struct from_range
{
#if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
// Attempt to use a deduction guide first...
template<typename Rng>
static auto from_rng_(int) -> decltype(ContT(range_cpp17_iterator_t<Rng>{},
range_cpp17_iterator_t<Rng>{}));
// No deduction guide. Fallback to instantiating with the
// iterator's value type.
template<typename Rng>
static auto from_rng_(long)
-> meta::invoke<meta::quote<ContT>, range_value_t<Rng>>;
template<typename Rng>
using invoke = decltype(from_range::from_rng_<Rng>(0));
#else
template<typename Rng>
using invoke = meta::invoke<meta::quote<ContT>, range_value_t<Rng>>;
#endif
};
} // namespace detail
/// \endcond
/// \addtogroup group-range
/// @{
/// \ingroup group-range
RANGES_INLINE_VARIABLE(detail::to_container_fn<detail::from_range<std::vector>>,
to_vector)
/// \cond
namespace _to_
{
/// \endcond
/// \brief For initializing a container of the specified type with the elements of
/// an Range
template<template<typename...> class ContT>
auto to(RANGES_HIDDEN_DETAIL(detail::to_container = {}))
-> detail::to_container_fn<detail::from_range<ContT>>
{
return {};
}
/// \overload
template<template<typename...> class ContT, typename Rng>
auto to(Rng && rng) -> CPP_ret(ContT<range_value_t<Rng>>)( //
requires range<Rng> &&
detail::convertible_to_cont<Rng, ContT<range_value_t<Rng>>>)
{
return detail::to_container_fn<detail::from_range<ContT>>{}(
static_cast<Rng &&>(rng));
}
/// \overload
template<typename Cont>
auto to(RANGES_HIDDEN_DETAIL(detail::to_container = {}))
-> detail::to_container_fn<meta::id<Cont>>
{
return {};
}
/// \overload
template<typename Cont, typename Rng>
auto to(Rng && rng) -> CPP_ret(Cont)( //
requires range<Rng> && detail::convertible_to_cont<Rng, Cont>)
{
return detail::to_container_fn<meta::id<Cont>>{}(static_cast<Rng &&>(rng));
}
/// \cond
// Slightly odd initializer_list overloads, undocumented for now.
template<template<typename...> class ContT, typename T>
auto to(std::initializer_list<T> il) -> CPP_ret(ContT<T>)( //
requires detail::convertible_to_cont<std::initializer_list<T>, ContT<T>>)
{
return detail::to_container_fn<detail::from_range<ContT>>{}(il);
}
template<typename Cont, typename T>
auto to(std::initializer_list<T> il) -> CPP_ret(Cont)( //
requires detail::convertible_to_cont<std::initializer_list<T>, Cont>)
{
return detail::to_container_fn<meta::id<Cont>>{}(il);
}
/// \endcond
/// \cond
} // namespace _to_
using namespace _to_;
/// \endcond
/// @}
////////////////////////////////////////////////////////////////////////////
/// \cond
namespace _to_
{
// The old name "ranges::to_" is now deprecated:
template<template<typename...> class ContT>
RANGES_DEPRECATED("Please use ranges::to (no underscore) instead.")
auto to_(detail::to_container = {})
-> detail::to_container_fn<detail::from_range<ContT>>
{
return {};
}
template<template<typename...> class ContT, typename Rng>
RANGES_DEPRECATED("Please use ranges::to (no underscore) instead.")
auto to_(Rng && rng) -> CPP_ret(ContT<range_value_t<Rng>>)( //
requires range<Rng> &&
detail::convertible_to_cont<Rng, ContT<range_value_t<Rng>>>)
{
return static_cast<Rng &&>(rng) | ranges::to_<ContT>();
}
template<template<typename...> class ContT, typename T>
RANGES_DEPRECATED("Please use ranges::to (no underscore) instead.")
auto to_(std::initializer_list<T> il) -> CPP_ret(ContT<T>)( //
requires detail::convertible_to_cont<std::initializer_list<T>, ContT<T>>)
{
return il | ranges::to_<ContT>();
}
template<typename Cont>
RANGES_DEPRECATED("Please use ranges::to (no underscore) instead.")
auto to_(detail::to_container = {}) -> detail::to_container_fn<meta::id<Cont>>
{
return {};
}
template<typename Cont, typename Rng>
RANGES_DEPRECATED("Please use ranges::to (no underscore) instead.")
auto to_(Rng && rng) -> CPP_ret(Cont)( //
requires range<Rng> && detail::convertible_to_cont<Rng, Cont>)
{
return static_cast<Rng &&>(rng) | ranges::to_<Cont>();
}
template<typename Cont, typename T>
RANGES_DEPRECATED("Please use ranges::to (no underscore) instead.")
auto to_(std::initializer_list<T> list) -> CPP_ret(Cont)( //
requires detail::convertible_to_cont<std::initializer_list<T>, Cont>)
{
return list | ranges::to_<Cont>();
}
} // namespace _to_
/// \endcond
template<typename MetaFn, typename Fn>
RANGES_INLINE_VAR constexpr bool
is_pipeable_v<detail::to_container_closure<MetaFn, Fn>> = true;
} // namespace ranges
#include <range/v3/detail/reenable_warnings.hpp>
#endif
You can’t perform that action at this time.