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