mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
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.
292 lines
9.9 KiB
C++
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_
|