1 /*
2 	This file is part of solidity.
3 
4 	solidity is free software: you can redistribute it and/or modify
5 	it under the terms of the GNU General Public License as published by
6 	the Free Software Foundation, either version 3 of the License, or
7 	(at your option) any later version.
8 
9 	solidity is distributed in the hope that it will be useful,
10 	but WITHOUT ANY WARRANTY; without even the implied warranty of
11 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 	GNU General Public License for more details.
13 
14 	You should have received a copy of the GNU General Public License
15 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 // SPDX-License-Identifier: GPL-3.0
18 
19 #pragma once
20 
21 #include <libsolutil/Assertions.h>
22 #include <libsolutil/Exceptions.h>
23 
24 #include <memory>
25 #include <optional>
26 #include <type_traits>
27 #include <utility>
28 
29 namespace solidity::util
30 {
31 
32 DEV_SIMPLE_EXCEPTION(BadLazyInitAccess);
33 
34 /**
35  * A value that is initialized at some point after construction of the LazyInit. The stored value can only be accessed
36  * while calling "init", which initializes the stored value (if it has not already been initialized).
37  *
38  * @tparam T the type of the stored value; may not be a function, reference, array, or void type; may be const-qualified.
39  */
40 template<typename T>
41 class LazyInit
42 {
43 public:
44 	using value_type = T;
45 
46 	static_assert(std::is_object_v<value_type>, "Function, reference, and void types are not supported");
47 	static_assert(!std::is_array_v<value_type>, "Array types are not supported.");
48 	static_assert(!std::is_volatile_v<value_type>, "Volatile-qualified types are not supported.");
49 
50 	LazyInit() = default;
51 
52 	LazyInit(LazyInit const&) = delete;
53 	LazyInit& operator=(LazyInit const&) = delete;
54 
55 	// Move constructor must be overridden to ensure that moved-from object is left empty.
LazyInit(LazyInit && _other)56 	constexpr LazyInit(LazyInit&& _other) noexcept:
57 		m_value(std::move(_other.m_value))
58 	{
59 		_other.m_value.reset();
60 	}
61 
62 	LazyInit& operator=(LazyInit&& _other) noexcept
63 	{
64 		this->m_value.swap(_other.m_value);
65 		_other.m_value.reset();
66 	}
67 
68 	template<typename F>
init(F && _fun)69 	value_type& init(F&& _fun)
70 	{
71 		doInit(std::forward<F>(_fun));
72 		return m_value.value();
73 	}
74 
75 	template<typename F>
init(F && _fun)76 	value_type const& init(F&& _fun) const
77 	{
78 		doInit(std::forward<F>(_fun));
79 		return m_value.value();
80 	}
81 
82 private:
83 	/// Although not quite logically const, this is marked const for pragmatic reasons. It doesn't change the platonic
84 	/// value of the object (which is something that is initialized to some computed value on first use).
85 	template<typename F>
doInit(F && _fun)86 	void doInit(F&& _fun) const
87 	{
88 		if (!m_value.has_value())
89 			m_value.emplace(std::forward<F>(_fun)());
90 	}
91 
92 	mutable std::optional<value_type> m_value;
93 };
94 
95 }
96