Add implementation of LazyInit

This commit is contained in:
Jason Cobb 2020-05-13 10:30:55 -04:00
parent 749ec04708
commit 197f6bf3c8
No known key found for this signature in database
GPG Key ID: 2A3F6A6DCA1E8DED

View File

@ -16,3 +16,87 @@
*/ */
#pragma once #pragma once
#include <libsolutil/Assertions.h>
#include <libsolutil/Exceptions.h>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
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<typename T>
class LazyInit
{
public:
using value_type = T;
static_assert(std::is_object_v<value_type>, "Function, reference, and void types are not supported");
static_assert(!std::is_array_v<value_type>, "Array types are not supported.");
static_assert(!std::is_volatile_v<value_type>, "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<typename F>
value_type& init(F&& _fun)
{
doInit(std::forward<F>(_fun));
return m_value.value();
}
template<typename F>
value_type const& init(F&& _fun) const
{
doInit(std::forward<F>(_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<typename F>
void doInit(F&& _fun) const
{
if (!m_value.has_value())
m_value.emplace(std::forward<F>(_fun)());
}
mutable std::optional<value_type> m_value;
};
}