blob: c9c2e506745d7d085818ae34080af7a0f6fc0f5f [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
32#include <memory>
33#include <ostream> // NOLINT
34#include <string>
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000035#include <utility>
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000036
Lukasz Anforowiczcec7a8a2022-04-27 10:24:51 -070037#include "absl/container/flat_hash_set.h"
38#include "absl/flags/marshalling.h"
39#include "absl/meta/type_traits.h"
40#include "absl/strings/string_view.h"
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000041
42// Defines the StringType using StringTypeRepresentation and provides a type
43// alias to string_type_name. The struct string_type_name ## _tag_ trickery is
44// needed to ensure that a new type is created per string_type_name.
45//
46// StringTypeRepresentation classes, as a rule, should either *be* a string-like
47// object, or should provide a "value()" method that returns a string-like
48// object. If they can provide more optimal implementations of relational
49// operators, they should define operator== and operator<; all other relational
50// operators are defined in terms of those. If they can provide more optimal
51// implementations of AbslHashValue or operator<<, they should provide those as
52// well.
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000053#define CRUBIT_DEFINE_STRING_TYPE(string_type_name) \
Marcel Hlopkof15e8ce2022-04-08 08:46:09 -070054 using string_type_name = ::crubit::StringType<class string_type_name##_tag_>;
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000055
Marcel Hlopkof15e8ce2022-04-08 08:46:09 -070056namespace crubit {
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000057
58// StringType provides these operations:
59// * relational operators (==, !=, <, <=, >, >=)
60// * compare (future <=> operator)
61// * AbslHashValue
62// * streaming with operator<<
63// * value(), which should return a string-like object (const string&,
64// absl::string_view, ShortString<N>, etc.)
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000065template <typename Tag>
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000066class StringType {
67 public:
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000068 StringType() = default;
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000069 explicit StringType(std::string value) : s_(std::move(value)) {}
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000070
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000071 const std::string& value() const { return s_; }
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000072
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000073 bool empty() const { return value().empty(); }
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000074
75 // If you want to optimize your relational methods, you need only implement
76 // these three: compare, operator==, and operator<.
77 int compare(const StringType& other) const {
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000078 return value().compare(other.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 friend bool operator<(const StringType& left, const StringType& right) {
Lukasz Anforowicz1df56722022-03-17 18:30:19 +000084 return left.value() < right.value();
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +000085 }
86
87 // These methods are defined in terms of the above.
88 friend bool operator!=(const StringType& left, const StringType& right) {
89 return !(left == right);
90 }
91 friend bool operator>(const StringType& left, const StringType& right) {
92 return right < left;
93 }
94 friend bool operator<=(const StringType& left, const StringType& right) {
95 return !(left > right);
96 }
97 friend bool operator>=(const StringType& left, const StringType& right) {
98 return !(left < right);
99 }
100
101 template <typename H>
102 friend H AbslHashValue(H h, const StringType& s) {
Devin Jeanpierre8a261722023-01-12 14:04:05 -0800103 return H::combine(std::move(h), s.value());
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000104 }
105
106 friend std::ostream& operator<<(std::ostream& os, const StringType& s) {
Lukasz Anforowicz1df56722022-03-17 18:30:19 +0000107 return os << s.value();
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000108 }
109
110 private:
Lukasz Anforowicz1df56722022-03-17 18:30:19 +0000111 std::string s_;
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000112};
113
114// Allows typed strings to be used as ABSL_FLAG values.
115//
116// This is equivalent in behavior to just using a raw std::string.
Lukasz Anforowicz1df56722022-03-17 18:30:19 +0000117template <typename Tag>
118bool AbslParseFlag(absl::string_view text, StringType<Tag>* out,
119 std::string* error) {
120 *out = StringType<Tag>(text);
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000121 return true;
122}
123
Lukasz Anforowicz1df56722022-03-17 18:30:19 +0000124template <typename Tag>
125std::string AbslUnparseFlag(const StringType<Tag>& val) {
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000126 return absl::UnparseFlag(std::string(val.value()));
127}
128
Marcel Hlopkof15e8ce2022-04-08 08:46:09 -0700129} // namespace crubit
Lukasz Anforowicze0d6b852022-03-17 15:40:38 +0000130
Marco Polettic61bcc42022-04-08 12:54:30 -0700131#endif // CRUBIT_COMMON_STRING_TYPE_H_