From 2ce1db89784597d79ec2fe2c1135551676835b94 Mon Sep 17 00:00:00 2001 From: Morwenn Date: Sun, 14 Aug 2016 17:34:43 +0200 Subject: [PATCH] Add case_insensitive_less (issue #81) --- .../comparators/case_insensitive_less.h | 130 ++++++++++++++++++ testsuite/CMakeLists.txt | 1 + .../comparators/case_insensitive_less.cpp | 69 ++++++++++ 3 files changed, 200 insertions(+) create mode 100644 include/cpp-sort/comparators/case_insensitive_less.h create mode 100644 testsuite/comparators/case_insensitive_less.cpp diff --git a/include/cpp-sort/comparators/case_insensitive_less.h b/include/cpp-sort/comparators/case_insensitive_less.h new file mode 100644 index 00000000..dd253934 --- /dev/null +++ b/include/cpp-sort/comparators/case_insensitive_less.h @@ -0,0 +1,130 @@ +/* + * 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_CASE_INSENSITIVE_LESS_H_ +#define CPPSORT_COMPARATORS_CASE_INSENSITIVE_LESS_H_ + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +namespace cppsort +{ + namespace detail + { + //////////////////////////////////////////////////////////// + // Case insensitive comparison for char sequences + + template + struct char_less + { + const std::ctype& ct; + + char_less(const std::ctype& ct): + ct(ct) + {} + + auto operator()(CharT lhs, CharT rhs) const + -> bool + { + return ct.tolower(lhs) < ct.tolower(rhs); + } + }; + + template + auto case_insensitive_less(const T& lhs, const T& rhs, const std::locale loc) + -> bool + { + using char_type = std::decay_t; + const auto& ct = std::use_facet>(loc); + + return std::lexicographical_compare(std::begin(lhs), std::end(lhs), + std::begin(rhs), std::end(rhs), + char_less(ct)); + } + + template + auto case_insensitive_less(const T& lhs, const T& rhs) + -> bool + { + std::locale loc; + return case_insensitive_less(lhs, rhs, loc); + } + + //////////////////////////////////////////////////////////// + // Customization point + + class case_insensitive_less_locale_fn + { + private: + + const std::locale& loc; + + public: + + explicit case_insensitive_less_locale_fn(const std::locale& loc): + loc(loc) + {} + + template + constexpr auto operator()(T&& lhs, U&& rhs) const + noexcept(noexcept(case_insensitive_less(std::forward(lhs), std::forward(rhs), loc))) + -> decltype(case_insensitive_less(std::forward(lhs), std::forward(rhs), loc)) + { + return case_insensitive_less(std::forward(lhs), std::forward(rhs), loc); + } + }; + + struct case_insensitive_less_fn + { + template + constexpr auto operator()(T&& lhs, U&& rhs) const + noexcept(noexcept(case_insensitive_less(std::forward(lhs), std::forward(rhs)))) + -> decltype(case_insensitive_less(std::forward(lhs), std::forward(rhs))) + { + return case_insensitive_less(std::forward(lhs), std::forward(rhs)); + } + + inline auto operator()(const std::locale& loc) const + -> case_insensitive_less_locale_fn + { + return case_insensitive_less_locale_fn(loc); + } + }; + } + + namespace + { + constexpr auto&& case_insensitive_less = utility::static_const< + detail::case_insensitive_less_fn + >::value; + } +} + +#endif // CPPSORT_COMPARATORS_CASE_INSENSITIVE_LESS_H_ diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index 2ad09af4..e31fa63b 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -28,6 +28,7 @@ set( set( COMPARATORS_TESTS + comparators/case_insensitive_less.cpp comparators/natural_less.cpp comparators/total_less.cpp ) diff --git a/testsuite/comparators/case_insensitive_less.cpp b/testsuite/comparators/case_insensitive_less.cpp new file mode 100644 index 00000000..218d73db --- /dev/null +++ b/testsuite/comparators/case_insensitive_less.cpp @@ -0,0 +1,69 @@ +/* + * 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 +#include +#include +#include +#include + +TEST_CASE( "case-insensitive string comparison with case_insensitive_less" ) +{ + std::array array = { + "awry", + "Banana", + "greaTEr", + "bounTy", + "eXpected", + "aROma", + "excellent", + "GreaT", + "aROused" + }; + + std::array expected = { + "aROma", + "aROused", + "awry", + "Banana", + "bounTy", + "excellent", + "eXpected", + "GreaT", + "greaTEr" + }; + + SECTION( "implicit global locale" ) + { + cppsort::sort(array, cppsort::case_insensitive_less); + CHECK( array == expected ); + } + + SECTION( "explicit global locale" ) + { + std::locale locale; + cppsort::sort(array, cppsort::case_insensitive_less(locale)); + CHECK( array == expected ); + } +} +