blob: b443e294a0caea21689ff1f389fd0000a347be24 [file] [log] [blame]
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +00001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5// Typed strings.
6
7// Example usage:
8//
9// CRUBIT_DEFINE_STRING_TYPE(Foo);
10// CRUBIT_DEFINE_STRING_TYPE(Bar);
11// Foo foo("foo_value");
12// Bar bar("bar_value");
13//
14// The following two statements will not compile.
15//
16// foo = bar;
17//
18// if (foo == bar) { } else { }; ...
19//
20// The strongly-typed types are hashable with the Abseil hashing framework,
21// so they work out of the box with any modern hashing system. For non-Abseil
22// uses, the explicit functors absl::Hash<Foo>, absl::Hash<Bar> may be used:
23//
24// std::unordered_set<Foo, absl::Hash<Foo>>
25// __gnu_cxx::hash_map<Bar, int, absl::Hash<Bar>>
26//
27// (But absl::flat_hash_set<Foo> is much better!)
28
Marco Polettic61bcc42022-04-08 12:54:30 -070029#ifndef CRUBIT_COMMON_STRING_TYPE_H_
30#define CRUBIT_COMMON_STRING_TYPE_H_
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000031
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000032#include <ostream> // NOLINT
33#include <string>
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000034#include <utility>
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000035
Lukasz Anforowiczcec7a8a2022-04-27 10:24:51 -070036#include "absl/flags/marshalling.h"
Lukasz Anforowiczcec7a8a2022-04-27 10:24:51 -070037#include "absl/strings/string_view.h"
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000038
39// Defines the StringType using StringTypeRepresentation and provides a type
40// alias to string_type_name. The struct string_type_name ## _tag_ trickery is
41// needed to ensure that a new type is created per string_type_name.
42//
43// StringTypeRepresentation classes, as a rule, should either *be* a string-like
44// object, or should provide a "value()" method that returns a string-like
45// object. If they can provide more optimal implementations of relational
46// operators, they should define operator== and operator<; all other relational
47// operators are defined in terms of those. If they can provide more optimal
48// implementations of AbslHashValue or operator<<, they should provide those as
49// well.
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000050#define CRUBIT_DEFINE_STRING_TYPE(string_type_name) \
Marcel Hlopkof15e8ce2022-04-08 08:46:09 -070051 using string_type_name = ::crubit::StringType<class string_type_name##_tag_>;
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000052
Marcel Hlopkof15e8ce2022-04-08 08:46:09 -070053namespace crubit {
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000054
55// StringType provides these operations:
56// * relational operators (==, !=, <, <=, >, >=)
57// * compare (future <=> operator)
58// * AbslHashValue
59// * streaming with operator<<
60// * value(), which should return a string-like object (const string&,
61// absl::string_view, ShortString<N>, etc.)
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000062template <typename Tag>
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000063class StringType {
64 public:
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000065 StringType() = default;
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000066 explicit StringType(std::string value) : s_(std::move(value)) {}
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000067
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000068 const std::string& value() const { return s_; }
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000069
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000070 bool empty() const { return value().empty(); }
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000071
72 // If you want to optimize your relational methods, you need only implement
73 // these three: compare, operator==, and operator<.
74 int compare(const StringType& other) const {
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000075 return value().compare(other.value());
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000076 }
77 friend bool operator==(const StringType& left, const StringType& right) {
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000078 return left.value() == right.value();
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000079 }
80 friend bool operator<(const StringType& left, const StringType& right) {
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000081 return left.value() < right.value();
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000082 }
83
84 // These methods are defined in terms of the above.
85 friend bool operator!=(const StringType& left, const StringType& right) {
86 return !(left == right);
87 }
88 friend bool operator>(const StringType& left, const StringType& right) {
89 return right < left;
90 }
91 friend bool operator<=(const StringType& left, const StringType& right) {
92 return !(left > right);
93 }
94 friend bool operator>=(const StringType& left, const StringType& right) {
95 return !(left < right);
96 }
97
98 template <typename H>
99 friend H AbslHashValue(H h, const StringType& s) {
Devin Jeanpierre8a261722023-01-12 14:04:05 -0800100 return H::combine(std::move(h), s.value());
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000101 }
102
103 friend std::ostream& operator<<(std::ostream& os, const StringType& s) {
Lukasz Anforowicz1df56722022-03-17 18:30:19 +0000104 return os << s.value();
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000105 }
106
107 private:
Lukasz Anforowicz1df56722022-03-17 18:30:19 +0000108 std::string s_;
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000109};
110
111// Allows typed strings to be used as ABSL_FLAG values.
112//
113// This is equivalent in behavior to just using a raw std::string.
Lukasz Anforowicz1df56722022-03-17 18:30:19 +0000114template <typename Tag>
115bool AbslParseFlag(absl::string_view text, StringType<Tag>* out,
116 std::string* error) {
117 *out = StringType<Tag>(text);
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000118 return true;
119}
120
Lukasz Anforowicz1df56722022-03-17 18:30:19 +0000121template <typename Tag>
122std::string AbslUnparseFlag(const StringType<Tag>& val) {
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000123 return absl::UnparseFlag(std::string(val.value()));
124}
125
Marcel Hlopkof15e8ce2022-04-08 08:46:09 -0700126} // namespace crubit
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000127
Marco Polettic61bcc42022-04-08 12:54:30 -0700128#endif // CRUBIT_COMMON_STRING_TYPE_H_