17ec681f3Smrg//
27ec681f3Smrg// Copyright 2013 Francisco Jerez
37ec681f3Smrg//
47ec681f3Smrg// Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg// copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg// to deal in the Software without restriction, including without limitation
77ec681f3Smrg// the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg// and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg// Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg//
117ec681f3Smrg// The above copyright notice and this permission notice shall be included in
127ec681f3Smrg// all copies or substantial portions of the Software.
137ec681f3Smrg//
147ec681f3Smrg// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
157ec681f3Smrg// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
167ec681f3Smrg// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
177ec681f3Smrg// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
187ec681f3Smrg// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
197ec681f3Smrg// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
207ec681f3Smrg// OTHER DEALINGS IN THE SOFTWARE.
217ec681f3Smrg//
227ec681f3Smrg
237ec681f3Smrg#ifndef CLOVER_UTIL_LAZY_HPP
247ec681f3Smrg#define CLOVER_UTIL_LAZY_HPP
257ec681f3Smrg
267ec681f3Smrg#include <type_traits>
277ec681f3Smrg#include <stdexcept>
287ec681f3Smrg#include <memory>
297ec681f3Smrg
307ec681f3Smrgnamespace clover {
317ec681f3Smrg   namespace detail {
327ec681f3Smrg      template<typename T>
337ec681f3Smrg      class basic_lazy {
347ec681f3Smrg      public:
357ec681f3Smrg         virtual
367ec681f3Smrg         ~basic_lazy() {
377ec681f3Smrg         }
387ec681f3Smrg
397ec681f3Smrg         virtual basic_lazy *
407ec681f3Smrg         clone() const = 0;
417ec681f3Smrg
427ec681f3Smrg         virtual
437ec681f3Smrg         operator T() const = 0;
447ec681f3Smrg      };
457ec681f3Smrg
467ec681f3Smrg      template<typename T, typename F>
477ec681f3Smrg      class deferred_lazy : public basic_lazy<T> {
487ec681f3Smrg      public:
497ec681f3Smrg         template<typename G>
507ec681f3Smrg         deferred_lazy(G &&f) : f(new F(std::forward<G>(f))) {
517ec681f3Smrg         }
527ec681f3Smrg
537ec681f3Smrg         virtual basic_lazy<T> *
547ec681f3Smrg         clone() const {
557ec681f3Smrg            return new deferred_lazy(*this);
567ec681f3Smrg         }
577ec681f3Smrg
587ec681f3Smrg         operator T() const {
597ec681f3Smrg            if (f) {
607ec681f3Smrg               x = (*f)();
617ec681f3Smrg               f = {};
627ec681f3Smrg            }
637ec681f3Smrg
647ec681f3Smrg            return x;
657ec681f3Smrg         }
667ec681f3Smrg
677ec681f3Smrg      private:
687ec681f3Smrg         mutable std::shared_ptr<F> f;
697ec681f3Smrg         mutable T x;
707ec681f3Smrg      };
717ec681f3Smrg
727ec681f3Smrg      template<typename T>
737ec681f3Smrg      class strict_lazy : public basic_lazy<T> {
747ec681f3Smrg      public:
757ec681f3Smrg         template<typename S>
767ec681f3Smrg         strict_lazy(S &&x) : x(std::forward<S>(x)) {
777ec681f3Smrg         }
787ec681f3Smrg
797ec681f3Smrg         virtual basic_lazy<T> *
807ec681f3Smrg         clone() const {
817ec681f3Smrg            return new strict_lazy(*this);
827ec681f3Smrg         }
837ec681f3Smrg
847ec681f3Smrg         operator T() const {
857ec681f3Smrg            return x;
867ec681f3Smrg         }
877ec681f3Smrg
887ec681f3Smrg      private:
897ec681f3Smrg         T x;
907ec681f3Smrg      };
917ec681f3Smrg   }
927ec681f3Smrg
937ec681f3Smrg   ///
947ec681f3Smrg   /// Object that represents a value of type \a T that is calculated
957ec681f3Smrg   /// lazily as soon as it is required.
967ec681f3Smrg   ///
977ec681f3Smrg   template<typename T>
987ec681f3Smrg   class lazy {
997ec681f3Smrg   public:
1007ec681f3Smrg      class undefined_error : std::logic_error {
1017ec681f3Smrg      public:
1027ec681f3Smrg         undefined_error() : std::logic_error("") {
1037ec681f3Smrg         }
1047ec681f3Smrg      };
1057ec681f3Smrg
1067ec681f3Smrg      ///
1077ec681f3Smrg      /// Initialize to some fixed value \a x which isn't calculated
1087ec681f3Smrg      /// lazily.
1097ec681f3Smrg      ///
1107ec681f3Smrg      lazy(T x) : obj(new detail::strict_lazy<T>(x)) {
1117ec681f3Smrg      }
1127ec681f3Smrg
1137ec681f3Smrg      ///
1147ec681f3Smrg      /// Initialize by providing a functor \a f that will calculate
1157ec681f3Smrg      /// the value on-demand.
1167ec681f3Smrg      ///
1177ec681f3Smrg      template<typename F>
1187ec681f3Smrg      lazy(F &&f) : obj(new detail::deferred_lazy<
1197ec681f3Smrg                           T, typename std::remove_reference<F>::type
1207ec681f3Smrg                        >(std::forward<F>(f))) {
1217ec681f3Smrg      }
1227ec681f3Smrg
1237ec681f3Smrg      ///
1247ec681f3Smrg      /// Initialize to undefined.
1257ec681f3Smrg      ///
1267ec681f3Smrg      lazy() : lazy([]() {
1277ec681f3Smrg               throw undefined_error();
1287ec681f3Smrg               return T();
1297ec681f3Smrg            }) {
1307ec681f3Smrg      }
1317ec681f3Smrg
1327ec681f3Smrg      lazy(const lazy &other) : obj(obj->clone()) {
1337ec681f3Smrg      }
1347ec681f3Smrg
1357ec681f3Smrg      lazy(lazy &&other) : obj(NULL) {
1367ec681f3Smrg         std::swap(obj, other.obj);
1377ec681f3Smrg      }
1387ec681f3Smrg
1397ec681f3Smrg      ~lazy() {
1407ec681f3Smrg         delete obj;
1417ec681f3Smrg      }
1427ec681f3Smrg
1437ec681f3Smrg      lazy &
1447ec681f3Smrg      operator=(lazy other) {
1457ec681f3Smrg         std::swap(obj, other.obj);
1467ec681f3Smrg         return *this;
1477ec681f3Smrg      }
1487ec681f3Smrg
1497ec681f3Smrg      ///
1507ec681f3Smrg      /// Evaluate the value.
1517ec681f3Smrg      ///
1527ec681f3Smrg      operator T() const {
1537ec681f3Smrg         return *obj;
1547ec681f3Smrg      }
1557ec681f3Smrg
1567ec681f3Smrg   private:
1577ec681f3Smrg      detail::basic_lazy<T> *obj;
1587ec681f3Smrg   };
1597ec681f3Smrg}
1607ec681f3Smrg
1617ec681f3Smrg#endif
162