/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include namespace folly { ////////////////////////////////////////////////////////////////////// /* * Lazy -- for delayed initialization of a value. The value's * initialization will be computed on demand at its first use, but * will not be recomputed if its value is requested again. The value * may still be mutated after its initialization if the lazy is not * declared const. * * The value is created using folly::lazy, usually with a lambda, and * its value is requested using operator(). * * Note that the value is not safe for concurrent accesses by multiple * threads, even if you declare it const. See note below. * * * Example Usage: * * void foo() { * auto const val = folly::lazy([&]{ * return something_expensive(blah()); * }); * * if (condition1) { * use(val()); * } * if (condition2) { * useMaybeAgain(val()); * } else { * // Unneeded in this branch. * } * } * * * Rationale: * * - operator() is used to request the value instead of an implicit * conversion because the slight syntactic overhead in common * seems worth the increased clarity. * * - Lazy values do not model CopyConstructible because it is * unclear what semantics would be desirable. Either copies * should share the cached value (adding overhead to cases that * don't need to support copies), or they could recompute the * value unnecessarily. Sharing with mutable lazies would also * leave them with non-value semantics despite looking * value-like. * * - Not thread safe for const accesses. Many use cases for lazy * values are local variables on the stack, where multiple * threads shouldn't even be able to reach the value. It still * is useful to indicate/check that the value doesn't change with * const, particularly when it is captured by a large family of * lambdas. Adding internal synchronization seems like it would * pessimize the most common use case in favor of less likely use * cases. * */ ////////////////////////////////////////////////////////////////////// namespace detail { template struct Lazy { typedef invoke_result_t result_type; static_assert( !std::is_const::value, "Func should not be a const-qualified type"); static_assert( !std::is_reference::value, "Func should not be a reference type"); explicit Lazy(Func&& f) : func_(std::move(f)) {} explicit Lazy(const Func& f) : func_(f) {} Lazy(Lazy&& o) : value_(std::move(o.value_)), func_(std::move(o.func_)) {} Lazy(const Lazy&) = delete; Lazy& operator=(const Lazy&) = delete; Lazy& operator=(Lazy&&) = delete; const result_type& operator()() const { ensure_initialized(); return *value_; } result_type& operator()() { ensure_initialized(); return *value_; } private: void ensure_initialized() const { if (!value_) { value_ = func_(); } } mutable Optional value_; mutable Func func_; }; } // namespace detail ////////////////////////////////////////////////////////////////////// template auto lazy(Func&& fun) { return detail::Lazy>(std::forward(fun)); } ////////////////////////////////////////////////////////////////////// } // namespace folly