Home | History | Annotate | Download | only in util
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Immutable<T> provides an easy, cheap, and thread-safe way to pass
      6 // large immutable data around.
      7 //
      8 // For example, consider the following code:
      9 //
     10 //  typedef std::vector<LargeObject> LargeObjectList;
     11 //
     12 //   void ProcessStuff(const LargeObjectList& stuff) {
     13 //     for (LargeObjectList::const_iterator it = stuff.begin();
     14 //          it != stuff.end(); ++it) {
     15 //       ... process it ...
     16 //     }
     17 //   }
     18 //
     19 //   ...
     20 //
     21 //   LargeObjectList my_stuff;
     22 //   ... fill my_stuff with lots of LargeObjects ...
     23 //   some_loop->PostTask(FROM_HERE, base::Bind(&ProcessStuff, my_stuff));
     24 //
     25 // The last line incurs the cost of copying my_stuff, which is
     26 // undesirable.  Here's the above code re-written using Immutable<T>:
     27 //
     28 //   void ProcessStuff(const Immutable<LargeObjectList>& stuff) {
     29 //     for (LargeObjectList::const_iterator it = stuff.Get().begin();
     30 //          it != stuff.Get().end(); ++it) {
     31 //       ... process it ...
     32 //     }
     33 //   }
     34 //
     35 //   ...
     36 //
     37 //   LargeObjectList my_stuff;
     38 //   ... fill my_stuff with lots of LargeObjects ...
     39 //   some_loop->PostTask(
     40 //       FROM_HERE, base::Bind(&ProcessStuff, MakeImmutable(&my_stuff)));
     41 //
     42 // The last line, which resets my_stuff to a default-initialized
     43 // state, incurs only the cost of a swap of LargeObjectLists, which is
     44 // O(1) for most STL container implementations.  The data in my_stuff
     45 // is ref-counted (thread-safely), so it is freed as soon as
     46 // ProcessStuff is finished.
     47 //
     48 // NOTE: By default, Immutable<T> relies on ADL
     49 // (http://en.wikipedia.org/wiki/Argument-dependent_name_lookup) to
     50 // find a swap() function for T, falling back to std::swap() when
     51 // necessary.  If you overload swap() for your type in its namespace,
     52 // or if you specialize std::swap() for your type, (see
     53 // http://stackoverflow.com/questions/11562/how-to-overload-stdswap
     54 // for discussion) Immutable<T> should be able to find it.
     55 //
     56 // Alternatively, you could explicitly control which swap function is
     57 // used by providing your own traits class or using one of the
     58 // pre-defined ones below.  See comments on traits below for details.
     59 //
     60 // NOTE: Some complexity is necessary in order to use Immutable<T>
     61 // with forward-declared types.  See comments on traits below for
     62 // details.
     63 
     64 #ifndef SYNC_UTIL_IMMUTABLE_H_
     65 #define SYNC_UTIL_IMMUTABLE_H_
     66 
     67 // For std::swap().
     68 #include <algorithm>
     69 
     70 #include "base/basictypes.h"
     71 #include "base/memory/ref_counted.h"
     72 
     73 namespace syncer {
     74 
     75 namespace internal {
     76 // This class is part of the Immutable implementation.  DO NOT USE
     77 // THIS CLASS DIRECTLY YOURSELF.
     78 
     79 template <typename T, typename Traits>
     80 class ImmutableCore
     81     : public base::RefCountedThreadSafe<ImmutableCore<T, Traits> > {
     82  public:
     83   // wrapper_ is always explicitly default-initialized to handle
     84   // primitive types and the case where Traits::Wrapper == T.
     85 
     86   ImmutableCore() : wrapper_() {
     87     Traits::InitializeWrapper(&wrapper_);
     88   }
     89 
     90   explicit ImmutableCore(T* t) : wrapper_() {
     91     Traits::InitializeWrapper(&wrapper_);
     92     Traits::Swap(Traits::UnwrapMutable(&wrapper_), t);
     93   }
     94 
     95   const T& Get() const {
     96     return Traits::Unwrap(wrapper_);
     97   }
     98 
     99  private:
    100   ~ImmutableCore() {
    101     Traits::DestroyWrapper(&wrapper_);
    102   }
    103   friend class base::RefCountedThreadSafe<ImmutableCore<T, Traits> >;
    104 
    105   // This is semantically const, but we can't mark it a such as we
    106   // modify it in the constructor.
    107   typename Traits::Wrapper wrapper_;
    108 
    109   DISALLOW_COPY_AND_ASSIGN(ImmutableCore);
    110 };
    111 
    112 }  // namespace internal
    113 
    114 // Traits usage notes
    115 // ------------------
    116 // The most common reason to use your own traits class is to provide
    117 // your own swap method.  First, consider the pre-defined traits
    118 // classes HasSwapMemFn{ByRef,ByPtr} below.  If neither of those work,
    119 // then define your own traits class inheriting from
    120 // DefaultImmutableTraits<YourType> (to pick up the defaults for
    121 // everything else) and provide your own Swap() method.
    122 //
    123 // Another reason to use your own traits class is to be able to use
    124 // Immutable<T> with a forward-declared type (important for protobuf
    125 // classes, when you want to avoid headers pulling in generated
    126 // headers).  (This is why the Traits::Wrapper type exists; normally,
    127 // Traits::Wrapper is just T itself, but that needs to be changed for
    128 // forward-declared types.)
    129 //
    130 // For example, if you want to do this:
    131 //
    132 //   my_class.h
    133 //   ----------
    134 //   #include ".../immutable.h"
    135 //
    136 //   // Forward declaration.
    137 //   class SomeOtherType;
    138 //
    139 //   class MyClass {
    140 //     ...
    141 //    private:
    142 //     // Doesn't work, as defaults traits class needs SomeOtherType's
    143 //     // definition to be visible.
    144 //     Immutable<SomeOtherType> foo_;
    145 //   };
    146 //
    147 // You'll have to do this:
    148 //
    149 //   my_class.h
    150 //   ----------
    151 //   #include ".../immutable.h"
    152 //
    153 //   // Forward declaration.
    154 //   class SomeOtherType;
    155 //
    156 //   class MyClass {
    157 //     ...
    158 //    private:
    159 //     struct ImmutableSomeOtherTypeTraits {
    160 //       // scoped_ptr<SomeOtherType> won't work here, either.
    161 //       typedef SomeOtherType* Wrapper;
    162 //
    163 //       static void InitializeWrapper(Wrapper* wrapper);
    164 //
    165 //       static void DestroyWrapper(Wrapper* wrapper);
    166 //       ...
    167 //     };
    168 //
    169 //     typedef Immutable<SomeOtherType, ImmutableSomeOtherTypeTraits>
    170 //         ImmutableSomeOtherType;
    171 //
    172 //     ImmutableSomeOtherType foo_;
    173 //   };
    174 //
    175 //   my_class.cc
    176 //   -----------
    177 //   #include ".../some_other_type.h"
    178 //
    179 //   void MyClass::ImmutableSomeOtherTypeTraits::InitializeWrapper(
    180 //       Wrapper* wrapper) {
    181 //     *wrapper = new SomeOtherType();
    182 //   }
    183 //
    184 //   void MyClass::ImmutableSomeOtherTypeTraits::DestroyWrapper(
    185 //       Wrapper* wrapper) {
    186 //     delete *wrapper;
    187 //   }
    188 //
    189 //   ...
    190 //
    191 // Also note that this incurs an additional memory allocation when you
    192 // create an Immutable<SomeOtherType>.
    193 
    194 template <typename T>
    195 struct DefaultImmutableTraits {
    196   typedef T Wrapper;
    197 
    198   static void InitializeWrapper(Wrapper* wrapper) {}
    199 
    200   static void DestroyWrapper(Wrapper* wrapper) {}
    201 
    202   static const T& Unwrap(const Wrapper& wrapper) { return wrapper; }
    203 
    204   static T* UnwrapMutable(Wrapper* wrapper) { return wrapper; }
    205 
    206   static void Swap(T* t1, T* t2) {
    207     // Uses ADL (see
    208     // http://en.wikipedia.org/wiki/Argument-dependent_name_lookup).
    209     using std::swap;
    210     swap(*t1, *t2);
    211   }
    212 };
    213 
    214 // Most STL containers have by-reference swap() member functions,
    215 // although they usually already overload std::swap() to use those.
    216 template <typename T>
    217 struct HasSwapMemFnByRef : public DefaultImmutableTraits<T> {
    218   static void Swap(T* t1, T* t2) {
    219     t1->swap(*t2);
    220   }
    221 };
    222 
    223 // Most Google-style objects have by-pointer Swap() member functions
    224 // (for example, generated protocol buffer classes).
    225 template <typename T>
    226 struct HasSwapMemFnByPtr : public DefaultImmutableTraits<T> {
    227   static void Swap(T* t1, T* t2) {
    228     t1->Swap(t2);
    229   }
    230 };
    231 
    232 template <typename T, typename Traits = DefaultImmutableTraits<T> >
    233 class Immutable {
    234  public:
    235   // Puts the underlying object in a default-initialized state.
    236   Immutable() : core_(new internal::ImmutableCore<T, Traits>()) {}
    237 
    238   // Copy constructor and assignment welcome.
    239 
    240   // Resets |t| to a default-initialized state.
    241   explicit Immutable(T* t)
    242       : core_(new internal::ImmutableCore<T, Traits>(t)) {}
    243 
    244   const T& Get() const {
    245     return core_->Get();
    246   }
    247 
    248  private:
    249   scoped_refptr<const internal::ImmutableCore<T, Traits> > core_;
    250 };
    251 
    252 // Helper function to avoid having to write out template arguments.
    253 template <typename T>
    254 Immutable<T> MakeImmutable(T* t) {
    255   return Immutable<T>(t);
    256 }
    257 
    258 }  // namespace syncer
    259 
    260 #endif  // SYNC_UTIL_IMMUTABLE_H_
    261