--- /dev/null
+/*
+ * Copyright 2013 Facebook, Inc.
+ *
+ * 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.
+ */
+#ifndef FOLLY_LAZY_H_
+#define FOLLY_LAZY_H_
+
+#include <utility>
+#include <type_traits>
+
+#include "folly/Optional.h"
+
+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 current accesses by multiple
+ * threads, even if you declare it const.
+ *
+ *
+ * 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.
+ */
+
+//////////////////////////////////////////////////////////////////////
+
+namespace detail {
+
+template<class Func>
+struct Lazy {
+ typedef typename std::result_of<Func()>::type result_type;
+
+ explicit Lazy(Func&& f) : func_(std::move(f)) {}
+ explicit Lazy(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 {
+ return const_cast<Lazy&>(*this)();
+ }
+
+ result_type& operator()() {
+ if (!value_) value_ = func_();
+ return *value_;
+ }
+
+private:
+ Optional<result_type> value_;
+ Func func_;
+};
+
+}
+
+//////////////////////////////////////////////////////////////////////
+
+template<class Func>
+detail::Lazy<typename std::remove_reference<Func>::type>
+lazy(Func&& fun) {
+ return detail::Lazy<typename std::remove_reference<Func>::type>(
+ std::forward<Func>(fun)
+ );
+}
+
+//////////////////////////////////////////////////////////////////////
+
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright 2013 Facebook, Inc.
+ *
+ * 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.
+ */
+#include "folly/Lazy.h"
+
+#include <map>
+#include <functional>
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+namespace folly {
+
+TEST(Lazy, Simple) {
+ int computeCount = 0;
+
+ auto const val = folly::lazy([&]() -> int {
+ EXPECT_EQ(++computeCount, 1);
+ return 12;
+ });
+ EXPECT_EQ(computeCount, 0);
+
+ for (int i = 0; i < 100; ++i) {
+ if (i > 50) {
+ EXPECT_EQ(val(), 12);
+ EXPECT_EQ(computeCount, 1);
+ } else {
+ EXPECT_EQ(computeCount, 0);
+ }
+ }
+ EXPECT_EQ(val(), 12);
+ EXPECT_EQ(computeCount, 1);
+}
+
+auto globalCount = folly::lazy([]{ return 0; });
+auto const foo = folly::lazy([]() -> std::string {
+ EXPECT_EQ(++globalCount(), 1);
+ return std::string("YEP");
+});
+
+TEST(Lazy, Global) {
+ EXPECT_EQ(globalCount(), 0);
+ EXPECT_EQ(foo(), "YEP");
+ EXPECT_EQ(globalCount(), 1);
+}
+
+TEST(Lazy, Map) {
+ auto lazyMap = folly::lazy([]() -> std::map<std::string,std::string> {
+ return {
+ { "foo", "bar" },
+ { "baz", "quux" }
+ };
+ });
+
+ EXPECT_EQ(lazyMap().size(), 2);
+ lazyMap()["blah"] = "asd";
+ EXPECT_EQ(lazyMap().size(), 3);
+}
+
+struct CopyCount {
+ CopyCount() {}
+ CopyCount(const CopyCount&) { ++count; }
+ CopyCount(CopyCount&&) {}
+
+ static int count;
+
+ bool operator()() const { return true ; }
+};
+
+int CopyCount::count = 0;
+
+TEST(Lazy, NonLambda) {
+ auto const rval = folly::lazy(CopyCount());
+ EXPECT_EQ(CopyCount::count, 0);
+ EXPECT_EQ(rval(), true);
+ EXPECT_EQ(CopyCount::count, 0);
+
+ CopyCount cpy;
+ auto const lval = folly::lazy(cpy);
+ EXPECT_EQ(CopyCount::count, 1);
+ EXPECT_EQ(lval(), true);
+ EXPECT_EQ(CopyCount::count, 1);
+
+ std::function<bool()> f = [&]{ return 12; };
+ auto const lazyF = folly::lazy(f);
+ EXPECT_EQ(lazyF(), true);
+}
+
+TEST(Lazy, Consty) {
+ std::function<int ()> const f = [&] { return 12; };
+ auto lz = folly::lazy(f);
+ EXPECT_EQ(lz(), 12);
+}
+
+}
+