C++程序  |  261行  |  7.55 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Immutable<T> provides an easy, cheap, and thread-safe way to pass
// large immutable data around.
//
// For example, consider the following code:
//
//  typedef std::vector<LargeObject> LargeObjectList;
//
//   void ProcessStuff(const LargeObjectList& stuff) {
//     for (LargeObjectList::const_iterator it = stuff.begin();
//          it != stuff.end(); ++it) {
//       ... process it ...
//     }
//   }
//
//   ...
//
//   LargeObjectList my_stuff;
//   ... fill my_stuff with lots of LargeObjects ...
//   some_loop->PostTask(FROM_HERE, base::Bind(&ProcessStuff, my_stuff));
//
// The last line incurs the cost of copying my_stuff, which is
// undesirable.  Here's the above code re-written using Immutable<T>:
//
//   void ProcessStuff(const Immutable<LargeObjectList>& stuff) {
//     for (LargeObjectList::const_iterator it = stuff.Get().begin();
//          it != stuff.Get().end(); ++it) {
//       ... process it ...
//     }
//   }
//
//   ...
//
//   LargeObjectList my_stuff;
//   ... fill my_stuff with lots of LargeObjects ...
//   some_loop->PostTask(
//       FROM_HERE, base::Bind(&ProcessStuff, MakeImmutable(&my_stuff)));
//
// The last line, which resets my_stuff to a default-initialized
// state, incurs only the cost of a swap of LargeObjectLists, which is
// O(1) for most STL container implementations.  The data in my_stuff
// is ref-counted (thread-safely), so it is freed as soon as
// ProcessStuff is finished.
//
// NOTE: By default, Immutable<T> relies on ADL
// (http://en.wikipedia.org/wiki/Argument-dependent_name_lookup) to
// find a swap() function for T, falling back to std::swap() when
// necessary.  If you overload swap() for your type in its namespace,
// or if you specialize std::swap() for your type, (see
// http://stackoverflow.com/questions/11562/how-to-overload-stdswap
// for discussion) Immutable<T> should be able to find it.
//
// Alternatively, you could explicitly control which swap function is
// used by providing your own traits class or using one of the
// pre-defined ones below.  See comments on traits below for details.
//
// NOTE: Some complexity is necessary in order to use Immutable<T>
// with forward-declared types.  See comments on traits below for
// details.

#ifndef SYNC_UTIL_IMMUTABLE_H_
#define SYNC_UTIL_IMMUTABLE_H_

// For std::swap().
#include <algorithm>

#include "base/basictypes.h"
#include "base/memory/ref_counted.h"

namespace syncer {

namespace internal {
// This class is part of the Immutable implementation.  DO NOT USE
// THIS CLASS DIRECTLY YOURSELF.

template <typename T, typename Traits>
class ImmutableCore
    : public base::RefCountedThreadSafe<ImmutableCore<T, Traits> > {
 public:
  // wrapper_ is always explicitly default-initialized to handle
  // primitive types and the case where Traits::Wrapper == T.

  ImmutableCore() : wrapper_() {
    Traits::InitializeWrapper(&wrapper_);
  }

  explicit ImmutableCore(T* t) : wrapper_() {
    Traits::InitializeWrapper(&wrapper_);
    Traits::Swap(Traits::UnwrapMutable(&wrapper_), t);
  }

  const T& Get() const {
    return Traits::Unwrap(wrapper_);
  }

 private:
  ~ImmutableCore() {
    Traits::DestroyWrapper(&wrapper_);
  }
  friend class base::RefCountedThreadSafe<ImmutableCore<T, Traits> >;

  // This is semantically const, but we can't mark it a such as we
  // modify it in the constructor.
  typename Traits::Wrapper wrapper_;

  DISALLOW_COPY_AND_ASSIGN(ImmutableCore);
};

}  // namespace internal

// Traits usage notes
// ------------------
// The most common reason to use your own traits class is to provide
// your own swap method.  First, consider the pre-defined traits
// classes HasSwapMemFn{ByRef,ByPtr} below.  If neither of those work,
// then define your own traits class inheriting from
// DefaultImmutableTraits<YourType> (to pick up the defaults for
// everything else) and provide your own Swap() method.
//
// Another reason to use your own traits class is to be able to use
// Immutable<T> with a forward-declared type (important for protobuf
// classes, when you want to avoid headers pulling in generated
// headers).  (This is why the Traits::Wrapper type exists; normally,
// Traits::Wrapper is just T itself, but that needs to be changed for
// forward-declared types.)
//
// For example, if you want to do this:
//
//   my_class.h
//   ----------
//   #include ".../immutable.h"
//
//   // Forward declaration.
//   class SomeOtherType;
//
//   class MyClass {
//     ...
//    private:
//     // Doesn't work, as defaults traits class needs SomeOtherType's
//     // definition to be visible.
//     Immutable<SomeOtherType> foo_;
//   };
//
// You'll have to do this:
//
//   my_class.h
//   ----------
//   #include ".../immutable.h"
//
//   // Forward declaration.
//   class SomeOtherType;
//
//   class MyClass {
//     ...
//    private:
//     struct ImmutableSomeOtherTypeTraits {
//       // scoped_ptr<SomeOtherType> won't work here, either.
//       typedef SomeOtherType* Wrapper;
//
//       static void InitializeWrapper(Wrapper* wrapper);
//
//       static void DestroyWrapper(Wrapper* wrapper);
//       ...
//     };
//
//     typedef Immutable<SomeOtherType, ImmutableSomeOtherTypeTraits>
//         ImmutableSomeOtherType;
//
//     ImmutableSomeOtherType foo_;
//   };
//
//   my_class.cc
//   -----------
//   #include ".../some_other_type.h"
//
//   void MyClass::ImmutableSomeOtherTypeTraits::InitializeWrapper(
//       Wrapper* wrapper) {
//     *wrapper = new SomeOtherType();
//   }
//
//   void MyClass::ImmutableSomeOtherTypeTraits::DestroyWrapper(
//       Wrapper* wrapper) {
//     delete *wrapper;
//   }
//
//   ...
//
// Also note that this incurs an additional memory allocation when you
// create an Immutable<SomeOtherType>.

template <typename T>
struct DefaultImmutableTraits {
  typedef T Wrapper;

  static void InitializeWrapper(Wrapper* wrapper) {}

  static void DestroyWrapper(Wrapper* wrapper) {}

  static const T& Unwrap(const Wrapper& wrapper) { return wrapper; }

  static T* UnwrapMutable(Wrapper* wrapper) { return wrapper; }

  static void Swap(T* t1, T* t2) {
    // Uses ADL (see
    // http://en.wikipedia.org/wiki/Argument-dependent_name_lookup).
    using std::swap;
    swap(*t1, *t2);
  }
};

// Most STL containers have by-reference swap() member functions,
// although they usually already overload std::swap() to use those.
template <typename T>
struct HasSwapMemFnByRef : public DefaultImmutableTraits<T> {
  static void Swap(T* t1, T* t2) {
    t1->swap(*t2);
  }
};

// Most Google-style objects have by-pointer Swap() member functions
// (for example, generated protocol buffer classes).
template <typename T>
struct HasSwapMemFnByPtr : public DefaultImmutableTraits<T> {
  static void Swap(T* t1, T* t2) {
    t1->Swap(t2);
  }
};

template <typename T, typename Traits = DefaultImmutableTraits<T> >
class Immutable {
 public:
  // Puts the underlying object in a default-initialized state.
  Immutable() : core_(new internal::ImmutableCore<T, Traits>()) {}

  // Copy constructor and assignment welcome.

  // Resets |t| to a default-initialized state.
  explicit Immutable(T* t)
      : core_(new internal::ImmutableCore<T, Traits>(t)) {}

  const T& Get() const {
    return core_->Get();
  }

 private:
  scoped_refptr<const internal::ImmutableCore<T, Traits> > core_;
};

// Helper function to avoid having to write out template arguments.
template <typename T>
Immutable<T> MakeImmutable(T* t) {
  return Immutable<T>(t);
}

}  // namespace syncer

#endif  // SYNC_UTIL_IMMUTABLE_H_