Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef SkTLazy_DEFINED
      9 #define SkTLazy_DEFINED
     10 
     11 #include "../private/SkTemplates.h"
     12 #include "SkTypes.h"
     13 #include <new>
     14 #include <utility>
     15 
     16 /**
     17  *  Efficient way to defer allocating/initializing a class until it is needed
     18  *  (if ever).
     19  */
     20 template <typename T> class SkTLazy {
     21 public:
     22     SkTLazy() : fPtr(NULL) {}
     23 
     24     explicit SkTLazy(const T* src) : fPtr(NULL) {
     25         if (src) {
     26             fPtr = new (fStorage.get()) T(*src);
     27         }
     28     }
     29 
     30     SkTLazy(const SkTLazy<T>& src) : fPtr(NULL) {
     31         if (src.isValid()) {
     32             fPtr = new (fStorage.get()) T(*src->get());
     33         } else {
     34             fPtr = NULL;
     35         }
     36     }
     37 
     38     ~SkTLazy() {
     39         if (this->isValid()) {
     40             fPtr->~T();
     41         }
     42     }
     43 
     44     /**
     45      *  Return a pointer to an instance of the class initialized with 'args'.
     46      *  If a previous instance had been initialized (either from init() or
     47      *  set()) it will first be destroyed, so that a freshly initialized
     48      *  instance is always returned.
     49      */
     50     template <typename... Args> T* init(Args&&... args) {
     51         if (this->isValid()) {
     52             fPtr->~T();
     53         }
     54         fPtr = new (SkTCast<T*>(fStorage.get())) T(std::forward<Args>(args)...);
     55         return fPtr;
     56     }
     57 
     58     /**
     59      *  Copy src into this, and return a pointer to a copy of it. Note this
     60      *  will always return the same pointer, so if it is called on a lazy that
     61      *  has already been initialized, then this will copy over the previous
     62      *  contents.
     63      */
     64     T* set(const T& src) {
     65         if (this->isValid()) {
     66             *fPtr = src;
     67         } else {
     68             fPtr = new (SkTCast<T*>(fStorage.get())) T(src);
     69         }
     70         return fPtr;
     71     }
     72 
     73     /**
     74      * Destroy the lazy object (if it was created via init() or set())
     75      */
     76     void reset() {
     77         if (this->isValid()) {
     78             fPtr->~T();
     79             fPtr = NULL;
     80         }
     81     }
     82 
     83     /**
     84      *  Returns true if a valid object has been initialized in the SkTLazy,
     85      *  false otherwise.
     86      */
     87     bool isValid() const { return SkToBool(fPtr); }
     88 
     89     /**
     90      * Returns the object. This version should only be called when the caller
     91      * knows that the object has been initialized.
     92      */
     93     T* get() const { SkASSERT(this->isValid()); return fPtr; }
     94 
     95     /**
     96      * Like above but doesn't assert if object isn't initialized (in which case
     97      * NULL is returned).
     98      */
     99     T* getMaybeNull() const { return fPtr; }
    100 
    101 private:
    102     T* fPtr; // NULL or fStorage
    103     SkAlignedSTStorage<1, T> fStorage;
    104 };
    105 
    106 /**
    107  * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized
    108  * with a const pointer but provides a non-const pointer accessor. The first time the
    109  * accessor is called (if ever) the object is cloned.
    110  *
    111  * In the following example at most one copy of constThing is made:
    112  *
    113  * SkTCopyOnFirstWrite<Thing> thing(&constThing);
    114  * ...
    115  * function_that_takes_a_const_thing_ptr(thing); // constThing is passed
    116  * ...
    117  * if (need_to_modify_thing()) {
    118  *    thing.writable()->modifyMe(); // makes a copy of constThing
    119  * }
    120  * ...
    121  * x = thing->readSomething();
    122  * ...
    123  * if (need_to_modify_thing_now()) {
    124  *    thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe()
    125  * }
    126  *
    127  * consume_a_thing(thing); // could be constThing or a modified copy.
    128  */
    129 template <typename T>
    130 class SkTCopyOnFirstWrite {
    131 public:
    132     SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
    133 
    134     SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {}
    135 
    136     // Constructor for delayed initialization.
    137     SkTCopyOnFirstWrite() : fObj(NULL) {}
    138 
    139     // Should only be called once, and only if the default constructor was used.
    140     void init(const T& initial) {
    141         SkASSERT(NULL == fObj);
    142         SkASSERT(!fLazy.isValid());
    143         fObj = &initial;
    144     }
    145 
    146     /**
    147      * Returns a writable T*. The first time this is called the initial object is cloned.
    148      */
    149     T* writable() {
    150         SkASSERT(fObj);
    151         if (!fLazy.isValid()) {
    152             fLazy.set(*fObj);
    153             fObj = fLazy.get();
    154         }
    155         return const_cast<T*>(fObj);
    156     }
    157 
    158     /**
    159      * Operators for treating this as though it were a const pointer.
    160      */
    161 
    162     const T *operator->() const { return fObj; }
    163 
    164     operator const T*() const { return fObj; }
    165 
    166     const T& operator *() const { return *fObj; }
    167 
    168 private:
    169     const T*    fObj;
    170     SkTLazy<T>  fLazy;
    171 };
    172 
    173 #endif
    174