1
#pragma once
2

            
3
#include "absl/types/optional.h" // required for absl::nullopt
4

            
5
namespace Envoy {
6

            
7
// Helper class to make it easier to work with optional references, allowing:
8
//   foo(OptRef<T> t) {
9
//     if (t.has_value()) {
10
//       t->method();
11
//     }
12
//   }
13
//
14
// Using absl::optional directly you must write optref.value().method() which is
15
// a bit more awkward.
16
//
17
// This class also consumes less memory -- e.g. 8 bytes for a pointer rather
18
// than 16 bytes for a pointer plus a bool with alignment padding.
19
template <class T> struct OptRef {
20
33982475
  OptRef(T& t) : ptr_(&t) {}
21
32698579
  OptRef() : ptr_(nullptr) {}
22
584231
  OptRef(absl::nullopt_t) : ptr_(nullptr) {}
23

            
24
  /**
25
   * Copy constructor that allows conversion.
26
   */
27
211607
  template <class From> explicit OptRef(OptRef<From> rhs) : ptr_(rhs.ptr()) {}
28

            
29
  /**
30
   * Assignment that allows conversion.
31
   */
32
1
  template <class From> OptRef<T>& operator=(OptRef<From> rhs) {
33
1
    ptr_ = rhs.ptr();
34
1
    return *this;
35
1
  }
36

            
37
  /**
38
   * Cast operator to extract a ref to U from this, assuming T*
39
   * is assignable to U*. For example, if U* is const T, or t is derived
40
   * from U.
41
   *
42
   * @return a ref to the converted object.
43
   */
44
512416
  template <class U> operator OptRef<U>() {
45
512416
    return ptr_ == nullptr ? absl::nullopt : OptRef<U>(*ptr_);
46
512416
  }
47

            
48
  /**
49
   * Helper to call a const method on T. The caller is responsible for ensuring
50
   * has_value() is true.
51
   */
52
9008086
  T* operator->() const { return ptr_; }
53

            
54
  /**
55
   * Helper to convert a OptRef into a pointer. If the optional is not set, returns a nullptr.
56
   */
57
977580
  T* ptr() const { return ptr_; }
58

            
59
  /**
60
   * Helper to convert a OptRef into a ref. Behavior if !has_value() is undefined.
61
   */
62
160453
  T& ref() const { return *ptr_; } // NOLINT(clang-analyzer-core.uninitialized.UndefReturn)
63

            
64
  /**
65
   * Helper to dereference an OptRef. Behavior if !has_value() is undefined.
66
   */
67
6010106
  T& operator*() const { return *ptr_; } // NOLINT(clang-analyzer-core.uninitialized.UndefReturn)
68

            
69
  /**
70
   * @return true if the object has a value.
71
   */
72
60457285
  bool has_value() const { return ptr_ != nullptr; }
73

            
74
21826860
  T& value_or(T& other) const { return has_value() ? *ptr_ : other; }
75

            
76
  /**
77
   * @return true if the object has a value.
78
   */
79
99886
  bool operator!() const { return ptr_ == nullptr; }
80
1342327
  operator bool() const { return ptr_ != nullptr; }
81

            
82
  /**
83
   * Copies the OptRef into an optional<T>. To use this, T must supply
84
   * an assignment operator.
85
   *
86
   * It is OK to copy an unset object -- it will result in an optional where
87
   * has_value() is false.
88
   *
89
   * @return an optional copy of the referenced object (or nullopt).
90
   */
91
252
  absl::optional<T> copy() const {
92
252
    absl::optional<T> ret;
93
252
    if (has_value()) {
94
111
      ret = *ptr_;
95
111
    }
96
252
    return ret;
97
252
  }
98

            
99
  /**
100
   * Places a reference to the an object into the OptRef.
101
   *
102
   * @param ref the object to be referenced.
103
   */
104
108
  void emplace(T& ref) { ptr_ = &ref; }
105

            
106
  /**
107
   * The value method has no intrinsic value to OptRef, but is left here for
108
   * compatibility reasons. This is used in call-sites which would be needed if
109
   * they were using optional<reference_wrapper<T>> directly without
110
   * OptRef. Having this function makes it easier upgrade to using OptRef
111
   * without having to change all call-sites.
112
   *
113
   * This must be called with has_value() true.
114
   *
115
   * @return a reference_wrapper around the value.
116
   */
117
2
  std::reference_wrapper<const T> value() const { return std::reference_wrapper<T>(*ptr_); }
118
1315732
  std::reference_wrapper<T> value() { return std::reference_wrapper<T>(*ptr_); }
119

            
120
  /**
121
   * Clears any current reference.
122
   */
123
9237
  void reset() { ptr_ = nullptr; }
124

            
125
private:
126
  T* ptr_;
127
};
128

            
129
/**
130
 * Constructs an OptRef<T> from the provided reference.
131
 * @param ref the reference to wrap
132
 * @return OptRef<T> the wrapped reference
133
 */
134
97007
template <class T> OptRef<T> makeOptRef(T& ref) { return {ref}; }
135

            
136
/**
137
 * Constructs an OptRef<T> from the provided pointer.
138
 * @param ptr the pointer to wrap
139
 * @return OptRef<T> the wrapped pointer, or absl::nullopt if the pointer is nullptr
140
 */
141
31632096
template <class T> OptRef<T> makeOptRefFromPtr(T* ptr) {
142
31632096
  if (ptr == nullptr) {
143
29779138
    return {};
144
29779138
  }
145

            
146
1852958
  return {*ptr};
147
31632096
}
148

            
149
// Overloads for comparing OptRef against absl::nullopt.
150
5931
template <class T> bool operator!=(const OptRef<T>& optref, absl::nullopt_t) {
151
5931
  return optref.has_value();
152
5931
}
153
85
template <class T> bool operator!=(absl::nullopt_t, const OptRef<T>& optref) {
154
85
  return optref.has_value();
155
85
}
156
98
template <class T> bool operator==(const OptRef<T>& optref, absl::nullopt_t) {
157
98
  return !optref.has_value();
158
98
}
159
45
template <class T> bool operator==(absl::nullopt_t, const OptRef<T>& optref) {
160
45
  return !optref.has_value();
161
45
}
162

            
163
} // namespace Envoy