From acfaaeda7aa78b27d6f5b81ff9bbd1b3b58c62c7 Mon Sep 17 00:00:00 2001 From: koe Date: Thu, 1 Dec 2022 17:25:26 -0600 Subject: [PATCH 1/6] add container helpers --- src/common/container_helpers.h | 106 +++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/common/container_helpers.h diff --git a/src/common/container_helpers.h b/src/common/container_helpers.h new file mode 100644 index 000000000..cd74b1ada --- /dev/null +++ b/src/common/container_helpers.h @@ -0,0 +1,106 @@ +// Copyright (c) 2022, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Miscellaneous container helpers. + +#pragma once + +//local headers + +//third party headers + +//standard headers +#include +#include +#include +#include +#include + +//forward declarations + + +namespace tools +{ + +/// use operator< to get operator== +/// WARNING: equality is not always implied by operator<, depending on implementation +struct equals_from_less final +{ + template + bool operator()(const T &a, const T &b) { return !(a < b) && !(b < a); } +}; +/// note: uniqueness uses 'equals_from_less' to match the use of operator< when testing if sorted +template +bool is_sorted_and_unique(const T& container) +{ + if (!std::is_sorted(container.begin(), container.end())) + return false; + + if (std::adjacent_find(container.begin(), container.end(), equals_from_less{}) != container.end()) + return false; + + return true; +} +/// convenience wrapper for checking if a mapped object is mapped to a key embedded in that object +template +bool keys_match_internal_values(const std::unordered_map &map, + const std::function< + const typename std::unordered_map::key_type& + (const typename std::unordered_map::mapped_type&) + > &get_internal_key_func) +{ + for (const auto &map_element : map) + { + if (!(map_element.first == get_internal_key_func(map_element.second))) + return false; + } + + return true; +} +/// convenience wrapper for getting the last element after emplacing back +template +typename ContainerT::value_type& add_element(ContainerT &container) +{ + container.emplace_back(); + return container.back(); +} +/// convenience erasor for unordered maps: std::erase_if(std::unordered_map) is C++20 +template +void for_all_in_map_erase_if(std::unordered_map &map_inout, + const std::function::value_type&)> &predicate) +{ + for (auto map_it = map_inout.begin(); map_it != map_inout.end();) + { + if (predicate(*map_it)) + map_it = map_inout.erase(map_it); + else + ++map_it; + } +} + +} //namespace tools From 3d604757533f5efe953fd9917a814c4371834895 Mon Sep 17 00:00:00 2001 From: koe Date: Thu, 15 Dec 2022 13:04:34 -0600 Subject: [PATCH 2/6] comment updates --- src/common/container_helpers.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/container_helpers.h b/src/common/container_helpers.h index cd74b1ada..6ac94b51a 100644 --- a/src/common/container_helpers.h +++ b/src/common/container_helpers.h @@ -54,7 +54,7 @@ struct equals_from_less final template bool operator()(const T &a, const T &b) { return !(a < b) && !(b < a); } }; -/// note: uniqueness uses 'equals_from_less' to match the use of operator< when testing if sorted +/// note: test for sorted and uniqueness using the same criteria (operator<) template bool is_sorted_and_unique(const T& container) { @@ -67,6 +67,8 @@ bool is_sorted_and_unique(const T& container) return true; } /// convenience wrapper for checking if a mapped object is mapped to a key embedded in that object +/// example: std::unorderd_map> where the map key is supposed to +/// reproduce the pair's rct::key; use the predicate to get the pair's rct::key element template bool keys_match_internal_values(const std::unordered_map &map, const std::function< From 73298734d64d68656aae2da687342761065ab4e2 Mon Sep 17 00:00:00 2001 From: koe Date: Sun, 18 Dec 2022 10:18:46 -0600 Subject: [PATCH 3/6] adjust is_sorted_and_unique() --- src/common/container_helpers.h | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/common/container_helpers.h b/src/common/container_helpers.h index 6ac94b51a..1865bd111 100644 --- a/src/common/container_helpers.h +++ b/src/common/container_helpers.h @@ -47,21 +47,20 @@ namespace tools { -/// use operator< to get operator== -/// WARNING: equality is not always implied by operator<, depending on implementation -struct equals_from_less final +/// test if a container is sorted and unique according to a comparison criteria (defaults to operator<) +template > +bool is_sorted_and_unique(const T &container, const ComparisonOpT &ComparisonOp = ComparisonOpT{}) { - template - bool operator()(const T &a, const T &b) { return !(a < b) && !(b < a); } -}; -/// note: test for sorted and uniqueness using the same criteria (operator<) -template -bool is_sorted_and_unique(const T& container) -{ - if (!std::is_sorted(container.begin(), container.end())) + if (!std::is_sorted(container.begin(), container.end(), ComparisonOp)) return false; - if (std::adjacent_find(container.begin(), container.end(), equals_from_less{}) != container.end()) + if (std::adjacent_find(container.begin(), + container.end(), + [&ComparisonOp](const typename T::value_type &a, const typename T::value_type &b) -> bool + { + return !ComparisonOp(a, b) && !ComparisonOp(b, a); + }) + != container.end()) return false; return true; From c60b11f3da40cf5dd6ccbd02aa441ce33198aa73 Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 19 Dec 2022 15:50:16 -0600 Subject: [PATCH 4/6] add compare_func() method so user-defined comparison functions are easier to use --- src/common/container_helpers.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/common/container_helpers.h b/src/common/container_helpers.h index 1865bd111..2c526bb4e 100644 --- a/src/common/container_helpers.h +++ b/src/common/container_helpers.h @@ -47,7 +47,14 @@ namespace tools { +/// convert a binary comparison function to a functor +template +inline auto compare_func(const ComparisonOpT &comparison_function) +{ + return [&comparison_function](const T &a, const T &b) -> bool { return comparison_function(a, b); }; +} /// test if a container is sorted and unique according to a comparison criteria (defaults to operator<) +/// NOTE: ComparisonOpT must establish 'strict weak ordering' https://en.cppreference.com/w/cpp/named_req/Compare template > bool is_sorted_and_unique(const T &container, const ComparisonOpT &ComparisonOp = ComparisonOpT{}) { @@ -65,6 +72,13 @@ bool is_sorted_and_unique(const T &container, const ComparisonOpT &ComparisonOp return true; } +/// specialization for raw function pointers +template +bool is_sorted_and_unique(const T &container, + bool (*const ComparisonOpFunc)(const typename T::value_type &a, const typename T::value_type &b)) +{ + return is_sorted_and_unique(container, compare_func(ComparisonOpFunc)); +} /// convenience wrapper for checking if a mapped object is mapped to a key embedded in that object /// example: std::unorderd_map> where the map key is supposed to /// reproduce the pair's rct::key; use the predicate to get the pair's rct::key element From e5aa058a8d9052bf6f4b58ba7bb9074909b6040a Mon Sep 17 00:00:00 2001 From: koe Date: Sat, 24 Dec 2022 19:15:27 -0600 Subject: [PATCH 5/6] vtnerd review comments --- src/common/container_helpers.h | 79 ++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/src/common/container_helpers.h b/src/common/container_helpers.h index 2c526bb4e..238f0ddad 100644 --- a/src/common/container_helpers.h +++ b/src/common/container_helpers.h @@ -36,10 +36,8 @@ //standard headers #include -#include #include #include -#include //forward declarations @@ -49,23 +47,50 @@ namespace tools /// convert a binary comparison function to a functor template -inline auto compare_func(const ComparisonOpT &comparison_function) +inline auto compare_func(ComparisonOpT comparison_op_func) { - return [&comparison_function](const T &a, const T &b) -> bool { return comparison_function(a, b); }; + static_assert( + std::is_same< + bool, + decltype( + comparison_op_func( + std::declval>(), + std::declval>() + ) + ) + >::value, + "invalid callable - expected callable in form bool(T, T)" + ); + + return [func = std::move(comparison_op_func)] (const T &a, const T &b) -> bool { return func(a, b); }; } /// test if a container is sorted and unique according to a comparison criteria (defaults to operator<) /// NOTE: ComparisonOpT must establish 'strict weak ordering' https://en.cppreference.com/w/cpp/named_req/Compare template > -bool is_sorted_and_unique(const T &container, const ComparisonOpT &ComparisonOp = ComparisonOpT{}) +bool is_sorted_and_unique(const T &container, ComparisonOpT comparison_op = ComparisonOpT{}) { - if (!std::is_sorted(container.begin(), container.end(), ComparisonOp)) + using ValueT = typename T::value_type; + static_assert( + std::is_same< + bool, + decltype( + comparison_op( + std::declval>(), + std::declval>() + ) + ) + >::value, + "invalid callable - expected callable in form bool(ValueT, ValueT)" + ); + + if (!std::is_sorted(container.begin(), container.end(), comparison_op)) return false; if (std::adjacent_find(container.begin(), container.end(), - [&ComparisonOp](const typename T::value_type &a, const typename T::value_type &b) -> bool + [comparison_op](const ValueT &a, const ValueT &b) -> bool { - return !ComparisonOp(a, b) && !ComparisonOp(b, a); + return !comparison_op(a, b) && !comparison_op(b, a); }) != container.end()) return false; @@ -75,20 +100,28 @@ bool is_sorted_and_unique(const T &container, const ComparisonOpT &ComparisonOp /// specialization for raw function pointers template bool is_sorted_and_unique(const T &container, - bool (*const ComparisonOpFunc)(const typename T::value_type &a, const typename T::value_type &b)) + bool (*const comparison_op_func)(const typename T::value_type &a, const typename T::value_type &b)) { - return is_sorted_and_unique(container, compare_func(ComparisonOpFunc)); + return is_sorted_and_unique(container, compare_func(comparison_op_func)); } /// convenience wrapper for checking if a mapped object is mapped to a key embedded in that object /// example: std::unorderd_map> where the map key is supposed to /// reproduce the pair's rct::key; use the predicate to get the pair's rct::key element -template -bool keys_match_internal_values(const std::unordered_map &map, - const std::function< - const typename std::unordered_map::key_type& - (const typename std::unordered_map::mapped_type&) - > &get_internal_key_func) +template +bool keys_match_internal_values(const std::unordered_map &map, PredT get_internal_key_func) { + static_assert( + std::is_same< + std::remove_cv_t>, + std::remove_cv_t>>())) + >> + >::value, + "invalid callable - expected callable in form Key(Value)" + ); + for (const auto &map_element : map) { if (!(map_element.first == get_internal_key_func(map_element.second))) @@ -105,10 +138,18 @@ typename ContainerT::value_type& add_element(ContainerT &container) return container.back(); } /// convenience erasor for unordered maps: std::erase_if(std::unordered_map) is C++20 -template -void for_all_in_map_erase_if(std::unordered_map &map_inout, - const std::function::value_type&)> &predicate) +template +void for_all_in_map_erase_if(std::unordered_map &map_inout, PredT predicate) { + using MapValueT = typename std::unordered_map::value_type; + static_assert( + std::is_same< + bool, + decltype(predicate(std::declval>>())) + >::value, + "invalid callable - expected callable in form bool(Value)" + ); + for (auto map_it = map_inout.begin(); map_it != map_inout.end();) { if (predicate(*map_it)) From f50b9e38037ea0c9b7c0d3d3daa3b39f21a38b32 Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 26 Dec 2022 11:36:44 -0600 Subject: [PATCH 6/6] revisions --- src/common/container_helpers.h | 40 ++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/common/container_helpers.h b/src/common/container_helpers.h index 238f0ddad..5824c9c37 100644 --- a/src/common/container_helpers.h +++ b/src/common/container_helpers.h @@ -45,8 +45,15 @@ namespace tools { +/// convert an arbitrary function to functor +template +inline auto as_functor(F f) +{ + return [f = std::move(f)](auto&&... args) { return f(std::forward(args)...); }; +} /// convert a binary comparison function to a functor -template +/// note: for most use-cases 'const T&' will work because only non-trivial types need a user-defined comparison operation +template inline auto compare_func(ComparisonOpT comparison_op_func) { static_assert( @@ -62,7 +69,7 @@ inline auto compare_func(ComparisonOpT comparison_op_func) "invalid callable - expected callable in form bool(T, T)" ); - return [func = std::move(comparison_op_func)] (const T &a, const T &b) -> bool { return func(a, b); }; + return as_functor(std::move(comparison_op_func)); } /// test if a container is sorted and unique according to a comparison criteria (defaults to operator<) /// NOTE: ComparisonOpT must establish 'strict weak ordering' https://en.cppreference.com/w/cpp/named_req/Compare @@ -88,7 +95,7 @@ bool is_sorted_and_unique(const T &container, ComparisonOpT comparison_op = Comp if (std::adjacent_find(container.begin(), container.end(), - [comparison_op](const ValueT &a, const ValueT &b) -> bool + [comparison_op = std::move(comparison_op)](const ValueT &a, const ValueT &b) -> bool { return !comparison_op(a, b) && !comparison_op(b, a); }) @@ -100,31 +107,32 @@ bool is_sorted_and_unique(const T &container, ComparisonOpT comparison_op = Comp /// specialization for raw function pointers template bool is_sorted_and_unique(const T &container, - bool (*const comparison_op_func)(const typename T::value_type &a, const typename T::value_type &b)) + bool (*const comparison_op_func)(const typename T::value_type&, const typename T::value_type&)) { return is_sorted_and_unique(container, compare_func(comparison_op_func)); } -/// convenience wrapper for checking if a mapped object is mapped to a key embedded in that object +/// convenience wrapper for checking if the key to a mapped object is correct for that object /// example: std::unorderd_map> where the map key is supposed to -/// reproduce the pair's rct::key; use the predicate to get the pair's rct::key element +/// reproduce the pair's rct::key; use the predicate to check that relationship template -bool keys_match_internal_values(const std::unordered_map &map, PredT get_internal_key_func) +bool keys_match_internal_values(const std::unordered_map &map, PredT check_key_func) { static_assert( std::is_same< - std::remove_cv_t>, - std::remove_cv_t>>())) - >> + bool, + decltype( + check_key_func( + std::declval>(), + std::declval>() + ) + ) >::value, - "invalid callable - expected callable in form Key(Value)" + "invalid callable - expected callable in form bool(KeyT, ValueT)" ); for (const auto &map_element : map) { - if (!(map_element.first == get_internal_key_func(map_element.second))) + if (!check_key_func(map_element.first, map_element.second)) return false; } @@ -145,7 +153,7 @@ void for_all_in_map_erase_if(std::unordered_map &map_inout, PredT static_assert( std::is_same< bool, - decltype(predicate(std::declval>>())) + decltype(predicate(std::declval>())) >::value, "invalid callable - expected callable in form bool(Value)" );