diff --git a/source/mijin/util/property.hpp b/source/mijin/util/property.hpp new file mode 100644 index 0000000..c292d64 --- /dev/null +++ b/source/mijin/util/property.hpp @@ -0,0 +1,107 @@ + +#pragma once + +#ifndef MIJIN_UTIL_PROPERTY_HPP_INCLUDED +#define MIJIN_UTIL_PROPERTY_HPP_INCLUDED 1 + +#include +#include +#include "../debug/assert.hpp" + +namespace mijin +{ + +// +// public defines +// + +// +// public types +// + +template +class PropertyStorage +{ +public: + using value_t = T; + using get_t = TGet; + using set_t = TSet; + + virtual ~PropertyStorage() noexcept = default; + + [[nodiscard]] virtual TGet getValue() = 0; + virtual void setValue(TSet value) = 0; +}; + +template +class SimplePropertyStorage : public PropertyStorage +{ +private: + T value_; +public: + SimplePropertyStorage() noexcept = default; + SimplePropertyStorage(const SimplePropertyStorage& other) noexcept(noexcept(T(other.value_))) = default; + SimplePropertyStorage(SimplePropertyStorage&& other) noexcept(noexcept(T(std::move(other.value_)))) = default; + explicit SimplePropertyStorage(T value) noexcept(noexcept(T(std::move(value)))) : value_(std::move(value)) {} + + SimplePropertyStorage& operator=(const SimplePropertyStorage& other) noexcept(noexcept(value_ = other.value_)) = default; + SimplePropertyStorage& operator=(SimplePropertyStorage&& other) noexcept(noexcept(value_ = std::move(other.value_))) = default; + + const T& getValue() noexcept override { return value_; } + void setValue(T value) override { value_ = std::move(value); } +}; + +template +class Property +{ +private: + using storage_t = PropertyStorage; + using storage_ptr_t = std::unique_ptr; + + storage_ptr_t storage_; +public: + class PropertyProxy + { + private: + Property* base_; + public: + explicit PropertyProxy(Property* base) noexcept : base_(base) {} + + operator TGet() noexcept { return base_->get(); } + PropertyProxy& operator=(TSet value) noexcept(noexcept(std::declval() = std::move(value))) + { + base_->set(std::move(value)); + return *this; + } + }; + + Property() noexcept = default; + explicit Property(storage_ptr_t storage) noexcept : storage_(std::move(storage)) {} + Property(Property&&) noexcept = default; + + Property& operator=(Property&&) noexcept = default; + PropertyProxy operator*() noexcept { return PropertyProxy(this); } + + [[nodiscard]] TGet get() noexcept + { + MIJIN_ASSERT_FATAL(storage_ != nullptr, "Cannot get value from an unset property."); + return storage_->getValue(); + } + void set(TSet value) noexcept(noexcept(std::declval() = std::move(value))) + { + MIJIN_ASSERT_FATAL(storage_ != nullptr, "Cannot set value of an unset property."); + storage_->setValue(std::move(value)); + } +}; + +template +Property(std::unique_ptr) -> Property; + +template +Property makeSimpleProperty(T value) noexcept(noexcept(std::declval() = std::move(value))) +{ + return Property(std::make_unique>(std::move(value))); +} +} + +#endif // MIJIN_UTIL_PROPERTY_HPP_INCLUDED