227 lines
7.2 KiB
C++
227 lines
7.2 KiB
C++
// =====================================================================================================================
|
|
// fennec, a free and open source game engine
|
|
// Copyright © 2025 Medusa Slockbower
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
#ifndef FENNEC_CONTAINERS_MAP_H
|
|
#define FENNEC_CONTAINERS_MAP_H
|
|
|
|
#include <fennec/containers/pair.h>
|
|
#include <fennec/containers/set.h>
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
/* Ramblings
|
|
*
|
|
* Definitions:
|
|
* user = Programmer using this data structure
|
|
*
|
|
* The STL maps are very contrived. Some of its functionality encourages younger programmers to use
|
|
* the exception model. Ideally, I would like this structure to never throw an error with typical use.
|
|
*
|
|
* The array access operator is, in my opinion, poorly implemented. I do not think that this operator should handle
|
|
* insertions and should handle access only. This is the only data structure in STL that has this behavior, no other
|
|
* data structure modifies contents by inherently calling operator[].
|
|
*
|
|
* Currently, I am considering implementing this as the following:
|
|
* Access will be handled only via operator[]. Return value will be a pointer which forces user validation.
|
|
* Insertions will be handled only via an insert/emplace function.
|
|
* Deletions will be handled only via an erase function.
|
|
*/
|
|
|
|
///
|
|
/// \brief Map for Pairing Values to Keys
|
|
/// \tparam KeyT The Key Type
|
|
/// \tparam ValueT The Value Type
|
|
/// \tparam Hash The Hash to Use
|
|
/// \tparam Alloc The Allocator to Use
|
|
template<typename KeyT, typename ValueT, typename Hash = hash<KeyT>, typename Alloc = allocator<pair<KeyT, ValueT>>>
|
|
struct map {
|
|
|
|
// Definitions =========================================================================================================
|
|
public:
|
|
struct key_hash;
|
|
struct node_equals;
|
|
using key_t = KeyT;
|
|
using value_t = ValueT;
|
|
using elem_t = pair<KeyT, ValueT>;
|
|
using alloc_t = typename allocator_traits<Alloc>::template rebind<elem_t>;
|
|
using hash_t = Hash;
|
|
using set_t = set<elem_t, key_hash, node_equals, alloc_t>;
|
|
using iterator = set_t::iterator;
|
|
|
|
// We only want to hash the key
|
|
struct key_hash : hash_t {
|
|
constexpr size_t operator()(const elem_t& p) const {
|
|
return hash_t::operator()(p.first);
|
|
}
|
|
};
|
|
|
|
// We only want to compare the keys
|
|
struct node_equals : equality<KeyT> {
|
|
constexpr bool operator()(const elem_t& a, const elem_t& b) const {
|
|
return equality<KeyT>::operator()(a.first, b.first);
|
|
}
|
|
};
|
|
|
|
|
|
// Constructors ========================================================================================================
|
|
|
|
///
|
|
/// \brief Default Constructor, initializes empty map
|
|
constexpr map() = default;
|
|
|
|
///
|
|
/// \brief Destructor, Destructs all elements and releases the allocation
|
|
constexpr ~map() = default;
|
|
|
|
|
|
// Access ==============================================================================================================
|
|
|
|
///
|
|
/// \brief Key Access Operator
|
|
/// \param key Key value to access
|
|
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
|
constexpr value_t* operator[](const KeyT& key) {
|
|
auto it = _set.at(this->_find(key));
|
|
return it ? &it->second : nullptr;
|
|
}
|
|
|
|
///
|
|
/// \brief Key Const Access Operator
|
|
/// \param key Key value to access
|
|
/// \returns A const-qualified pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
|
constexpr const value_t* operator[](const KeyT& key) const {
|
|
auto it = _set.at(this->_find(key));
|
|
return it ? &it->second : nullptr;
|
|
}
|
|
|
|
///
|
|
/// \brief Argument Key Access Operator
|
|
/// \tparam ArgT Argument Type
|
|
/// \param arg Argument to construct the key with
|
|
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
|
template<typename...ArgsT>
|
|
constexpr value_t* operator[](ArgsT&&...args) {
|
|
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
|
return it ? &it->second : nullptr;
|
|
}
|
|
|
|
///
|
|
/// \brief Argument Key Const Access Operator
|
|
/// \tparam ArgsT Argument Type
|
|
/// \param args Argument to construct the key with
|
|
/// \returns A const-qualified pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
|
template<typename...ArgsT>
|
|
constexpr const value_t* operator[](ArgsT&&...args) const {
|
|
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
|
return it ? &it->second : nullptr;
|
|
}
|
|
|
|
|
|
// Insertion & Deletion ================================================================================================
|
|
|
|
///
|
|
/// \brief Key-Value Insertion
|
|
/// \param pair a pair containing the key and its value
|
|
constexpr void insert(elem_t&& pair) {
|
|
this->_insert(fennec::forward<elem_t>(pair));
|
|
}
|
|
|
|
///
|
|
/// \brief Key-Value Insertion
|
|
/// \param args Arguments for constructing the key-value pair
|
|
template<typename...ArgsT>
|
|
constexpr void emplace(const KeyT& key, ArgsT&&...args) {
|
|
this->_insert(key, fennec::forward<ArgsT>(args)...);
|
|
}
|
|
|
|
///
|
|
/// \brief Key-Value Insertion
|
|
/// \param args Arguments for constructing the key-value pair
|
|
template<typename...ArgsT>
|
|
constexpr void emplace(ArgsT&&...args) {
|
|
this->_insert(fennec::forward<ArgsT>(args)...);
|
|
}
|
|
|
|
///
|
|
/// \brief Erase a key
|
|
/// \param key key to erase
|
|
constexpr void erase(KeyT&& key) {
|
|
_set.erase(this->_find(fennec::forward<KeyT>(key)));
|
|
}
|
|
|
|
///
|
|
/// \brief Erase a key
|
|
/// \param key key to erase
|
|
constexpr void erase(const KeyT& key) {
|
|
_set.erase(this->_find(key));
|
|
}
|
|
|
|
///
|
|
/// \brief Argument Erase
|
|
/// \tparam ArgsT Argument Types
|
|
/// \param args Arguments to construct a key to erase
|
|
template<typename...ArgsT>
|
|
constexpr void erase(ArgsT&&...args) {
|
|
_set.erase(this->_find(fennec::forward<ArgsT>(args)...));
|
|
}
|
|
|
|
|
|
// Iteration ===========================================================================================================
|
|
|
|
constexpr iterator begin() {
|
|
return _set.begin();
|
|
}
|
|
|
|
constexpr iterator end() {
|
|
return _set.end();
|
|
}
|
|
|
|
|
|
private:
|
|
set_t _set;
|
|
|
|
template<typename...ArgsT>
|
|
set_t::iterator _find(ArgsT&&...args) {
|
|
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
|
pair<KeyT, char[sizeof(ValueT)]> root;
|
|
pair<KeyT, ValueT> val;
|
|
|
|
~U() {
|
|
fennec::destruct(&root);
|
|
}
|
|
} trick = { .root = { KeyT(fennec::forward<ArgsT>(args)...), 0 } };
|
|
return _set.find(trick.val);
|
|
}
|
|
|
|
template<typename...ArgsT>
|
|
constexpr void _insert(ArgsT&&...args) {
|
|
elem_t elem(fennec::forward<ArgsT>(args)...);
|
|
auto it = this->_find(elem.first);
|
|
if (it != _set.end()) {
|
|
_set.at(it)->second = fennec::move(elem.second);
|
|
} else {
|
|
_set.insert(fennec::move(elem));
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif // FENNEC_CONTAINERS_MAP_H
|