unique_object.h 4.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
// Copyright 2018 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_FML_UNIQUE_OBJECT_H_
#define FLUTTER_FML_UNIQUE_OBJECT_H_

#include <utility>

#include "lib/fxl/compiler_specific.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/macros.h"

namespace fml {

// struct UniqueFooTraits {
//   // This function should be fast an inline.
//   static int InvalidValue() { return 0; }
//
//   // This function should be fast an inline.
//   static bool IsValid(const T& value) { return value != InvalidValue(); }
//
//   // This free function will not be called if f == InvalidValue()!
//   static void Free(int f) { ::FreeFoo(f); }
// };

template <typename T, typename Traits>
class UniqueObject {
 private:
  // This must be first since it's used inline below.
  //
  // Use the empty base class optimization to allow us to have a Traits
  // member, while avoiding any space overhead for it when Traits is an
  // empty class.  See e.g. http://www.cantrip.org/emptyopt.html for a good
  // discussion of this technique.
  struct Data : public Traits {
    explicit Data(const T& in) : generic(in) {}
    Data(const T& in, const Traits& other) : Traits(other), generic(in) {}

    T generic;
  };

 public:
  using element_type = T;
  using traits_type = Traits;

  UniqueObject() : data_(Traits::InvalidValue()) {}
  explicit UniqueObject(const T& value) : data_(value) {}

  UniqueObject(const T& value, const Traits& traits) : data_(value, traits) {}

  UniqueObject(UniqueObject&& other)
      : data_(other.release(), other.get_traits()) {}

  ~UniqueObject() { FreeIfNecessary(); }

  UniqueObject& operator=(UniqueObject&& other) {
    reset(other.release());
    return *this;
  }

  void reset(const T& value = Traits::InvalidValue()) {
    FXL_CHECK(data_.generic == Traits::InvalidValue() ||
              data_.generic != value);
    FreeIfNecessary();
    data_.generic = value;
  }

  void swap(UniqueObject& other) {
    // Standard swap idiom: 'using std::swap' ensures that std::swap is
    // present in the overload set, but we call swap unqualified so that
    // any more-specific overloads can be used, if available.
    using std::swap;
    swap(static_cast<Traits&>(data_), static_cast<Traits&>(other.data_));
    swap(data_.generic, other.data_.generic);
  }

  // Release the object. The return value is the current object held by this
  // object. After this operation, this object will hold an invalid value, and
  // will not own the object any more.
  T release() FXL_WARN_UNUSED_RESULT {
    T old_generic = data_.generic;
    data_.generic = Traits::InvalidValue();
    return old_generic;
  }

  const T& get() const { return data_.generic; }

  bool is_valid() const { return Traits::IsValid(data_.generic); }

  bool operator==(const T& value) const { return data_.generic == value; }

  bool operator!=(const T& value) const { return data_.generic != value; }

  Traits& get_traits() { return data_; }
  const Traits& get_traits() const { return data_; }

 private:
  void FreeIfNecessary() {
    if (data_.generic != Traits::InvalidValue()) {
      data_.Free(data_.generic);
      data_.generic = Traits::InvalidValue();
    }
  }

  // Forbid comparison. If U != T, it totally doesn't make sense, and if U ==
  // T, it still doesn't make sense because you should never have the same
  // object owned by two different UniqueObject.
  template <typename T2, typename Traits2>
  bool operator==(const UniqueObject<T2, Traits2>& p2) const = delete;

  template <typename T2, typename Traits2>
  bool operator!=(const UniqueObject<T2, Traits2>& p2) const = delete;

  Data data_;

  FXL_DISALLOW_COPY_AND_ASSIGN(UniqueObject);
};

template <class T, class Traits>
void swap(const UniqueObject<T, Traits>& a, const UniqueObject<T, Traits>& b) {
  a.swap(b);
}

template <class T, class Traits>
bool operator==(const T& value, const UniqueObject<T, Traits>& object) {
  return value == object.get();
}

template <class T, class Traits>
bool operator!=(const T& value, const UniqueObject<T, Traits>& object) {
  return !(value == object.get());
}

}  // namespace fml

#endif  // FLUTTER_FML_UNIQUE_OBJECT_H_