123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- // Copyright 2019 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_TYPES_STRONG_ALIAS_H_
- #define BASE_TYPES_STRONG_ALIAS_H_
- #include <ostream>
- #include <type_traits>
- #include <utility>
- #include "base/template_util.h"
- #include "base/trace_event/base_tracing_forward.h"
- namespace base {
- // A type-safe alternative for a typedef or a 'using' directive.
- //
- // C++ currently does not support type-safe typedefs, despite multiple proposals
- // (ex. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3515.pdf). The
- // next best thing is to try and emulate them in library code.
- //
- // The motivation is to disallow several classes of errors:
- //
- // using Orange = int;
- // using Apple = int;
- // Apple apple(2);
- // Orange orange = apple; // Orange should not be able to become an Apple.
- // Orange x = orange + apple; // Shouldn't add Oranges and Apples.
- // if (orange > apple); // Shouldn't compare Apples to Oranges.
- // void foo(Orange);
- // void foo(Apple); // Redefinition.
- // etc.
- //
- // StrongAlias may instead be used as follows:
- //
- // using Orange = StrongAlias<class OrangeTag, int>;
- // using Apple = StrongAlias<class AppleTag, int>;
- // using Banana = StrongAlias<class BananaTag, std::string>;
- // Apple apple(2);
- // Banana banana("Hello");
- // Orange orange = apple; // Does not compile.
- // Orange other_orange = orange; // Compiles, types match.
- // Orange x = orange + apple; // Does not compile.
- // Orange y = Orange(orange.value() + apple.value()); // Compiles.
- // Orange z = Orange(banana->size() + *other_orange); // Compiles.
- // if (orange > apple); // Does not compile.
- // if (orange > other_orange); // Compiles.
- // void foo(Orange);
- // void foo(Apple); // Compiles into separate overload.
- //
- // StrongAlias is a zero-cost abstraction, it's compiled away.
- //
- // TagType is an empty tag class (also called "phantom type") that only serves
- // the type system to differentiate between different instantiations of the
- // template.
- // UnderlyingType may be almost any value type. Note that some methods of the
- // StrongAlias may be unavailable (ie. produce elaborate compilation errors when
- // used) if UnderlyingType doesn't support them.
- //
- // StrongAlias only directly exposes comparison operators (for convenient use in
- // ordered containers) and a Hasher struct (for unordered_map/set). It's
- // impossible, without reflection, to expose all methods of the UnderlyingType
- // in StrongAlias's interface. It's also potentially unwanted (ex. you don't
- // want to be able to add two StrongAliases that represent socket handles).
- // A getter and dereference operators are provided in case you need to access
- // the UnderlyingType.
- //
- // See also
- // - //styleguide/c++/blink-c++.md which provides recommendation and examples of
- // using StrongAlias<Tag, bool> instead of a bare bool.
- // - IdType<...> which provides helpers for specializing StrongAlias to be
- // used as an id.
- // - TokenType<...> which provides helpers for specializing StrongAlias to be
- // used as a wrapper of base::UnguessableToken.
- template <typename TagType, typename UnderlyingType>
- class StrongAlias {
- public:
- constexpr StrongAlias() = default;
- constexpr explicit StrongAlias(const UnderlyingType& v) : value_(v) {}
- constexpr explicit StrongAlias(UnderlyingType&& v) noexcept
- : value_(std::move(v)) {}
- constexpr UnderlyingType* operator->() { return &value_; }
- constexpr const UnderlyingType* operator->() const { return &value_; }
- constexpr UnderlyingType& operator*() & { return value_; }
- constexpr const UnderlyingType& operator*() const& { return value_; }
- constexpr UnderlyingType&& operator*() && { return std::move(value_); }
- constexpr const UnderlyingType&& operator*() const&& {
- return std::move(value_);
- }
- constexpr UnderlyingType& value() & { return value_; }
- constexpr const UnderlyingType& value() const& { return value_; }
- constexpr UnderlyingType&& value() && { return std::move(value_); }
- constexpr const UnderlyingType&& value() const&& { return std::move(value_); }
- constexpr explicit operator const UnderlyingType&() const& { return value_; }
- constexpr bool operator==(const StrongAlias& other) const {
- return value_ == other.value_;
- }
- constexpr bool operator!=(const StrongAlias& other) const {
- return value_ != other.value_;
- }
- constexpr bool operator<(const StrongAlias& other) const {
- return value_ < other.value_;
- }
- constexpr bool operator<=(const StrongAlias& other) const {
- return value_ <= other.value_;
- }
- constexpr bool operator>(const StrongAlias& other) const {
- return value_ > other.value_;
- }
- constexpr bool operator>=(const StrongAlias& other) const {
- return value_ >= other.value_;
- }
- // Hasher to use in std::unordered_map, std::unordered_set, etc.
- //
- // Example usage:
- // using MyType = base::StrongAlias<...>;
- // using MySet = std::unordered_set<MyType, typename MyType::Hasher>;
- //
- // https://google.github.io/styleguide/cppguide.html#std_hash asks to avoid
- // defining specializations of `std::hash` - this is why the hasher needs to
- // be explicitly specified and why the following code will *not* work:
- // using MyType = base::StrongAlias<...>;
- // using MySet = std::unordered_set<MyType>; // This won't work.
- struct Hasher {
- using argument_type = StrongAlias;
- using result_type = std::size_t;
- result_type operator()(const argument_type& id) const {
- return std::hash<UnderlyingType>()(id.value());
- }
- };
- // If UnderlyingType can be serialised into trace, its alias is also
- // serialisable.
- template <class U = UnderlyingType>
- typename perfetto::check_traced_value_support<U>::type WriteIntoTrace(
- perfetto::TracedValue&& context) const {
- perfetto::WriteIntoTracedValue(std::move(context), value_);
- }
- protected:
- UnderlyingType value_;
- };
- // Stream operator for convenience, streams the UnderlyingType.
- template <typename TagType,
- typename UnderlyingType,
- typename = std::enable_if_t<
- base::internal::SupportsOstreamOperator<UnderlyingType>::value>>
- std::ostream& operator<<(std::ostream& stream,
- const StrongAlias<TagType, UnderlyingType>& alias) {
- return stream << alias.value();
- }
- } // namespace base
- #endif // BASE_TYPES_STRONG_ALIAS_H_
|