Files
Goober5000 10edea2291 add contains, at_or, and value_or to the SCP maps
Extend the various maps to add a `contains` function, just as several other containers already add `contains`.

Also add `at_or` and `value_or` functions to provide ways to query for map values without inserting anything by default, as `operator[]` does.  If the map does not contain the key, the default value is returned.  Use `at_or` to return a const reference and `value_or` to return a value.  (These are only added to `SCP_map` and `SCP_unordered_map`; a `SCP_multimap` equivalent isn't as clearly useful.)

Also add Class Template Argument Deduction (CTAD) guides, per existing precedent.  And delete function overloads that are intrinsic footguns.
2026-08-01 23:49:27 -04:00

292 lines
9.9 KiB
C++

#ifndef _VMALLOCATOR_H_INCLUDED_
#define _VMALLOCATOR_H_INCLUDED_
#include <algorithm>
#include <deque>
#include <iterator>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <queue>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
template <typename T>
class SCP_vector : public std::vector<T, std::allocator<T>>
{
public:
using std::vector<T, std::allocator<T>>::vector; // inherit all constructors
// this is needed as a workaround to a GCC compiler bug when using a templated accessor...
// error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
inline auto size() const noexcept { return std::vector<T, std::allocator<T>>::size(); }
bool contains(const T& item) const
{
return std::find(this->begin(), this->end(), item) != this->end();
}
void concat(SCP_vector<T>&& other)
{
insert(this->end(), std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()));
}
void concat(const SCP_vector<T>& other)
{
insert(this->end(), other.begin(), other.end());
}
bool in_bounds(int idx) const
{
return (idx >= 0) && (static_cast<size_t>(idx) < this->size());
}
bool in_bounds(size_t idx) const
{
return idx < this->size();
}
};
template<typename T> SCP_vector(std::initializer_list<T>) -> SCP_vector<T>;
template <typename T>
bool SCP_vector_contains(const SCP_vector<T>& vector, const T& item) {
return std::find(vector.begin(), vector.end(), item) != vector.end();
}
template <typename T>
bool SCP_vector_contains_lcase(const SCP_vector<T>& vector, const T& item) {
return std::find_if(vector.begin(), vector.end(), [&item](const T& iterator_item) { return lcase_equal(iterator_item, item); }) != vector.end();
}
template <typename T>
bool SCP_vector_contains_lcase(const SCP_vector<T>& vector, const char* item) {
return std::find_if(vector.begin(), vector.end(), [&item](const T& iterator_item) { return !stricmp(iterator_item.c_str(), item); }) != vector.end();
}
template <typename T>
inline bool SCP_vector_inbounds(const SCP_vector<T>& vector, int idx) {
return ((idx >= 0) && (static_cast<size_t>(idx) < vector.size()));
}
template <typename T>
inline bool SCP_vector_inbounds(const SCP_vector<T>& vector, size_t idx) {
return idx < vector.size();
}
template <typename T>
class SCP_list : public std::list<T, std::allocator<T>>
{
public:
using std::list<T, std::allocator<T>>::list; // inherit all constructors
bool contains(const T& item) const
{
return std::find(this->begin(), this->end(), item) != this->end();
}
};
template<typename T> SCP_list(std::initializer_list<T>) -> SCP_list<T>;
extern std::locale SCP_default_locale;
template <class charT>
[[nodiscard]] charT SCP_toupper(charT ch) { return std::toupper(ch, SCP_default_locale); }
template <class charT>
[[nodiscard]] charT SCP_tolower(charT ch) { return std::tolower(ch, SCP_default_locale); }
typedef std::basic_string<char, std::char_traits<char>, std::allocator<char>> SCP_string;
typedef std::basic_stringstream<char, std::char_traits<char>, std::allocator<char>> SCP_stringstream;
extern void SCP_tolower(char *str);
extern void SCP_toupper(char *str);
extern void SCP_totitle(char *str);
extern void SCP_tolower(SCP_string &str);
extern void SCP_toupper(SCP_string &str);
extern void SCP_totitle(SCP_string &str);
extern bool SCP_truncate(SCP_string &str, size_t len);
extern bool SCP_trim(SCP_string& str);
extern bool lcase_equal(const SCP_string& _Left, const SCP_string& _Right);
extern bool lcase_lessthan(const SCP_string& _Left, const SCP_string& _Right);
template <typename T, typename U, typename Less = std::less<T>>
class SCP_map : public std::map<T, U, Less, std::allocator<std::pair<const T, U>>>
{
public:
using std::map<T, U, Less, std::allocator<std::pair<const T, U>>>::map; // inherit all constructors
bool contains(const T& key) const
{
return this->find(key) != this->end();
}
// returns a copy of the value at the key, or a copy of default_value if the key is not present;
// mirrors std::optional::value_or, and accepts any default that is convertible to the mapped type
template <typename V = U>
U value_or(const T& key, V&& default_value) const
{
auto it = this->find(key);
return (it != this->end()) ? it->second : static_cast<U>(std::forward<V>(default_value));
}
// returns a reference to the value at the key, or to default_value if the key is not present;
// the map and default_value must both outlive the returned reference (use value_or otherwise)
const U& at_or(const T& key, const U& default_value) const&
{
auto it = this->find(key);
return (it != this->end()) ? it->second : default_value;
}
// deleted to prevent returning a reference to a temporary default, which would dangle
const U& at_or(const T& key, const U&& default_value) const& = delete;
// deleted to prevent returning a reference into a temporary map, which would dangle
const U& at_or(const T& key, const U& default_value) && = delete;
const U& at_or(const T& key, const U& default_value) const&& = delete;
};
template<typename T, typename U> SCP_map(std::initializer_list<std::pair<T, U>>) -> SCP_map<T, U>;
template <typename T, typename U, typename Less = std::less<T>>
class SCP_multimap : public std::multimap<T, U, Less, std::allocator<std::pair<const T, U>>>
{
public:
using std::multimap<T, U, Less, std::allocator<std::pair<const T, U>>>::multimap; // inherit all constructors
bool contains(const T& key) const
{
return this->find(key) != this->end();
}
};
template<typename T, typename U> SCP_multimap(std::initializer_list<std::pair<T, U>>) -> SCP_multimap<T, U>;
template <typename T>
using SCP_queue = std::queue<T, std::deque<T, std::allocator<T>>>;
template <typename T>
using SCP_deque = std::deque<T, std::allocator<T>>;
template <typename T, typename Less = std::less<T>>
class SCP_set : public std::set<T, Less, std::allocator<T>>
{
public:
using std::set<T, Less, std::allocator<T>>::set; // inherit all constructors
bool contains(const T& item) const
{
return this->find(item) != this->end();
}
};
template<typename T> SCP_set(std::initializer_list<T>) -> SCP_set<T>;
template <typename T, typename Less = std::less<T>>
class SCP_multiset : public std::multiset<T, Less, std::allocator<T>>
{
public:
using std::multiset<T, Less, std::allocator<T>>::multiset; // inherit all constructors
bool contains(const T& item) const
{
return this->find(item) != this->end();
}
};
template<typename T> SCP_multiset(std::initializer_list<T>) -> SCP_multiset<T>;
// Now that the codebase is on C++17, we can use the standard hash. (Enum classes are not hashable on C++ < 14.)
template <typename T>
using SCP_hash = std::hash<T>;
struct SCP_string_lcase_hash {
size_t operator()(const SCP_string& elem) const {
SCP_string lcase_copy;
std::transform(elem.begin(), elem.end(), std::back_inserter(lcase_copy),
[](char c) {
return SCP_tolower(c);
});
return SCP_hash<SCP_string>()(lcase_copy);
}
};
struct SCP_string_lcase_equal_to {
bool operator()(const SCP_string& _Left, const SCP_string& _Right) const {
return lcase_equal(_Left, _Right);
}
};
struct SCP_string_lcase_less_than {
bool operator()(const SCP_string& _Left, const SCP_string& _Right) const {
return lcase_lessthan(_Left, _Right);
}
};
template <typename Key, typename T, typename Hash = SCP_hash<Key>, typename KeyEqual = std::equal_to<Key>>
class SCP_unordered_map : public std::unordered_map<Key, T, Hash, KeyEqual, std::allocator<std::pair<const Key, T>>>
{
public:
using std::unordered_map<Key, T, Hash, KeyEqual, std::allocator<std::pair<const Key, T>>>::unordered_map; // inherit all constructors
bool contains(const Key& key) const
{
return this->find(key) != this->end();
}
// returns a copy of the value at the key, or a copy of default_value if the key is not present;
// mirrors std::optional::value_or, and accepts any default that is convertible to the mapped type
template <typename V = T>
T value_or(const Key& key, V&& default_value) const
{
auto it = this->find(key);
return (it != this->end()) ? it->second : static_cast<T>(std::forward<V>(default_value));
}
// returns a reference to the value at the key, or to default_value if the key is not present;
// the map and default_value must both outlive the returned reference (use value_or otherwise)
const T& at_or(const Key& key, const T& default_value) const&
{
auto it = this->find(key);
return (it != this->end()) ? it->second : default_value;
}
// deleted to prevent returning a reference to a temporary default, which would dangle
const T& at_or(const Key& key, const T&& default_value) const& = delete;
// deleted to prevent returning a reference into a temporary map, which would dangle
const T& at_or(const Key& key, const T& default_value) && = delete;
const T& at_or(const Key& key, const T& default_value) const&& = delete;
};
template<typename Key, typename T> SCP_unordered_map(std::initializer_list<std::pair<Key, T>>) -> SCP_unordered_map<Key, T>;
template <typename Key, typename Hash = SCP_hash<Key>, typename KeyEqual = std::equal_to<Key>>
class SCP_unordered_set : public std::unordered_set<Key, Hash, KeyEqual, std::allocator<Key>>
{
public:
using std::unordered_set<Key, Hash, KeyEqual, std::allocator<Key>>::unordered_set; // inherit all constructors
bool contains(const Key& item) const
{
return this->find(item) != this->end();
}
};
template<typename T> SCP_unordered_set(std::initializer_list<T>) -> SCP_unordered_set<T>;
template <typename T, typename... Args>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(std::size_t n) {
return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]());
}
template <typename T, typename... Args>
typename std::enable_if<std::is_array<T>::value, std::shared_ptr<T>>::type make_shared(std::size_t n) {
return std::shared_ptr<T>(new typename std::remove_extent<T>::type[n]());
}
#endif // _VMALLOCATOR_H_INCLUDED_