| Index: base/id_type.h
|
| diff --git a/base/id_type.h b/base/id_type.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0c97b13b8a7b7eee26c3e160bacdb3668e28b98c
|
| --- /dev/null
|
| +++ b/base/id_type.h
|
| @@ -0,0 +1,144 @@
|
| +// Copyright 2015 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.
|
| +
|
| +#ifndef BASE_ID_TYPE_H_
|
| +#define BASE_ID_TYPE_H_
|
| +
|
| +#include <ostream>
|
| +
|
| +#include "base/containers/hash_tables.h"
|
| +
|
| +// Problem:
|
| +//
|
| +// void f(int foo_id, int bar_id);
|
| +// ...
|
| +// // Arguments passed in a wrong order:
|
| +// f(bar_id, foo_id); // Compiles - sigh... :-(
|
| +//
|
| +// Solution:
|
| +//
|
| +// using FooId = base::IdType<Foo, uint64_t, 0>;
|
| +// using BarId = base::IdType<Bar, uint64_t, 0>;
|
| +// void f(FooId foo_id, BarId bar_id);
|
| +// ...
|
| +// // Arguments passed in a wrong order:
|
| +// f(bar_id, foo_id); // Doesn't compile anymore - yay! :-)
|
| +//
|
| +// Typically FooId would be declared like this:
|
| +//
|
| +// foo_id.h:
|
| +//
|
| +// #include "base/id_type.h"
|
| +//
|
| +// namespace foo_namespace {
|
| +//
|
| +// class Foo;
|
| +// using FooId = base::IdType<Foo, uint64_t, 0>;
|
| +//
|
| +// } // namespace foo_namespace
|
| +//
|
| +// For most practical purposes, FooId declared like above behaves just like
|
| +// an uint64_t, except that:
|
| +//
|
| +// 1. FooId only supports a subset of uint64_t operations:
|
| +// - ==, !=, <, hashing
|
| +// - stream output
|
| +// - copy construction and assignment
|
| +//
|
| +// 2. FooId does not implicitly coerce to/from other uint64_t values and
|
| +// to/from base::IdType<SomeOtherType, ...>. Explicit coercion to/from
|
| +// uint64_t is possible through the following FooId methods:
|
| +// - static FooId FromUnsafeValue(uint64_t)
|
| +// - uint64_t GetUnsafeValue()
|
| +//
|
| +// 3. Default-constructed FooId contains a "null" / "invalid" value specified
|
| +// by the 3rd argument of the IdType<...> template. Testing against this
|
| +// value can be done by the following FooId methods:
|
| +// - bool is_valid()
|
| +// - bool is_null()
|
| +//
|
| +// 4. The presence of a custom default constructor means that FooId is not a
|
| +// "trivial" class and therefore is not a POD type (unlike an uint64_t).
|
| +// At the same time FooId has almost all of the properties of a POD type:
|
| +// - is "trivially copyable" (i.e. is memcpy-able),
|
| +// - has "standard layout" (i.e. interops with things expecting C layout).
|
| +// See http://stackoverflow.com/a/7189821 for more info about these
|
| +// concepts.
|
| +
|
| +namespace base {
|
| +
|
| +template <typename TypeBeingIdentified,
|
| + typename WrappedType,
|
| + WrappedType kInvalidValue>
|
| +struct IdType {
|
| + public:
|
| + IdType() : value_(kInvalidValue) {}
|
| + bool is_valid() const { return value_ != kInvalidValue; }
|
| + bool is_null() const { return value_ == kInvalidValue; }
|
| +
|
| + static IdType FromUnsafeValue(WrappedType value) { return IdType(value); }
|
| + WrappedType GetUnsafeValue() const { return value_; }
|
| +
|
| + IdType(const IdType& other) = default;
|
| + IdType& operator=(const IdType& other) = default;
|
| +
|
| + bool operator==(const IdType& other) const { return value_ == other.value_; }
|
| + bool operator!=(const IdType& other) const { return value_ != other.value_; }
|
| + bool operator<(const IdType& other) const { return value_ < other.value_; }
|
| +
|
| + private:
|
| + explicit IdType(WrappedType val) : value_(val) {}
|
| +
|
| + WrappedType value_;
|
| +};
|
| +
|
| +template <typename TypeBeingIdentified,
|
| + typename WrappedType,
|
| + WrappedType kInvalidValue>
|
| +std::ostream& operator<<(
|
| + std::ostream& stream,
|
| + const IdType<TypeBeingIdentified, WrappedType, kInvalidValue>& id) {
|
| + return stream << id.GetUnsafeValue();
|
| +}
|
| +
|
| +} // namespace base
|
| +
|
| +namespace BASE_HASH_NAMESPACE {
|
| +
|
| +template <typename TypeBeingIdentified,
|
| + typename WrappedType,
|
| + WrappedType kInvalidValue>
|
| +struct hash<base::IdType<TypeBeingIdentified, WrappedType, kInvalidValue>> {
|
| + std::size_t operator()(
|
| + const base::IdType<TypeBeingIdentified, WrappedType, kInvalidValue>& id)
|
| + const {
|
| + return BASE_HASH_NAMESPACE::hash<WrappedType>()(id.GetUnsafeValue());
|
| + }
|
| +};
|
| +
|
| +} // namespace BASE_HASH_NAMESPACE
|
| +
|
| +// If defined(COMPILER_MSVC) then BASE_HASH_NAMESPACE == std.
|
| +// In this case we need to avoid defininig std::hash<...> second time
|
| +// (it has already been defined above).
|
| +#if !defined(COMPILER_MSVC)
|
| +
|
| +namespace std {
|
| +
|
| +template <typename TypeBeingIdentified,
|
| + typename WrappedType,
|
| + WrappedType kInvalidValue>
|
| +struct hash<base::IdType<TypeBeingIdentified, WrappedType, kInvalidValue>> {
|
| + size_t operator()(
|
| + const base::IdType<TypeBeingIdentified, WrappedType, kInvalidValue>& id)
|
| + const {
|
| + return std::hash<WrappedType>()(id.GetUnsafeValue());
|
| + }
|
| +};
|
| +
|
| +} // namespace std;
|
| +
|
| +#endif // !defined(COMPILER_MSVC)
|
| +
|
| +#endif // BASE_ID_TYPE_H_
|
|
|