Skip to content

Commit

Permalink
Defer Invokable test in ranges::action, fix up many action concept te…
Browse files Browse the repository at this point in the history
…sts, fixes #632
  • Loading branch information
ericniebler committed Apr 7, 2017
1 parent 714f590 commit ea9e8e0
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 19 deletions.
13 changes: 12 additions & 1 deletion include/range/v3/action/action.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,18 @@ namespace ranges
private:
Action action_;
friend pipeable_access;

template<typename Rng, typename...Rest>
using ActionConcept = meta::and_<
Range<Rng>,
Invocable<Action const&, Rng, Rest...>>;

template<typename Rng>
using ActionPipeConcept = meta::and_<
Invocable<Action&, Rng>,
Range<Rng>,
meta::not_<std::is_reference<Rng>>>;

// Pipeing requires things are passed by value.
template<typename Rng, typename Act,
CONCEPT_REQUIRES_(ActionPipeConcept<Rng>())>
Expand All @@ -76,6 +83,7 @@ namespace ranges
(
invoke(act.action_, detail::move(rng))
)

#ifndef RANGES_DOXYGEN_INVOKED
// For better error messages:
template<typename Rng, typename Act,
Expand All @@ -94,19 +102,22 @@ namespace ranges
"Or, wrap the argument with std::ref to pass it by reference.");
}
#endif

public:
action() = default;
action(Action a)
: action_(detail::move(a))
{}

// Calling directly requires things are passed by reference.
template<typename Rng, typename...Rest,
CONCEPT_REQUIRES_(Range<Rng &>() && Invocable<Action const&, Rng &, Rest...>())>
CONCEPT_REQUIRES_(ActionConcept<Rng &, Rest...>())>
auto operator()(Rng & rng, Rest &&... rest) const
RANGES_DECLTYPE_AUTO_RETURN
(
invoke(action_, rng, static_cast<Rest&&>(rest)...)
)

// Currying overload.
template<typename T, typename...Rest, typename A = Action>
auto operator()(T && t, Rest &&... rest) const
Expand Down
8 changes: 6 additions & 2 deletions include/range/v3/action/join.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ namespace ranges
template<typename Rng>
using Concept = meta::and_<
InputRange<Rng>,
InputRange<range_value_type_t<Rng>>,
SemiRegular<join_value_t<Rng>>>;
meta::lazy::invoke<
meta::compose<meta::quote<InputRange>, meta::quote<range_value_type_t>>,
Rng>,
meta::lazy::invoke<
meta::compose<meta::quote<SemiRegular>, meta::quote<join_value_t>>,
Rng>>;

template<typename Rng,
CONCEPT_REQUIRES_(Concept<Rng>())>
Expand Down
4 changes: 2 additions & 2 deletions include/range/v3/action/reverse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ namespace ranges
friend action_access;

public:
template<typename Rng, typename I = iterator_t<Rng>,
CONCEPT_REQUIRES_(BidirectionalRange<Rng>() && Permutable<I>())>
template<typename Rng,
CONCEPT_REQUIRES_(reverse_detail::Concept<Rng>())>
Rng operator()(Rng && rng) const
{
ranges::reverse(rng);
Expand Down
7 changes: 6 additions & 1 deletion include/range/v3/action/sort.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ namespace ranges
public:
template<typename Rng, typename C = ordered_less, typename P = ident>
using Concept = meta::and_<
ForwardRange<Rng>, Sortable<iterator_t<Rng>, C, P>>;
ForwardRange<Rng>,
meta::lazy::invoke<
meta::compose<
meta::bind_back<meta::quote<Sortable>, C, P>,
meta::quote<iterator_t>>,
Rng>>;

template<typename Rng, typename C = ordered_less, typename P = ident,
CONCEPT_REQUIRES_(Concept<Rng, C, P>())>
Expand Down
19 changes: 7 additions & 12 deletions include/range/v3/action/stable_sort.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,14 @@ namespace ranges
protect(std::move(proj)))
)
public:
struct ConceptImpl
{
template<typename Rng, typename C = ordered_less, typename P = ident,
typename I = iterator_t<Rng>>
auto requires_() -> decltype(
concepts::valid_expr(
concepts::model_of<concepts::ForwardRange, Rng>(),
concepts::is_true(Sortable<I, C, P>())
));
};

template<typename Rng, typename C = ordered_less, typename P = ident>
using Concept = concepts::models<ConceptImpl, Rng, C, P>;
using Concept = meta::and_<
ForwardRange<Rng>,
meta::lazy::invoke<
meta::compose<
meta::bind_back<meta::quote<Sortable>, C, P>,
meta::quote<iterator_t>>,
Rng>>;

template<typename Rng, typename C = ordered_less, typename P = ident,
CONCEPT_REQUIRES_(Concept<Rng, C, P>())>
Expand Down
4 changes: 4 additions & 0 deletions include/range/v3/view/view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ namespace ranges
(
v.view_(static_cast<Rng&&>(rng))
)

#ifndef RANGES_DOXYGEN_INVOKED
// For better error messages:
template<typename Rng, typename Vw,
Expand All @@ -110,11 +111,13 @@ namespace ranges
"a named variable, and then pipe it to the view.");
}
#endif

public:
view() = default;
view(View a)
: view_(std::move(a))
{}

// Calling directly requires View arguments or lvalue containers.
template<typename Rng, typename...Rest,
CONCEPT_REQUIRES_(ViewConcept<Rng, Rest...>())>
Expand All @@ -123,6 +126,7 @@ namespace ranges
(
view_(static_cast<Rng&&>(rng), static_cast<Rest&&>(rest)...)
)

// Currying overload.
template<typename...Ts, typename V = View>
auto operator()(Ts &&... ts) const
Expand Down
6 changes: 6 additions & 0 deletions test/action/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ add_test(test.act.drop_while act.drop_while)
add_executable(act.insert insert.cpp)
add_test(test.act.insert act.insert)

add_executable(act.join join.cpp)
add_test(test.act.join act.join)

add_executable(act.push_front push_front.cpp)
add_test(test.act.push_front act.push_front)

Expand All @@ -34,6 +37,9 @@ add_test(test.act.sort act.sort)
add_executable(act.split split.cpp)
add_test(test.act.split act.split)

add_executable(act.stable_sort stable_sort.cpp)
add_test(test.act.stable_sort act.stable_sort)

add_executable(act.stride stride.cpp)
add_test(test.act.stride act.stride)

Expand Down
35 changes: 35 additions & 0 deletions test/action/join.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Range v3 library
//
// Copyright Eric Niebler 2014
//
// 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)

#include <vector>
#include <range/v3/core.hpp>
#include <range/v3/action/join.hpp>
#include <range/v3/algorithm/move.hpp>
#include <range/v3/algorithm/equal.hpp>
#include <range/v3/view/transform.hpp>
#include "../simple_test.hpp"
#include "../test_utils.hpp"

template<class> struct undef;

int main()
{
using namespace ranges;

std::vector<std::string> v {"hello"," ","world"};
auto s = v | move | action::join;
static_assert(std::is_same<decltype(s), std::string>::value, "");
CHECK(s == "hello world");

auto s2 = v | view::transform(view::all) | action::join;
static_assert(std::is_same<decltype(s2), std::vector<char>>::value, "");
CHECK(equal(s, s2));

return ::test_result();
}
2 changes: 1 addition & 1 deletion test/action/sort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ int main()
v |= action::shuffle(gen);
CHECK(!is_sorted(v));

auto v2 = v | copy | action::sort;
auto v2 = v | copy | action::sort(std::less<int>());;
CHECK(size(v2) == size(v));
CHECK(is_sorted(v2));
CHECK(!is_sorted(v));
Expand Down
84 changes: 84 additions & 0 deletions test/action/stable_sort.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Range v3 library
//
// Copyright Eric Niebler 2014
//
// 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)

#include <array>
#include <random>
#include <vector>
#include <range/v3/core.hpp>
#include <range/v3/view/iota.hpp>
#include <range/v3/view/stride.hpp>
#include <range/v3/view/take.hpp>
#include <range/v3/algorithm/shuffle.hpp>
#include <range/v3/algorithm/copy.hpp>
#include <range/v3/algorithm/move.hpp>
#include <range/v3/algorithm/is_sorted.hpp>
#include <range/v3/algorithm/equal.hpp>
#include <range/v3/action/shuffle.hpp>
#include <range/v3/action/stable_sort.hpp>
#include "../simple_test.hpp"
#include "../test_utils.hpp"

void test_bug632()
{
const std::vector<double> scores = { 3.0, 1.0, 2.0 };
std::vector<int> indices = { 0, 1, 2 };

indices |= ranges::action::stable_sort(
ranges::less{},
[&] (const int &x) { return scores[ (std::size_t)x ]; }
);

::check_equal( indices, {1, 2, 0} );
}

int main()
{
using namespace ranges;
std::mt19937 gen;

std::vector<int> v = view::ints(0,100);
v |= action::shuffle(gen);
CHECK(!is_sorted(v));

auto v2 = v | copy | action::stable_sort;
CHECK(size(v2) == size(v));
CHECK(is_sorted(v2));
CHECK(!is_sorted(v));
::models<concepts::Same>(v, v2);

v |= action::stable_sort;
CHECK(is_sorted(v));

v |= action::shuffle(gen);
CHECK(!is_sorted(v));

v = v | move | action::stable_sort(std::less<int>());
CHECK(is_sorted(v));
CHECK(equal(v, v2));

// Container algorithms can also be called directly
// in which case they take and return by reference
shuffle(v, gen);
CHECK(!is_sorted(v));
auto & v3 = action::stable_sort(v);
CHECK(is_sorted(v));
CHECK(&v3 == &v);

auto ref=std::ref(v);
ref |= action::stable_sort;

// Can pipe a view to a "container" algorithm.
action::stable_sort(v, std::greater<int>());
v | view::stride(2) | action::stable_sort;
check_equal(view::take(v, 10), {1,98,3,96,5,94,7,92,9,90});

test_bug632();

return ::test_result();
}

0 comments on commit ea9e8e0

Please sign in to comment.