intial commit
This commit is contained in:
237
source/mijin/container/boxed_object.hpp
Normal file
237
source/mijin/container/boxed_object.hpp
Normal file
@@ -0,0 +1,237 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_CONTAINER_BOXED_OBJECT_HPP_INCLUDED)
|
||||
#define MIJIN_CONTAINER_BOXED_OBJECT_HPP_INCLUDED 1
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include "../debug/assert.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public defines
|
||||
//
|
||||
|
||||
#if !defined(MIJIN_BOXED_OBJECT_DEBUG)
|
||||
#define MIJIN_BOXED_OBJECT_DEBUG MIJIN_DEBUG
|
||||
#endif
|
||||
|
||||
//
|
||||
// public constants
|
||||
//
|
||||
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
template<typename T>
|
||||
class BoxedObject
|
||||
{
|
||||
private:
|
||||
union {
|
||||
T object_;
|
||||
};
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
bool constructed = false;
|
||||
#endif
|
||||
public:
|
||||
BoxedObject() = default;
|
||||
explicit BoxedObject(T object)
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
: constructed(true)
|
||||
#endif
|
||||
{
|
||||
std::construct_at(&object_, std::move(object));
|
||||
}
|
||||
BoxedObject(const BoxedObject&) = delete;
|
||||
BoxedObject(BoxedObject&&) = delete;
|
||||
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
~BoxedObject()
|
||||
{
|
||||
MIJIN_ASSERT(!constructed, "BoxedObject::~BoxedObject(): Object has not been destroy prior to destructor!")
|
||||
}
|
||||
#endif
|
||||
|
||||
BoxedObject& operator=(const BoxedObject&) = delete;
|
||||
BoxedObject& operator=(BoxedObject&&) = delete;
|
||||
|
||||
template<typename... TArgs>
|
||||
void construct(TArgs&&... args)
|
||||
{
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
MIJIN_ASSERT(!constructed, "BoxedObject::construct(): Attempt to construct an already constructed object!")
|
||||
constructed = true;
|
||||
#endif
|
||||
std::construct_at(&object_, std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
MIJIN_ASSERT(constructed, "BoxedObject::destroy(): Attempt to destroy a not constructed object!")
|
||||
constructed = false;
|
||||
#endif
|
||||
std::destroy_at(&object_);
|
||||
}
|
||||
|
||||
void copyTo(BoxedObject& other) const
|
||||
{
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!")
|
||||
#endif
|
||||
other.construct(object_);
|
||||
}
|
||||
|
||||
void moveTo(BoxedObject& other)
|
||||
{
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!")
|
||||
#endif
|
||||
other.construct(std::move(object_));
|
||||
destroy();
|
||||
}
|
||||
|
||||
[[nodiscard]] T& get()
|
||||
{
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!")
|
||||
#endif
|
||||
return object_;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T& get() const
|
||||
{
|
||||
#if MIJIN_BOXED_OBJECT_DEBUG
|
||||
MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!")
|
||||
#endif
|
||||
return object_;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class BoxedObjectIterator
|
||||
{
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
private:
|
||||
BoxedObject<T>* box_ = nullptr;
|
||||
#if MIJIN_CHECKED_ITERATORS
|
||||
BoxedObject<T>* start_ = nullptr;
|
||||
BoxedObject<T>* end_ = nullptr;
|
||||
#endif
|
||||
public:
|
||||
BoxedObjectIterator() = default;
|
||||
explicit constexpr BoxedObjectIterator(BoxedObject<T>* box, BoxedObject<T>* start, BoxedObject<T>* end)
|
||||
: box_(box)
|
||||
#if MIJIN_CHECKED_ITERATORS
|
||||
, start_(start), end_(end)
|
||||
#endif
|
||||
{}
|
||||
BoxedObjectIterator(const BoxedObjectIterator&) = default;
|
||||
BoxedObjectIterator(BoxedObjectIterator&&) noexcept = default;
|
||||
|
||||
BoxedObjectIterator& operator=(const BoxedObjectIterator&) = default;
|
||||
BoxedObjectIterator& operator=(BoxedObjectIterator&&) noexcept = default;
|
||||
BoxedObjectIterator& operator+=(difference_type diff);
|
||||
BoxedObjectIterator& operator-=(difference_type diff) { return (*this += -diff); }
|
||||
|
||||
constexpr auto operator<=>(const BoxedObjectIterator& other) const noexcept = default;
|
||||
|
||||
[[nodiscard]] T& operator*() const;
|
||||
[[nodiscard]] T* operator->() const;
|
||||
BoxedObjectIterator& operator++();
|
||||
BoxedObjectIterator operator++(int);
|
||||
BoxedObjectIterator& operator--();
|
||||
BoxedObjectIterator operator--(int);
|
||||
|
||||
[[nodiscard]] difference_type operator-(const BoxedObjectIterator& other) const { return box_ - other.box_; }
|
||||
[[nodiscard]] BoxedObjectIterator operator+(difference_type diff) const;
|
||||
[[nodiscard]] BoxedObjectIterator operator-(difference_type diff) const { return (*this + -diff); }
|
||||
|
||||
[[nodiscard]] T& operator[](difference_type diff) const { return *(*this + diff); }
|
||||
};
|
||||
template<typename T>
|
||||
inline BoxedObjectIterator<T> operator+(std::iter_difference_t<T> diff, const BoxedObjectIterator<T>& iter) {
|
||||
return iter + diff;
|
||||
}
|
||||
static_assert(std::random_access_iterator<BoxedObjectIterator<int>>);
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
template<typename T>
|
||||
BoxedObjectIterator<T>& BoxedObjectIterator<T>::operator+=(difference_type diff)
|
||||
{
|
||||
#if MIJIN_CHECKED_ITERATORS
|
||||
MIJIN_ASSERT(diff <= (end_ - box_) && diff >= (box_ - start_), "BoxedObjectIterator::operator+=(): Attempt to iterate out of range.");
|
||||
#endif
|
||||
box_ += diff;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& BoxedObjectIterator<T>::operator*() const
|
||||
{
|
||||
#if MIJIN_CHECKED_ITERATORS
|
||||
MIJIN_ASSERT(box_ >= start_ && box < end_, "BoxedObjectIterator::operator->(): Attempt to dereference out-of-range iterator.");
|
||||
#endif
|
||||
return box_->get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
BoxedObjectIterator<T>& BoxedObjectIterator<T>::operator++()
|
||||
{
|
||||
#if MIJIN_CHECKED_ITERATORS
|
||||
MIJIN_ASSERT(box_ < end_, "BoxedObjectIterator::operator++(): Attempt to iterator past the end.");
|
||||
#endif
|
||||
++box_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
BoxedObjectIterator<T> BoxedObjectIterator<T>::operator++(int)
|
||||
{
|
||||
#if MIJIN_CHECKED_ITERATORS
|
||||
MIJIN_ASSERT(box_ < end_, "BoxedObjectIterator::operator++(int): Attempt to iterator past the end.");
|
||||
#endif
|
||||
BoxedObjectIterator copy(*this);
|
||||
++box_;
|
||||
return copy;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
BoxedObjectIterator<T>& BoxedObjectIterator<T>::operator--()
|
||||
{
|
||||
#if MIJIN_CHECKED_ITERATORS
|
||||
MIJIN_ASSERT(box_ > start_, "BoxedObjectIterator::operator--(): Attempt to iterator past start.");
|
||||
#endif
|
||||
--box_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
BoxedObjectIterator<T> BoxedObjectIterator<T>::operator--(int)
|
||||
{
|
||||
#if MIJIN_CHECKED_ITERATORS
|
||||
MIJIN_ASSERT(box_ > start_, "BoxedObjectIterator::operator--(int): Attempt to iterator past start.");
|
||||
#endif
|
||||
BoxedObjectIterator copy(*this);
|
||||
--box_;
|
||||
return copy;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
BoxedObjectIterator<T> BoxedObjectIterator<T>::operator+(difference_type diff) const
|
||||
{
|
||||
BoxedObjectIterator copy(*this);
|
||||
copy += diff;
|
||||
return copy;
|
||||
}
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_CONTAINER_BOXED_OBJECT_HPP_INCLUDED)
|
||||
145
source/mijin/container/cached_array.hpp
Normal file
145
source/mijin/container/cached_array.hpp
Normal file
@@ -0,0 +1,145 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_CONTAINER_CACHED_ARRAY_HPP_INCLUDED)
|
||||
#define MIJIN_CONTAINER_CACHED_ARRAY_HPP_INCLUDED 1
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include "../debug/assert.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public defines
|
||||
//
|
||||
|
||||
//
|
||||
// public constants
|
||||
//
|
||||
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
template<typename T, std::size_t cacheSize = 32>
|
||||
class CachedArray
|
||||
{
|
||||
public:
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv_t<T>;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
|
||||
static constexpr std::size_t INVALID_INDEX = std::numeric_limits<size_type>::max();
|
||||
private:
|
||||
struct CacheEntry
|
||||
{
|
||||
size_type index = INVALID_INDEX;
|
||||
std::size_t useIndex = 0;
|
||||
std::optional<T> value;
|
||||
};
|
||||
mutable std::array<CacheEntry, cacheSize> cache_;
|
||||
std::vector<std::optional<T>> backbuffer_;
|
||||
std::size_t nextUseIndex_ = 1;
|
||||
public:
|
||||
[[nodiscard]] reference at(size_type index);
|
||||
[[nodiscard]] const_reference at(size_type index) const;
|
||||
[[nodiscard]] size_type size() const { return backbuffer_.size(); }
|
||||
void resize(size_type newSize);
|
||||
void reserve(size_type elements);
|
||||
|
||||
// TODO: make iterable (not that easy if we move from the backbuffer!)
|
||||
// [[nodiscard]] iterator begin() { return &backbuffer_[0]; }
|
||||
// [[nodiscard]] const_iterator begin() const { return &backbuffer_[0]; }
|
||||
// [[nodiscard]] const_iterator cbegin() const { return &backbuffer_[0]; }
|
||||
// [[nodiscard]] iterator end() { return begin() + size(); }
|
||||
// [[nodiscard]] const_iterator end() const { return begin() + size(); }
|
||||
// [[nodiscard]] const_iterator cend() const { return begin() + size(); }
|
||||
|
||||
[[nodiscard]] reference operator[](size_type index) { return at(index); }
|
||||
[[nodiscard]] const_reference operator[](size_type index) const { return at(index); }
|
||||
private:
|
||||
// TODO: use deducing this once it is available
|
||||
template<typename TSelf>
|
||||
static auto atImpl(TSelf&& self, std::size_t index);
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
template<typename T, std::size_t cacheSize>
|
||||
auto CachedArray<T, cacheSize>::at(std::size_t index) -> reference
|
||||
{
|
||||
return atImpl(*this, index);
|
||||
}
|
||||
|
||||
template<typename T, std::size_t cacheSize>
|
||||
auto CachedArray<T, cacheSize>::at(std::size_t index) const -> const_reference
|
||||
{
|
||||
return atImpl(*this, index);
|
||||
}
|
||||
|
||||
template<typename T, std::size_t cacheSize>
|
||||
void CachedArray<T, cacheSize>::resize(size_type newSize)
|
||||
{
|
||||
if (newSize < size())
|
||||
{
|
||||
// invalidate any cache entries that shouldn't exist anymore
|
||||
for (CacheEntry& entry : cache_)
|
||||
{
|
||||
if (entry.index < newSize)
|
||||
{
|
||||
entry.value = std::nullopt;
|
||||
entry.useIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
backbuffer_.resize(newSize);
|
||||
}
|
||||
|
||||
template<typename T, std::size_t cacheSize>
|
||||
template<typename TSelf>
|
||||
auto CachedArray<T, cacheSize>::atImpl(TSelf&& self, std::size_t index)
|
||||
{
|
||||
MIJIN_ASSERT(index < size(), "CachedArray::at(): Attempting to access index out of range.");
|
||||
// first try the cache
|
||||
for (CacheEntry& entry : self.cache_)
|
||||
{
|
||||
if (entry.index == index)
|
||||
{
|
||||
entry.useIndex = self.nextUseIndex_++;
|
||||
return *entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise copy from backbuffer to cache
|
||||
auto itCache = std::min_element(self.cache_.begin(), self.cache_.end(), [](const CacheEntry& left, const CacheEntry& right) {
|
||||
return left.useIndex < right.useIndex;
|
||||
});
|
||||
if (itCache->index != INVALID_INDEX)
|
||||
{
|
||||
// move back to the backbuffer
|
||||
self.backbuffer_[itCache->index] = std::move(itCache->value);
|
||||
}
|
||||
itCache->index = index;
|
||||
itCache->value = std::move(self.backbuffer_.at(index));
|
||||
itCache->useIndex = self.nextUseIndex_++;
|
||||
return *itCache->value;
|
||||
}
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_CONTAINER_CACHED_ARRAY_HPP_INCLUDED)
|
||||
133
source/mijin/container/inline_array.hpp
Normal file
133
source/mijin/container/inline_array.hpp
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_CONTAINER_INLINE_ARRAY_HPP_INCLUDED)
|
||||
#define MIJIN_CONTAINER_INLINE_ARRAY_HPP_INCLUDED 1
|
||||
|
||||
#include "./boxed_object.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public defines
|
||||
//
|
||||
|
||||
//
|
||||
// public constants
|
||||
//
|
||||
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
template<typename T, std::size_t maxSize>
|
||||
class InlineArray
|
||||
{
|
||||
public:
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv_t<T>;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = BoxedObjectIterator<T>;
|
||||
using const_iterator = BoxedObjectIterator<const T>;
|
||||
private:
|
||||
BoxedObject<T> elements_[maxSize]; // NOLINT(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
|
||||
size_type size_ = 0;
|
||||
public:
|
||||
InlineArray() = default;
|
||||
InlineArray(const InlineArray& other);
|
||||
InlineArray(InlineArray&& other) noexcept;
|
||||
~InlineArray();
|
||||
|
||||
reference operator[](size_type index);
|
||||
const_reference operator[](size_type index) const;
|
||||
|
||||
[[nodiscard]] iterator begin() { return BoxedObjectIterator<T>(&elements_[0], &elements_[0], &elements_[size_]); }
|
||||
[[nodiscard]] const_iterator begin() const { return BoxedObjectIterator<const T>(&elements_[0], &elements_[0], &elements_[size_]); }
|
||||
[[nodiscard]] const_iterator cbegin() const { return BoxedObjectIterator<const T>(&elements_[0], &elements_[0], &elements_[size_]); }
|
||||
[[nodiscard]] iterator end() { return BoxedObjectIterator<T>(&elements_[size_], &elements_[0], &elements_[size_]); }
|
||||
[[nodiscard]] const_iterator end() const { return BoxedObjectIterator<const T>(&elements_[size_], &elements_[0], &elements_[size_]); }
|
||||
[[nodiscard]] const_iterator cend() const { return BoxedObjectIterator<const T>(&elements_[size_], &elements_[0], &elements_[size_]); }
|
||||
|
||||
[[nodiscard]] constexpr size_type size() const { return size_; }
|
||||
[[nodiscard]] constexpr size_type capacity() const { return maxSize; }
|
||||
[[nodiscard]] constexpr bool empty() const { return size_ == 0; }
|
||||
[[nodiscard]] constexpr reference at(size_type index) {
|
||||
MIJIN_ASSERT(index < size_, "InlineArray::at() attempt to access out-of-bounds index.");
|
||||
return elements_[index].get();
|
||||
}
|
||||
[[nodiscard]] constexpr const_reference at(size_type index) const {
|
||||
MIJIN_ASSERT(index < size_, "InlineArray::at() attempt to access out-of-bounds index.");
|
||||
return elements_[index].get();
|
||||
}
|
||||
|
||||
void resize(size_type newSize);
|
||||
void clear() { resize(0); }
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
template<typename T, std::size_t numElements>
|
||||
InlineArray<T, numElements>::InlineArray(const InlineArray& other) : size_(other.size_)
|
||||
{
|
||||
for (size_type idx = 0; idx < size_; ++idx) {
|
||||
other.elements_[idx].copyTo(elements_[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, std::size_t numElements>
|
||||
InlineArray<T, numElements>::InlineArray(InlineArray&& other) noexcept : size_(other.size_)
|
||||
{
|
||||
for (size_type idx = 0; idx < size_; ++idx) {
|
||||
other.elements_[idx].moveTo(elements_[idx]);
|
||||
}
|
||||
other.size_ = 0;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t numElements>
|
||||
InlineArray<T, numElements>::~InlineArray()
|
||||
{
|
||||
for (std::size_t idx = 0; idx < size_; ++idx) {
|
||||
elements_[idx].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, std::size_t numElements>
|
||||
auto InlineArray<T, numElements>::operator[](size_type index) -> reference
|
||||
{
|
||||
return at(index);
|
||||
}
|
||||
|
||||
template<typename T, std::size_t numElements>
|
||||
auto InlineArray<T, numElements>::operator[](size_type index) const -> const_reference
|
||||
{
|
||||
return at(index);
|
||||
}
|
||||
|
||||
template<typename T, std::size_t numElements>
|
||||
void InlineArray<T, numElements>::resize(size_type newSize)
|
||||
{
|
||||
MIJIN_ASSERT(newSize <= numElements, "InlineArray::resize(): Attempt to resize beyond maximum size.");
|
||||
if (newSize < size_) {
|
||||
for (size_type idx = newSize; idx < size_; ++idx) {
|
||||
elements_[idx].destroy();
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (size_type idx = size_; idx < newSize; ++idx) {
|
||||
elements_[idx].construct();
|
||||
}
|
||||
}
|
||||
size_ = newSize;
|
||||
}
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_CONTAINER_INLINE_ARRAY_HPP_INCLUDED)
|
||||
90
source/mijin/container/map_view.hpp
Normal file
90
source/mijin/container/map_view.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_CONTAINER_MAP_VIEW_HPP_INCLUDED)
|
||||
#define MIJIN_CONTAINER_MAP_VIEW_HPP_INCLUDED 1
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public defines
|
||||
//
|
||||
|
||||
//
|
||||
// public constants
|
||||
//
|
||||
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
template<typename TKey, typename TValue, typename TMap = std::unordered_map<TKey, TValue>>
|
||||
struct MapView
|
||||
{
|
||||
private:
|
||||
TMap& map;
|
||||
public:
|
||||
MapView() = delete;
|
||||
MapView(const MapView&) = default;
|
||||
MapView(MapView&&) noexcept = default;
|
||||
inline MapView(TMap& map_) : map(map_) {}
|
||||
|
||||
MapView& operator=(const MapView&) = default;
|
||||
MapView& operator=(MapView&&) noexcept = default;
|
||||
|
||||
[[nodiscard]] TValue& operator[](const TKey& key)
|
||||
{
|
||||
return at(key);
|
||||
}
|
||||
[[nodiscard]] const TValue& operator[](const TKey& key) const
|
||||
{
|
||||
return at(key);
|
||||
}
|
||||
|
||||
[[nodiscard]] TValue& at(const TKey& key)
|
||||
{
|
||||
return map.at(key);
|
||||
}
|
||||
|
||||
[[nodiscard]] const TValue& at(const TKey& key) const
|
||||
{
|
||||
return map.at(key);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto begin() const
|
||||
{
|
||||
return map.begin();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto begin()
|
||||
{
|
||||
return map.begin();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto end()
|
||||
{
|
||||
return map.end();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto end() const
|
||||
{
|
||||
return map.end();
|
||||
}
|
||||
};
|
||||
|
||||
// template<typename TKey, typename TValue, template<typename, typename, typename...> typename TMap>
|
||||
// MapView(TMap<TKey, TValue>& map) -> MapView<TKey, TValue, TMap<TKey, TValue>>;
|
||||
|
||||
template<typename TMap>
|
||||
MapView(TMap& map) -> MapView<typename TMap::key_type, typename TMap::mapped_type, TMap>;
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_CONTAINER_MAP_VIEW_HPP_INCLUDED)
|
||||
298
source/mijin/container/optional.hpp
Normal file
298
source/mijin/container/optional.hpp
Normal file
@@ -0,0 +1,298 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED)
|
||||
#define MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED 1
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include "../debug/assert.hpp"
|
||||
#include "../util/concepts.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public defines
|
||||
//
|
||||
|
||||
//
|
||||
// public constants
|
||||
//
|
||||
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template<typename T>
|
||||
struct OptionalStorage
|
||||
{
|
||||
alignas(T) std::uint8_t data[sizeof(T)]; // NOLINT(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
|
||||
std::uint8_t used = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool empty() const noexcept { return !used; }
|
||||
|
||||
template<typename... TArgs>
|
||||
constexpr void emplace(TArgs&&... args) noexcept
|
||||
{
|
||||
MIJIN_ASSERT(!used, "Attempting to emplace in an already set OptionalStorage!");
|
||||
used = 1;
|
||||
::new (data) T(std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
MIJIN_ASSERT(used, "Attempting to clear an empty OptionalStorage!");
|
||||
get().~T();
|
||||
used = 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& get() noexcept { return *reinterpret_cast<T*>(data); }
|
||||
[[nodiscard]] constexpr const T& get() const noexcept { return *reinterpret_cast<const T*>(data); }
|
||||
};
|
||||
|
||||
template<pointer_type T>
|
||||
struct OptionalStorage<T>
|
||||
{
|
||||
static constexpr T invalidPointer() noexcept { return reinterpret_cast<T>(1); }
|
||||
|
||||
T ptr = invalidPointer();
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool empty() const noexcept { return ptr == invalidPointer(); }
|
||||
|
||||
void emplace(T value) noexcept
|
||||
{
|
||||
ptr = value;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
ptr = invalidPointer();
|
||||
}
|
||||
|
||||
[[nodiscard]] T& get() noexcept { return ptr; }
|
||||
[[nodiscard]] const T& get() const noexcept { return ptr; }
|
||||
};
|
||||
|
||||
template<reference_type T>
|
||||
struct OptionalStorage<T>
|
||||
{
|
||||
using pointer_t = std::remove_reference_t<T>*;
|
||||
|
||||
pointer_t ptr = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr bool empty() const noexcept { return ptr == nullptr; }
|
||||
|
||||
void emplace(T value) noexcept
|
||||
{
|
||||
ptr = &value;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
ptr = nullptr;
|
||||
}
|
||||
|
||||
T get() noexcept { return *ptr; }
|
||||
const T get() const noexcept { return *ptr; } // NOLINT(readability-const-return-type) T already is a reference
|
||||
};
|
||||
}
|
||||
struct NullOptional {};
|
||||
static constexpr NullOptional NULL_OPTIONAL;
|
||||
|
||||
template<typename TValue>
|
||||
class Optional
|
||||
{
|
||||
private:
|
||||
impl::OptionalStorage<TValue> storage_;
|
||||
public:
|
||||
constexpr Optional() = default;
|
||||
constexpr Optional(NullOptional) noexcept {}
|
||||
inline Optional(const Optional& other) noexcept;
|
||||
inline Optional(Optional&& other) noexcept;
|
||||
inline Optional(TValue value) noexcept;
|
||||
inline ~Optional() noexcept;
|
||||
public:
|
||||
Optional& operator =(const Optional& other) noexcept;
|
||||
Optional& operator =(Optional&& other) noexcept;
|
||||
Optional& operator =(TValue value) noexcept;
|
||||
Optional& operator =(NullOptional) noexcept;
|
||||
public:
|
||||
[[nodiscard]]
|
||||
constexpr bool operator ==(NullOptional) const noexcept { return empty(); }
|
||||
template<typename TOther>
|
||||
[[nodiscard]]
|
||||
constexpr bool operator ==(const Optional<TOther>& other) const noexcept;
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr bool operator ==(const T& value) const noexcept;
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr bool operator !=(const T& value) const noexcept { return !(*this == value); }
|
||||
[[nodiscard]]
|
||||
constexpr explicit operator bool() const noexcept { return !empty(); }
|
||||
[[nodiscard]]
|
||||
constexpr bool operator !() const noexcept { return empty(); }
|
||||
public:
|
||||
template<typename... Types>
|
||||
void emplace(Types&&... params) noexcept;
|
||||
public:
|
||||
[[nodiscard]] inline std::remove_reference_t<TValue>& get() noexcept;
|
||||
[[nodiscard]] inline const std::remove_reference_t<TValue>& get() const noexcept;
|
||||
[[nodiscard]] constexpr bool empty() const noexcept { return storage_.empty(); }
|
||||
inline void reset() noexcept;
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
template<typename TValue>
|
||||
Optional<TValue>::Optional(const Optional& other) noexcept
|
||||
{
|
||||
if (other) {
|
||||
emplace(other.get());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
Optional<TValue>::Optional(Optional&& other) noexcept
|
||||
{
|
||||
if (other)
|
||||
{
|
||||
emplace(std::move(other.get()));
|
||||
other.reset();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
Optional<TValue>::Optional(TValue value) noexcept
|
||||
{
|
||||
if constexpr (std::is_reference_v<TValue>) {
|
||||
emplace(value);
|
||||
}
|
||||
else {
|
||||
emplace(std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
Optional<TValue>::~Optional() noexcept
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
auto Optional<TValue>::operator =(const Optional& other) noexcept -> Optional&
|
||||
{
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
reset();
|
||||
if (other) {
|
||||
emplace(other.get());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
auto Optional<TValue>::operator =(Optional&& other) noexcept -> Optional&
|
||||
{
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
reset();
|
||||
if (other)
|
||||
{
|
||||
emplace(std::move(other.get()));
|
||||
other.reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
auto Optional<TValue>::operator =(TValue value) noexcept -> Optional&
|
||||
{
|
||||
if constexpr (std::is_reference_v<TValue>) {
|
||||
emplace(value);
|
||||
}
|
||||
else {
|
||||
emplace(std::move(value));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
auto Optional<TValue>::operator =(NullOptional) noexcept -> Optional&
|
||||
{
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
template<typename TOther>
|
||||
constexpr bool Optional<TValue>::operator ==(const Optional<TOther>& other) const noexcept
|
||||
{
|
||||
if (empty())
|
||||
{
|
||||
return other.empty();
|
||||
}
|
||||
if (!other.empty())
|
||||
{
|
||||
return get() == other.get();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
template<typename T>
|
||||
constexpr bool Optional<TValue>::operator ==(const T& value) const noexcept
|
||||
{
|
||||
if (empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return get() == value;
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
template<typename... Types>
|
||||
void Optional<TValue>::emplace(Types&&... params) noexcept
|
||||
{
|
||||
reset();
|
||||
storage_.emplace(std::forward<Types>(params)...);
|
||||
MIJIN_ASSERT(!empty(), "Something is wrong.");
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
inline std::remove_reference_t<TValue>& Optional<TValue>::get() noexcept
|
||||
{
|
||||
MIJIN_ASSERT(!empty(), "Attempting to fetch value from empty Optional!");
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
inline const std::remove_reference_t<TValue>& Optional<TValue>::get() const noexcept
|
||||
{
|
||||
MIJIN_ASSERT(!empty(), "Attempting to fetch value from empty Optional!");
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
inline void Optional<TValue>::reset() noexcept
|
||||
{
|
||||
if (empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
storage_.clear();
|
||||
}
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED)
|
||||
104
source/mijin/container/typeless_buffer.hpp
Normal file
104
source/mijin/container/typeless_buffer.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED)
|
||||
#define MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED 1
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include "../debug/assert.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
|
||||
//
|
||||
// public defines
|
||||
//
|
||||
|
||||
//
|
||||
// public constants
|
||||
//
|
||||
|
||||
//
|
||||
// public types
|
||||
//
|
||||
|
||||
template<typename T>
|
||||
class BufferView;
|
||||
|
||||
class TypelessBuffer
|
||||
{
|
||||
public:
|
||||
using size_type = std::size_t;
|
||||
private:
|
||||
std::vector<std::byte> bytes_;
|
||||
public:
|
||||
[[nodiscard]] void* data() { return bytes_.data(); }
|
||||
[[nodiscard]] const void* data() const { return bytes_.data(); }
|
||||
[[nodiscard]] size_type byteSize() const { return bytes_.size(); }
|
||||
void resize(size_type numBytes) { bytes_.resize(numBytes); }
|
||||
void reserve(size_type numBytes) { bytes_.reserve(numBytes); }
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] BufferView<T> makeBufferView() { return BufferView<T>(this); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class BufferView
|
||||
{
|
||||
public:
|
||||
using element_type = T;
|
||||
using value_type = std::remove_cv_t<T>;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = T*;
|
||||
using const_iterator = const T*;
|
||||
private:
|
||||
class TypelessBuffer* buffer_ = nullptr;
|
||||
public:
|
||||
BufferView() = default;
|
||||
explicit BufferView(class TypelessBuffer* buffer) : buffer_(buffer) {}
|
||||
BufferView(const BufferView&) = default;
|
||||
|
||||
BufferView& operator=(const BufferView&) = default;
|
||||
|
||||
[[nodiscard]] inline size_type size() const { return buffer_ ? buffer_->byteSize() / sizeof(T) : 0; }
|
||||
inline void resize(size_type numElements) {
|
||||
MIJIN_ASSERT(buffer_, "BufferView::resize(): cannot resize a view without a buffer.");
|
||||
buffer_->resize(numElements * sizeof(T));
|
||||
}
|
||||
inline void reserve(size_type numElements) {
|
||||
MIJIN_ASSERT(buffer_, "BufferView::resize(): cannot resize a view without a buffer.");
|
||||
buffer_->reserve(numElements * sizeof(T));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline iterator begin() { return buffer_ ? static_cast<T*>(buffer_->data()) : nullptr; }
|
||||
[[nodiscard]] inline const_iterator begin() const { return buffer_ ? static_cast<T*>(buffer_->data()) : nullptr; }
|
||||
[[nodiscard]] inline const_iterator cbegin() const { return begin(); }
|
||||
[[nodiscard]] inline iterator end() { return buffer_ ? static_cast<T*>(buffer_->data()) + size() : nullptr; }
|
||||
[[nodiscard]] inline const_iterator end() const { return buffer_ ? static_cast<T*>(buffer_->data()) + size() : nullptr; }
|
||||
[[nodiscard]] inline const_iterator cend() const { return end(); }
|
||||
[[nodiscard]] inline reference at(size_type pos) {
|
||||
MIJIN_ASSERT(pos < size(), "BufferView::at(): pos is out of range.");
|
||||
return *(begin() + pos);
|
||||
}
|
||||
[[nodiscard]] inline const_reference at(size_type pos) const {
|
||||
MIJIN_ASSERT(pos < size(), "BufferView::at(): pos is out of range.");
|
||||
return *(begin() + pos);
|
||||
}
|
||||
// operators
|
||||
inline reference operator[](size_type pos) { return at(pos); }
|
||||
inline const_reference operator[](size_type pos) const { return at(pos); }
|
||||
};
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED)
|
||||
Reference in New Issue
Block a user