1//
2// Copyright 2013 Francisco Jerez
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20// OTHER DEALINGS IN THE SOFTWARE.
21//
22
23#ifndef CLOVER_UTIL_LAZY_HPP
24#define CLOVER_UTIL_LAZY_HPP
25
26#include <type_traits>
27#include <stdexcept>
28#include <memory>
29
30namespace clover {
31   namespace detail {
32      template<typename T>
33      class basic_lazy {
34      public:
35         virtual
36         ~basic_lazy() {
37         }
38
39         virtual basic_lazy *
40         clone() const = 0;
41
42         virtual
43         operator T() const = 0;
44      };
45
46      template<typename T, typename F>
47      class deferred_lazy : public basic_lazy<T> {
48      public:
49         template<typename G>
50         deferred_lazy(G &&f) : f(new F(std::forward<G>(f))) {
51         }
52
53         virtual basic_lazy<T> *
54         clone() const {
55            return new deferred_lazy(*this);
56         }
57
58         operator T() const {
59            if (f) {
60               x = (*f)();
61               f = {};
62            }
63
64            return x;
65         }
66
67      private:
68         mutable std::shared_ptr<F> f;
69         mutable T x;
70      };
71
72      template<typename T>
73      class strict_lazy : public basic_lazy<T> {
74      public:
75         template<typename S>
76         strict_lazy(S &&x) : x(std::forward<S>(x)) {
77         }
78
79         virtual basic_lazy<T> *
80         clone() const {
81            return new strict_lazy(*this);
82         }
83
84         operator T() const {
85            return x;
86         }
87
88      private:
89         T x;
90      };
91   }
92
93   ///
94   /// Object that represents a value of type \a T that is calculated
95   /// lazily as soon as it is required.
96   ///
97   template<typename T>
98   class lazy {
99   public:
100      class undefined_error : std::logic_error {
101      public:
102         undefined_error() : std::logic_error("") {
103         }
104      };
105
106      ///
107      /// Initialize to some fixed value \a x which isn't calculated
108      /// lazily.
109      ///
110      lazy(T x) : obj(new detail::strict_lazy<T>(x)) {
111      }
112
113      ///
114      /// Initialize by providing a functor \a f that will calculate
115      /// the value on-demand.
116      ///
117      template<typename F>
118      lazy(F &&f) : obj(new detail::deferred_lazy<
119                           T, typename std::remove_reference<F>::type
120                        >(std::forward<F>(f))) {
121      }
122
123      ///
124      /// Initialize to undefined.
125      ///
126      lazy() : lazy([]() {
127               throw undefined_error();
128               return T();
129            }) {
130      }
131
132      lazy(const lazy &other) : obj(obj->clone()) {
133      }
134
135      lazy(lazy &&other) : obj(NULL) {
136         std::swap(obj, other.obj);
137      }
138
139      ~lazy() {
140         delete obj;
141      }
142
143      lazy &
144      operator=(lazy other) {
145         std::swap(obj, other.obj);
146         return *this;
147      }
148
149      ///
150      /// Evaluate the value.
151      ///
152      operator T() const {
153         return *obj;
154      }
155
156   private:
157      detail::basic_lazy<T> *obj;
158   };
159}
160
161#endif
162