diff --git a/src/util/range.hpp b/src/util/range.hpp index 2ed703df96ae92dd1d8130a64ff00fe7dddeb501..6c161fda604aa396227a5f0894b006c8e11f3692 100644 --- a/src/util/range.hpp +++ b/src/util/range.hpp @@ -151,6 +151,9 @@ range<U, V> make_range(const U& left, const V& right) { return range<U, V>(left, right); } +// Present a possibly sentinel-terminated range as an STL-compatible sequence +// using the sentinel_iterator adaptor. + template <typename Seq> auto canonical_view(Seq& s) -> range<sentinel_iterator_t<decltype(std::begin(s)), decltype(std::end(s))>> @@ -165,6 +168,21 @@ auto canonical_view(const Seq& s) -> return {make_sentinel_iterator(std::begin(s), std::end(s)), make_sentinel_end(std::begin(s), std::end(s))}; } +// Strictly evaluate end point in sentinel-terminated range and present as a range over +// iterators. Note: O(N) behaviour with forward iterator ranges or sentinel-terminated ranges. + +template <typename Seq> +auto strict_view(Seq& s) -> range<decltype(std::begin(s))> +{ + return make_range(std::begin(s), std::next(util::upto(std::begin(s), std::end(s)))); +} + +template <typename Seq> +auto strict_view(const Seq& s) -> range<decltype(std::begin(s))> +{ + return make_range(std::begin(s), std::next(util::upto(std::begin(s), std::end(s)))); +} + } // namespace util } // namespace mc } // namespace nest diff --git a/src/util/rangeutil.hpp b/src/util/rangeutil.hpp index 95200b9c9a5b37c6a02bc236aa1d86bec8dac629..d87edc93be85b7211717d979bbbd86506b174574 100644 --- a/src/util/rangeutil.hpp +++ b/src/util/rangeutil.hpp @@ -75,9 +75,18 @@ AssignableContainer& assign_by(AssignableContainer& c, const Seq& seq, const Pro } // Sort in-place +// Note that a const range reference may wrap non-const iterators. template <typename Seq> -void sort(Seq& seq) { +enable_if_t<!std::is_const<typename util::sequence_traits<Seq>::reference>::value> +sort(Seq& seq) { + auto canon = canonical_view(seq); + std::sort(std::begin(canon), std::end(canon)); +} + +template <typename Seq> +enable_if_t<!std::is_const<typename util::sequence_traits<Seq>::reference>::value> +sort(const Seq& seq) { auto canon = canonical_view(seq); std::sort(std::begin(canon), std::end(canon)); } @@ -85,7 +94,20 @@ void sort(Seq& seq) { // Sort in-place by projection `proj` template <typename Seq, typename Proj> -void sort_by(Seq& seq, const Proj& proj) { +enable_if_t<!std::is_const<typename util::sequence_traits<Seq>::reference>::value> +sort_by(Seq& seq, const Proj& proj) { + using value_type = typename sequence_traits<Seq>::value_type; + auto canon = canonical_view(seq); + + std::sort(std::begin(canon), std::end(canon), + [&proj](const value_type& a, const value_type& b) { + return proj(a) < proj(b); + }); +} + +template <typename Seq, typename Proj> +enable_if_t<!std::is_const<typename util::sequence_traits<Seq>::reference>::value> +sort_by(const Seq& seq, const Proj& proj) { using value_type = typename sequence_traits<Seq>::value_type; auto canon = canonical_view(seq); diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 1c286863c3e92a9c8f2a11eba8679f66094b3c72..b67683ecebd803018185f9b09b18be7a774dd097 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -1,13 +1,3 @@ -set(HEADERS - ${PROJECT_SOURCE_DIR}/src/cell.hpp - ${PROJECT_SOURCE_DIR}/src/cell_tree.hpp - ${PROJECT_SOURCE_DIR}/src/math.hpp - ${PROJECT_SOURCE_DIR}/src/point.hpp - ${PROJECT_SOURCE_DIR}/src/segment.hpp - ${PROJECT_SOURCE_DIR}/src/swcio.hpp - ${PROJECT_SOURCE_DIR}/src/tree.hpp -) - set(TEST_SOURCES # unit tests test_algorithms.cpp @@ -47,7 +37,7 @@ set(TEST_SOURCES ) add_definitions("-DDATADIR=\"${CMAKE_SOURCE_DIR}/data\"") -add_executable(test.exe ${TEST_SOURCES} ${HEADERS}) +add_executable(test.exe ${TEST_SOURCES}) set(TARGETS test.exe) diff --git a/tests/unit/test_range.cpp b/tests/unit/test_range.cpp index 3cf2893e451c9674e85ba7ffa384a24e3a6eb6f5..1f1e3cb60847ba896eb3bd2ffed447f43dc0c93b 100644 --- a/tests/unit/test_range.cpp +++ b/tests/unit/test_range.cpp @@ -184,6 +184,16 @@ TEST(range, sentinel) { EXPECT_EQ(0u, empty_cstr_range.size()); } +TEST(range, strictify) { + const char *cstr = "hello world"; + auto cstr_range = util::make_range(cstr, null_terminated); + + auto ptr_range = util::strict_view(cstr_range); + EXPECT_TRUE((std::is_same<decltype(ptr_range), util::range<const char *>>::value)); + EXPECT_EQ(cstr, ptr_range.left); + EXPECT_EQ(cstr+11, ptr_range.right); +} + template <typename V> class counter_range: public ::testing::Test {}; @@ -290,12 +300,15 @@ TEST(range, sort) { auto cstr_range = util::make_range(std::begin(cstr), null_terminated); + // Alas, no forward_iterator sort yet, so make a strict (non-sentinel) + // range to sort on below + // simple sort - util::sort(cstr_range); + util::sort(util::strict_view(cstr_range)); EXPECT_EQ(std::string("dhowy"), cstr); // reverse sort by transform c to -c - util::sort_by(cstr_range, [](char c) { return -c; }); + util::sort_by(util::strict_view(cstr_range), [](char c) { return -c; }); EXPECT_EQ(std::string("ywohd"), cstr); }