Forked from
ogs / ogs
1048 commits behind the upstream repository.
-
Lars Bilke authoredLars Bilke authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Algorithm.h 10.08 KiB
/**
* \file
*
* \copyright
* Copyright (c) 2012-2024, OpenGeoSys Community (http://www.opengeosys.org)
* Distributed under a Modified BSD License.
* See accompanying file LICENSE.txt or
* http://www.opengeosys.org/project/license
*/
#pragma once
#include <algorithm>
#include <cassert>
#include <concepts>
#include <optional>
#include <range/v3/algorithm/find_if.hpp>
#include <range/v3/range/concepts.hpp>
#include <string>
#include <typeindex>
#include <typeinfo>
#include <utility>
#include "Error.h"
namespace BaseLib
{
/// excludeObjectCopy copies only those objects that position within the source
/// vector is not in the exclude_positions vector. The implementation of the
/// algorithm requires that the given positions in exclude_positions are sorted
/// in ascending order.
/// @param src_vec the vector of source objects
/// @param exclude_positions the positions of objects in the source vector that
/// do not have to be copied
/// @return vector that contains the copied objects
template <typename T>
std::vector<T> excludeObjectCopy(
std::vector<T> const& src_vec,
std::vector<std::size_t> const& exclude_positions)
{
std::vector<T> dest_vec;
if (exclude_positions.empty())
{
dest_vec = src_vec;
return dest_vec;
}
assert(exclude_positions.back() < src_vec.size());
std::copy_n(src_vec.cbegin(), exclude_positions[0],
std::back_inserter(dest_vec));
for (std::size_t i = 1; i < exclude_positions.size(); ++i)
{
std::copy_n(src_vec.cbegin() + exclude_positions[i - 1] + 1,
exclude_positions[i] - (exclude_positions[i - 1] + 1),
std::back_inserter(dest_vec));
}
std::copy(src_vec.cbegin() + exclude_positions.back() + 1, src_vec.cend(),
std::back_inserter(dest_vec));
return dest_vec;
}
template <typename T>
void excludeObjectCopy(std::vector<T> const& src_vec,
std::vector<std::size_t> const& exclude_positions,
std::vector<T>& dest_vec)
{
dest_vec = excludeObjectCopy(src_vec, exclude_positions);
}
/// Returns reference to an element in the range satisfying the predicate. If no
/// such element is found, error_callback is called and reference to
/// past-the-end of the range is returned.
template <ranges::input_range Range>
ranges::range_reference_t<Range> findElementOrError(
Range& range,
std::predicate<ranges::range_reference_t<Range>> auto&& predicate,
std::invocable auto error_callback)
{
auto it =
ranges::find_if(range, std::forward<decltype(predicate)>(predicate));
if (it == ranges::end(range))
{
error_callback();
OGS_FATAL(
"Element not found in the input range. The user provided error "
"callback is meant not to return. That has not happened.");
}
return *it;
}
//! Inserts the given \c key with the given \c value into the \c map if an entry
//! with the
//! given \c key does not yet exist; otherwise an \c error_message is printed
//! and the
//! program is aborted.
template <typename Map, typename Key, typename Value>
void insertIfKeyUniqueElseError(Map& map, Key const& key, Value&& value,
std::string const& error_message)
{
auto const inserted = map.emplace(key, std::forward<Value>(value));
if (!inserted.second)
{ // insertion failed, i.e., key already exists
OGS_FATAL("{} Key `{}' already exists.", error_message, key);
}
}
//! Returns the value of \c key from the given \c map if such an entry exists;
//! otherwise an \c error_message is printed and the program is aborted.
//! Cf. also the const overload below.
template <typename Map, typename Key>
typename Map::mapped_type& getOrError(Map& map, Key const& key,
std::string const& error_message)
{
auto it = map.find(key);
if (it == map.end())
{
if constexpr (std::is_convertible<Key, std::string>::value)
{
OGS_FATAL("{:s} Key `{:s}' does not exist.", error_message, key);
}
else
{
OGS_FATAL("{:s} Key `{:s}' does not exist.", error_message,
std::to_string(key));
}
}
return it->second;
}
//! \overload
template <typename Map, typename Key>
typename Map::mapped_type const& getOrError(Map const& map, Key const& key,
std::string const& error_message)
{
auto it = map.find(key);
if (it == map.end())
{
if constexpr (std::is_convertible<Key, std::string>::value)
{
OGS_FATAL("{:s} Key `{:s}' does not exist.", error_message, key);
}
else
{
OGS_FATAL("{:s} Key `{:s}' does not exist.", error_message,
std::to_string(key));
}
}
return it->second;
}
//! Returns the value of from the given \c container if such an entry fulfilling
//! the \c predicate exists;
//! otherwise an \c error_message is printed and the program is aborted.
template <typename Container, typename Predicate>
typename Container::value_type const& getIfOrError(
Container const& container,
Predicate&& predicate,
std::string const& error_message)
{
auto it = std::find_if(begin(container), end(container), predicate);
if (it == end(container))
{
OGS_FATAL("Could not find element matching the predicate: {:s}",
error_message);
}
return *it;
}
/// Make the entries of the std::vector \c v unique. The remaining entries will
/// be sorted.
template <typename T>
void makeVectorUnique(std::vector<T>& v)
{
std::sort(v.begin(), v.end());
auto it = std::unique(v.begin(), v.end());
v.erase(it, v.end());
}
/// Make the entries of the std::vector \c v unique using the given binary
/// function. The remaining entries will be sorted.
template <typename T, class Compare>
void makeVectorUnique(std::vector<T>& v, Compare comp)
{
std::sort(v.begin(), v.end(), comp);
auto it = std::unique(v.begin(), v.end());
v.erase(it, v.end());
}
/**
* Reorder a vector by a given index vector.
*
* Note: It is good enough in performance for medium size vectors.
*/
template <typename ValueType, typename IndexType>
void reorderVector(std::vector<ValueType>& v,
std::vector<IndexType> const& order)
{
std::vector<ValueType> temp_v(v.size());
temp_v.swap(v);
for (std::size_t i = 0; i < order.size(); i++)
{
std::swap(v[i], temp_v[order[i]]);
}
}
template <typename Container>
void uniquePushBack(Container& container,
typename Container::value_type const& element)
{
if (std::find(container.begin(), container.end(), element) ==
container.end())
{
container.push_back(element);
}
}
template <typename Container>
std::optional<typename Container::value_type> findFirstNotEqualElement(
Container const& container, typename Container::value_type const& element)
{
auto const it =
std::find_if_not(container.begin(), container.end(),
[&element](typename Container::value_type const& e)
{ return e == element; });
return it == container.end() ? std::nullopt : std::make_optional(*it);
}
/// Returns the index of first element in container or, if the element is not
/// found a std::size_t maximum value.
///
/// The maximum value of std::size_t is chosen, because such an index cannot
/// exist in a container; the maximum index is std::size_t::max-1.
template <typename Container>
std::size_t findIndex(Container const& container,
typename Container::value_type const& element)
{
auto const it = std::find(container.begin(), container.end(), element);
if (it == container.end())
{
return std::numeric_limits<std::size_t>::max();
}
return std::distance(container.begin(), it);
}
/** Function to destruct objects stored in a container as pointers. */
template <typename T>
void cleanupVectorElements(std::vector<T*>& items)
{
for (auto item : items)
{
delete item;
}
items.clear();
}
/** Util function to cleanup the memory of multiple containers containing
* pointers to objects. Sometimes, there are dependencies between the pointer
* items in the containers. For instance, a GeoLib::Polyline or a
* GeoLib::Surface depends on the GeoLib::Point pointers stored in a
* std::vector<GeoLib::Point*>. Then, the dependent items have to cleaned up
* before the GeoLib::Point objects are deleted. A similar relation exists
* between MeshLib::Element objects and MeshLib::Node objects.*/
template <typename T1, typename... Args>
void cleanupVectorElements(std::vector<T1*>& dependent_items, Args&&... args)
{
cleanupVectorElements(dependent_items);
cleanupVectorElements(std::forward<Args>(args)...);
}
/// Checks if any of the elements in the given list is true.
template <typename List>
constexpr bool any_of(List const& values)
{
// std::any_of is not constexpr enough in some STLs
for (auto& value : values)
{
if (static_cast<bool>(value))
{
return true;
}
}
return false;
}
/// Checks if all of the elements in the given list are true.
template <typename List>
constexpr bool all_of(List const& values)
{
// std::all_of is not constexpr enough in some STLs
for (auto& value : values)
{
if (!static_cast<bool>(value))
{
return false;
}
}
return true;
}
/// Checks if none of the elements in the given list are true.
template <typename List>
constexpr bool none_of(List const& values)
{
return !any_of(values);
}
/// A utility to combine multiple lambda functions into a single overloaded
/// function object.
/// Can be used with `std::visit` or similar functions requiring a callable that
/// handles multiple types.
template <class... Ts>
struct Overloaded : Ts...
{
using Ts::operator()...;
};
#if defined(__clang__)
#if (__clang_major__ <= 16)
/// Explicit deduction guide needed for clang <= 16.
template <class... Ts>
Overloaded(Ts...) -> Overloaded<Ts...>;
#endif
#endif
} // namespace BaseLib