diff --git a/libsolutil/LazyInit.h b/libsolutil/LazyInit.h index 39e4786df..92d28e0c2 100644 --- a/libsolutil/LazyInit.h +++ b/libsolutil/LazyInit.h @@ -16,3 +16,87 @@ */ #pragma once + +#include +#include + +#include +#include +#include +#include + +namespace solidity::util +{ + +DEV_SIMPLE_EXCEPTION(BadLazyInitAccess); + +/** + * Represents a lazy-initialized value. + * @tparam T the type of the lazy-initialized object; may not be a function, reference, array, or void type; may be const-qualified. + */ +template +class LazyInit +{ +public: + using value_type = T; + + static_assert(std::is_object_v, "Function, reference, and void types are not supported"); + static_assert(!std::is_array_v, "Array types are not supported."); + static_assert(!std::is_volatile_v, "Volatile-qualified types are not supported."); + + LazyInit() = default; + + LazyInit(LazyInit const&) = delete; + LazyInit& operator=(LazyInit const&) = delete; + + // Move constructor must be overridden to ensure that moved-from object is left empty. + constexpr LazyInit(LazyInit&& _other) noexcept: + m_value(std::move(_other.m_value)) + { + _other.m_value.reset(); + } + + // Copy and swap idiom. + LazyInit& operator=(LazyInit&& _other) noexcept + { + this->m_value.swap(_other.m_value); + _other.m_value.reset(); + } + + template + value_type& init(F&& _fun) + { + doInit(std::forward(_fun)); + return m_value.value(); + } + + template + value_type const& init(F&& _fun) const + { + doInit(std::forward(_fun)); + return m_value.value(); + } + +private: + constexpr void assertInitialized() const + { + assertThrow( + m_value.has_value(), + BadLazyInitAccess, + "Attempt to access an uninitialized LazyInit" + ); + } + + /// Although not quite logically const, this is marked const for pragmatic reasons. It doesn't change the platonic + /// value of the object (which is something that is initialized to some computed value on first use). + template + void doInit(F&& _fun) const + { + if (!m_value.has_value()) + m_value.emplace(std::forward(_fun)()); + } + + mutable std::optional m_value; +}; + +}