v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
strong-alias.h
Go to the documentation of this file.
1// Copyright 2019 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Slightly adapted for inclusion in V8.
6// Copyright 2024 the V8 project authors. All rights reserved.
7
8#ifndef V8_BASE_STRONG_ALIAS_H_
9#define V8_BASE_STRONG_ALIAS_H_
10
11#include <functional>
12#include <ostream>
13#include <type_traits>
14#include <utility>
15
16namespace v8::base {
17
18// A type-safe alternative for a typedef or a 'using' directive.
19//
20// ---
21// This is a port of Chromium's base::StrongAlias, keeping the API and naming.
22// https://source.chromium.org/chromium/chromium/src/+/main:base/types/strong_alias.h;drc=0e7afdb6498599a66ec246045a9accf26da66a2b
23// ---
24//
25// C++ currently does not support type-safe typedefs, despite multiple proposals
26// (ex. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3515.pdf). The
27// next best thing is to try and emulate them in library code.
28//
29// The motivation is to disallow several classes of errors:
30//
31// using Orange = int;
32// using Apple = int;
33// Apple apple(2);
34// Orange orange = apple; // Orange should not be able to become an Apple.
35// Orange x = orange + apple; // Shouldn't add Oranges and Apples.
36// if (orange > apple); // Shouldn't compare Apples to Oranges.
37// void foo(Orange);
38// void foo(Apple); // Redefinition.
39// etc.
40//
41// StrongAlias may instead be used as follows:
42//
43// using Orange = StrongAlias<class OrangeTag, int>;
44// using Apple = StrongAlias<class AppleTag, int>;
45// using Banana = StrongAlias<class BananaTag, std::string>;
46// Apple apple(2);
47// Banana banana("Hello");
48// Orange orange = apple; // Does not compile.
49// Orange other_orange = orange; // Compiles, types match.
50// Orange x = orange + apple; // Does not compile.
51// Orange y = Orange(orange.value() + apple.value()); // Compiles.
52// Orange z = Orange(banana->size() + *other_orange); // Compiles.
53// if (orange > apple); // Does not compile.
54// if (orange > other_orange); // Compiles.
55// void foo(Orange);
56// void foo(Apple); // Compiles into separate overload.
57//
58// StrongAlias is a zero-cost abstraction, it's compiled away.
59//
60// TagType is an empty tag class (also called "phantom type") that only serves
61// the type system to differentiate between different instantiations of the
62// template.
63// UnderlyingType may be almost any value type. Note that some methods of the
64// StrongAlias may be unavailable (ie. produce elaborate compilation errors when
65// used) if UnderlyingType doesn't support them.
66//
67// StrongAlias only directly exposes comparison operators (for convenient use in
68// ordered containers). It's impossible, without reflection, to expose all
69// methods of the UnderlyingType in StrongAlias's interface. It's also
70// potentially unwanted (ex. you don't want to be able to add two StrongAliases
71// that represent socket handles). A getter and dereference operators are
72// provided in case you need to access the UnderlyingType.
73template <typename TagType, typename UnderlyingType>
75 public:
76 using underlying_type = UnderlyingType;
77
78 StrongAlias() = default;
79 constexpr explicit StrongAlias(const UnderlyingType& v) : value_(v) {}
80 constexpr explicit StrongAlias(UnderlyingType&& v) noexcept
81 : value_(std::move(v)) {}
82
83 constexpr UnderlyingType* operator->() { return &value_; }
84 constexpr const UnderlyingType* operator->() const { return &value_; }
85
86 constexpr UnderlyingType& operator*() & { return value_; }
87 constexpr const UnderlyingType& operator*() const& { return value_; }
88 constexpr UnderlyingType&& operator*() && { return std::move(value_); }
89 constexpr const UnderlyingType&& operator*() const&& {
90 return std::move(value_);
91 }
92
93 constexpr UnderlyingType& value() & { return value_; }
94 constexpr const UnderlyingType& value() const& { return value_; }
95 constexpr UnderlyingType&& value() && { return std::move(value_); }
96 constexpr const UnderlyingType&& value() const&& { return std::move(value_); }
97
98 constexpr explicit operator const UnderlyingType&() const& { return value_; }
99
100 // Comparison operators that default to the behavior of `UnderlyingType`.
101 // Note that if you wish to compare `StrongAlias<UnderlyingType>`, e.g.,
102 // by using `operator<` in a `std::set`, then `UnderlyingType` must
103 // implement `operator<=>`. If you cannot modify `UnderlyingType` (e.g.,
104 // because it is from an external library), then a work-around is to create a
105 // thin wrapper `W` around it, define `operator<=>` for the wrapper and create
106 // a `StrongAlias<W>`.
107 friend auto operator<=>(const StrongAlias& lhs,
108 const StrongAlias& rhs) = default;
109 friend bool operator==(const StrongAlias& lhs,
110 const StrongAlias& rhs) = default;
111
112 // Hasher to use in std::unordered_map, std::unordered_set, etc.
113 //
114 // Example usage:
115 // using MyType = base::StrongAlias<...>;
116 // using MySet = std::unordered_set<MyType, typename MyType::Hasher>;
117 struct Hasher {
119 using result_type = std::size_t;
121 return std::hash<UnderlyingType>()(id.value());
122 }
123 };
124
125 protected:
126 UnderlyingType value_;
127};
128
129// Stream operator for convenience, streams the UnderlyingType.
130template <typename TagType, typename UnderlyingType>
131 requires requires(std::ostream& stream, const UnderlyingType& value) {
132 stream << value;
133 }
134std::ostream& operator<<(std::ostream& stream,
136 return stream << alias.value();
137}
138
139} // namespace v8::base
140
141template <typename TagType, typename UnderlyingType>
142struct std::hash<v8::base::StrongAlias<TagType, UnderlyingType>> {
145 return std::hash<UnderlyingType>()(id.value());
146 }
147};
148
149#endif // V8_BASE_STRONG_ALIAS_H_
constexpr UnderlyingType & operator*() &
constexpr const UnderlyingType & operator*() const &
friend bool operator==(const StrongAlias &lhs, const StrongAlias &rhs)=default
friend auto operator<=>(const StrongAlias &lhs, const StrongAlias &rhs)=default
constexpr StrongAlias(UnderlyingType &&v) noexcept
UnderlyingType value_
constexpr const UnderlyingType && value() const &&
constexpr const UnderlyingType * operator->() const
constexpr StrongAlias(const UnderlyingType &v)
constexpr const UnderlyingType && operator*() const &&
UnderlyingType underlying_type
constexpr UnderlyingType && value() &&
constexpr UnderlyingType & value() &
constexpr const UnderlyingType & value() const &
constexpr UnderlyingType * operator->()
constexpr UnderlyingType && operator*() &&
std::ostream & operator<<(std::ostream &out, AddressRegion region)
size_t operator()(const v8::base::StrongAlias< TagType, UnderlyingType > &id) const
result_type operator()(const argument_type &id) const
std::unique_ptr< ValueMirror > value