1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <type_traits>
20 #include <utility>
21 
22 #include <folly/Optional.h>
23 #include <folly/functional/Invoke.h>
24 
25 namespace folly {
26 
27 //////////////////////////////////////////////////////////////////////
28 
29 /*
30  * Lazy -- for delayed initialization of a value.  The value's
31  * initialization will be computed on demand at its first use, but
32  * will not be recomputed if its value is requested again.  The value
33  * may still be mutated after its initialization if the lazy is not
34  * declared const.
35  *
36  * The value is created using folly::lazy, usually with a lambda, and
37  * its value is requested using operator().
38  *
39  * Note that the value is not safe for concurrent accesses by multiple
40  * threads, even if you declare it const.  See note below.
41  *
42  *
43  * Example Usage:
44  *
45  *   void foo() {
46  *     auto const val = folly::lazy([&]{
47  *       return something_expensive(blah());
48  *     });
49  *
50  *     if (condition1) {
51  *       use(val());
52  *     }
53  *     if (condition2) {
54  *       useMaybeAgain(val());
55  *     } else {
56  *       // Unneeded in this branch.
57  *     }
58  *   }
59  *
60  *
61  * Rationale:
62  *
63  *    - operator() is used to request the value instead of an implicit
64  *      conversion because the slight syntactic overhead in common
65  *      seems worth the increased clarity.
66  *
67  *    - Lazy values do not model CopyConstructible because it is
68  *      unclear what semantics would be desirable.  Either copies
69  *      should share the cached value (adding overhead to cases that
70  *      don't need to support copies), or they could recompute the
71  *      value unnecessarily.  Sharing with mutable lazies would also
72  *      leave them with non-value semantics despite looking
73  *      value-like.
74  *
75  *    - Not thread safe for const accesses.  Many use cases for lazy
76  *      values are local variables on the stack, where multiple
77  *      threads shouldn't even be able to reach the value.  It still
78  *      is useful to indicate/check that the value doesn't change with
79  *      const, particularly when it is captured by a large family of
80  *      lambdas.  Adding internal synchronization seems like it would
81  *      pessimize the most common use case in favor of less likely use
82  *      cases.
83  *
84  */
85 
86 //////////////////////////////////////////////////////////////////////
87 
88 namespace detail {
89 
90 template <class Func>
91 struct Lazy {
92   typedef invoke_result_t<Func> result_type;
93 
94   static_assert(
95       !std::is_const<Func>::value, "Func should not be a const-qualified type");
96   static_assert(
97       !std::is_reference<Func>::value, "Func should not be a reference type");
98 
LazyLazy99   explicit Lazy(Func&& f) : func_(std::move(f)) {}
LazyLazy100   explicit Lazy(const Func& f) : func_(f) {}
101 
LazyLazy102   Lazy(Lazy&& o) : value_(std::move(o.value_)), func_(std::move(o.func_)) {}
103 
104   Lazy(const Lazy&) = delete;
105   Lazy& operator=(const Lazy&) = delete;
106   Lazy& operator=(Lazy&&) = delete;
107 
operatorLazy108   const result_type& operator()() const {
109     ensure_initialized();
110 
111     return *value_;
112   }
113 
operatorLazy114   result_type& operator()() {
115     ensure_initialized();
116 
117     return *value_;
118   }
119 
120  private:
ensure_initializedLazy121   void ensure_initialized() const {
122     if (!value_) {
123       value_ = func_();
124     }
125   }
126 
127   mutable Optional<result_type> value_;
128   mutable Func func_;
129 };
130 
131 } // namespace detail
132 
133 //////////////////////////////////////////////////////////////////////
134 
135 template <class Func>
lazy(Func && fun)136 auto lazy(Func&& fun) {
137   return detail::Lazy<remove_cvref_t<Func>>(std::forward<Func>(fun));
138 }
139 
140 //////////////////////////////////////////////////////////////////////
141 
142 } // namespace folly
143