diff --git a/src/common/container_helpers.h b/src/common/container_helpers.h new file mode 100644 index 000000000..5824c9c37 --- /dev/null +++ b/src/common/container_helpers.h @@ -0,0 +1,170 @@ +// 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 + +//forward declarations + + +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 +/// 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( + std::is_same< + bool, + decltype( + comparison_op_func( + std::declval>(), + std::declval>() + ) + ) + >::value, + "invalid callable - expected callable in form bool(T, T)" + ); + + 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 +template > +bool is_sorted_and_unique(const T &container, ComparisonOpT comparison_op = ComparisonOpT{}) +{ + 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(), + [comparison_op = std::move(comparison_op)](const ValueT &a, const ValueT &b) -> bool + { + return !comparison_op(a, b) && !comparison_op(b, a); + }) + != container.end()) + return false; + + return true; +} +/// specialization for raw function pointers +template +bool is_sorted_and_unique(const T &container, + 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 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 check that relationship +template +bool keys_match_internal_values(const std::unordered_map &map, PredT check_key_func) +{ + static_assert( + std::is_same< + bool, + decltype( + check_key_func( + std::declval>(), + std::declval>() + ) + ) + >::value, + "invalid callable - expected callable in form bool(KeyT, ValueT)" + ); + + for (const auto &map_element : map) + { + if (!check_key_func(map_element.first, 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, 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)) + map_it = map_inout.erase(map_it); + else + ++map_it; + } +} + +} //namespace tools