Skip to content

Commit

Permalink
Implement natural_less as a customization point (issue #81)
Browse files Browse the repository at this point in the history
The default version works with any forward sequence of char, including
std::string, std::vector<char> or char[]. It might work with other types
of characters than char, but it isn't guaranteed since it uses
std::isdigit.
  • Loading branch information
Morwenn committed Aug 6, 2016
1 parent 52b3554 commit c6bc5b1
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 0 deletions.
139 changes: 139 additions & 0 deletions include/cpp-sort/comparators/natural_less.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Morwenn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef CPPSORT_COMPARATORS_NATURAL_LESS_H_
#define CPPSORT_COMPARATORS_NATURAL_LESS_H_

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <cctype>
#include <iterator>
#include <utility>
#include <cpp-sort/utility/static_const.h>

namespace cppsort
{
namespace detail
{
////////////////////////////////////////////////////////////
// Natural order for char sequences

template<typename ForwardIterator>
auto natural_less_impl(ForwardIterator begin1, ForwardIterator end1,
ForwardIterator begin2, ForwardIterator end2)
-> bool
{
while (begin1 != end1 && begin2 != end2) {
auto last1 = begin1;
auto last2 = begin2;
do {
if (not std::isdigit(*last1)) break;
++last1;
} while (last1 != end1);
do {
if (not std::isdigit(*last2)) break;
++last2;
} while (last2 != end2);

if (last1 != begin1 && last2 != begin2) {
// Skip leading zeros
do {
if (*begin1 != '0') break;
++begin1;
} while (begin1 != last1);
do {
if (*begin2 != '0') break;
++begin2;
} while (begin2 != last2);

// Compare numbers
auto size1 = std::distance(begin1, last1);
auto size2 = std::distance(begin2, last2);
if (size1 != size2) {
return size1 < size2;
}
if (size1 == 0) {
return false;
}

// Sizes are equal and not 0
while (begin1 != end1) {
if (*begin1 != *begin2) {
return *begin1 < *begin2;
}
++begin1;
++begin2;
}
}

if (begin1 == end1) {
return begin2 != end2;
}
if (begin2 == end2) {
return false;
}

if (*begin1 != *begin2) {
return *begin1 < *begin2;
}

++begin1;
++begin2;
}

return begin1 == end1 && begin2 != end2;
}

template<typename T>
auto natural_less(const T& lhs, const T& rhs)
-> bool
{
return natural_less_impl(std::begin(lhs), std::end(lhs),
std::begin(rhs), std::end(rhs));
}

////////////////////////////////////////////////////////////
// Customization point

struct natural_less_fn
{
template<typename T, typename U>
constexpr auto operator()(T&& lhs, U&& rhs) const
noexcept(noexcept(natural_less(std::forward<T>(lhs), std::forward<U>(rhs))))
-> decltype(natural_less(std::forward<T>(lhs), std::forward<U>(rhs)))
{
return natural_less(std::forward<T>(lhs), std::forward<U>(rhs));
}
};
}

namespace
{
constexpr auto&& natural_less = utility::static_const<
detail::natural_less_fn
>::value;
}
}

#endif // CPPSORT_COMPARATORS_NATURAL_LESS_H_
1 change: 1 addition & 0 deletions testsuite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(
set(
COMPARATORS_TESTS

comparators/natural_less.cpp
comparators/total_less.cpp
)

Expand Down
54 changes: 54 additions & 0 deletions testsuite/comparators/natural_less.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Morwenn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <array>
#include <string>
#include <catch.hpp>
#include <cpp-sort/comparators/natural_less.h>
#include <cpp-sort/sort.h>

TEST_CASE( "string natural sort with natural_less" )
{
std::array<std::string, 7> array = {
"Yay",
"Yay 32 lol",
"Yuy 32 lol",
"Yay 045",
"Yay 01245 huhuhu",
"Yay 45",
"Yay 1234"
};
cppsort::sort(array, cppsort::natural_less);

std::array<std::string, 7> expected = {
"Yay",
"Yay 32 lol",
"Yay 45",
"Yay 045",
"Yay 1234",
"Yay 01245 huhuhu",
"Yuy 32 lol"
};
CHECK( array == expected );
}

0 comments on commit c6bc5b1

Please sign in to comment.