Google Cloud C++ Client  2.2.1
C++ Client Library for Google Cloud Platform
options.h
Go to the documentation of this file.
1 // Copyright 2021 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_OPTIONS_H
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_OPTIONS_H
17 
18 #include "google/cloud/internal/type_list.h"
19 #include "google/cloud/version.h"
20 #include "absl/base/attributes.h"
21 #include "absl/memory/memory.h"
22 #include <set>
23 #include <typeindex>
24 #include <typeinfo>
25 #include <unordered_map>
26 
27 namespace google {
28 namespace cloud {
30 
31 class Options;
32 namespace internal {
33 Options MergeOptions(Options, Options);
34 void CheckExpectedOptionsImpl(std::set<std::type_index> const&, Options const&,
35  char const*);
36 // TODO(#8800) - Remove when bigtable::Table no longer uses bigtable::DataClient
37 bool IsEmpty(Options const&);
38 template <typename T>
39 inline T const& DefaultValue() {
40  static auto const* const kDefaultValue = new T{};
41  return *kDefaultValue;
42 }
43 } // namespace internal
44 
45 /**
46  * A class that holds option structs indexed by their type.
47  *
48  * An "Option" is any struct that has a public `Type` member typedef. By
49  * convention they are named like "FooOption". Each library (e.g., spanner,
50  * storage) may define their own set of options. Additionally, there are common
51  * options defined that many libraries may use. All these options may be set in
52  * a single `Options` instance, and each library will look at the options that
53  * it needs.
54  *
55  * Here's an overview of this class's interface, but see the method
56  * documentation below for details.
57  *
58  * - `.set<T>(x)` -- Sets the option `T` to value `x`
59  * - `.has<T>()` -- Returns true iff option `T` is set
60  * - `.unset<T>()` -- Removes the option `T`
61  * - `.get<T>()` -- Gets a const-ref to the value of option `T`
62  * - `.lookup<T>(x)` -- Gets a non-const-ref to option `T`'s value, initializing
63  * it to `x` if it was not set (`x` is optional).
64  *
65  * @par Example:
66  *
67  * @code
68  * struct FooOption {
69  * using Type = int;
70  * };
71  * struct BarOption {
72  * using Type = std::set<std::string>;
73  * };
74  * ...
75  * Options opts;
76  *
77  * assert(opts.get<FooOption>() == 0);
78  * opts.set<FooOption>(42);
79  * assert(opts.get<FooOption>() == 42);
80  *
81  * // Inserts two elements directly into the BarOption's std::set.
82  * opts.lookup<BarOption>().insert("hello");
83  * opts.lookup<BarOption>().insert("world");
84  *
85  * std::set<std::string> const& bar = opts.get<BarOption>();
86  * assert(bar == std::set<std::string>{"hello", "world"});
87  * @endcode
88  */
89 class Options {
90  private:
91  template <typename T>
92  using ValueTypeT = typename T::Type;
93 
94  public:
95  /// Constructs an empty instance.
96  Options() = default;
97 
98  Options(Options const& rhs) {
99  for (auto const& kv : rhs.m_) m_.emplace(kv.first, kv.second->clone());
100  }
101  Options& operator=(Options const& rhs) {
102  Options tmp(rhs);
103  std::swap(m_, tmp.m_);
104  return *this;
105  }
106  Options(Options&&) = default;
107  Options& operator=(Options&&) = default;
108 
109  /**
110  * Sets option `T` to the value @p v and returns a reference to `*this`.
111  *
112  * @code
113  * struct FooOption {
114  * using Type = int;
115  * };
116  * auto opts = Options{}.set<FooOption>(123);
117  * @endcode
118  *
119  * @tparam T the option type
120  * @param v the value to set the option T
121  */
122  template <typename T>
123  Options& set(ValueTypeT<T> v) {
124  m_[typeid(T)] = absl::make_unique<Data<T>>(std::move(v));
125  return *this;
126  }
127 
128  /**
129  * Returns true IFF an option with type `T` exists.
130  *
131  * @tparam T the option type
132  */
133  template <typename T>
134  bool has() const {
135  return m_.find(typeid(T)) != m_.end();
136  }
137 
138  /**
139  * Erases the option specified by the type `T`.
140  *
141  * @tparam T the option type
142  */
143  template <typename T>
144  void unset() {
145  m_.erase(typeid(T));
146  }
147 
148  /**
149  * Returns a reference to the value for `T`, or a value-initialized default
150  * if `T` was not set.
151  *
152  * This method will always return a reference to a valid value of the correct
153  * type for option `T`, whether or not `T` has actually been set. Use
154  * `has<T>()` to check whether or not the option has been set.
155  *
156  * @code
157  * struct FooOption {
158  * using Type = std::set<std::string>;
159  * };
160  * Options opts;
161  * std::set<std::string> const& x = opts.get<FooOption>();
162  * assert(x.empty());
163  * assert(!x.has<FooOption>());
164  *
165  * opts.set<FooOption>({"foo"});
166  * assert(opts.get<FooOption>().size() == 1);
167  * @endcode
168  *
169  * @tparam T the option type
170  */
171  template <typename T>
172  ValueTypeT<T> const& get() const {
173  auto const it = m_.find(typeid(T));
174  if (it == m_.end()) return internal::DefaultValue<ValueTypeT<T>>();
175  auto const* value = it->second->data_address();
176  return *reinterpret_cast<ValueTypeT<T> const*>(value);
177  }
178 
179  /**
180  * Returns a reference to the value for option `T`, setting the value to @p
181  * init_value if necessary.
182  *
183  * @code
184  * struct BigOption {
185  * using Type = std::set<std::string>;
186  * };
187  * Options opts;
188  * std::set<std::string>& x = opts.lookup<BigOption>();
189  * assert(x.empty());
190  *
191  * x.insert("foo");
192  * opts.lookup<BigOption>().insert("bar");
193  * assert(x.size() == 2);
194  * @endcode
195  *
196  * @tparam T the option type
197  * @param value the initial value to use if `T` is not set (optional)
198  */
199  template <typename T>
200  ValueTypeT<T>& lookup(ValueTypeT<T> value = {}) {
201  auto p = m_.find(typeid(T));
202  if (p == m_.end()) {
203  p = m_.emplace(typeid(T), absl::make_unique<Data<T>>(std::move(value)))
204  .first;
205  }
206  auto* v = p->second->data_address();
207  return *reinterpret_cast<ValueTypeT<T>*>(v);
208  }
209 
210  private:
211  friend Options internal::MergeOptions(Options, Options);
212  friend void internal::CheckExpectedOptionsImpl(
213  std::set<std::type_index> const&, Options const&, char const*);
214  friend bool internal::IsEmpty(Options const&);
215 
216  // The type-erased data holder of all the option values.
217  class DataHolder {
218  public:
219  virtual ~DataHolder() = default;
220  virtual void const* data_address() const = 0;
221  virtual void* data_address() = 0;
222  virtual std::unique_ptr<DataHolder> clone() const = 0;
223  };
224 
225  // The data holder for all the option values.
226  template <typename T>
227  class Data : public DataHolder {
228  public:
229  explicit Data(ValueTypeT<T> v) : value_(std::move(v)) {}
230  ~Data() override = default;
231 
232  void const* data_address() const override { return &value_; }
233  void* data_address() override { return &value_; }
234  std::unique_ptr<DataHolder> clone() const override {
235  return absl::make_unique<Data<T>>(*this);
236  }
237 
238  private:
239  ValueTypeT<T> value_;
240  };
241 
242  // Note that (1) `typeid(T)` returns a `std::type_info const&`, but that
243  // implicitly converts to a `std::type_index`, and (2) `std::hash<>` is
244  // specialized for `std::type_index` to use `std::type_index::hash_code()`.
245  std::unordered_map<std::type_index, std::unique_ptr<DataHolder>> m_;
246 };
247 
248 /**
249  * A template to hold a list of "option" types.
250  *
251  * This can be a useful way to create meaningful lists of options. For example,
252  * there could be a list containing all the gRPC options. Or a list of all
253  * ProductX options. This gives us a way to link to lists of options with
254  * doxygen, and to do some checking about what options a function may expect.
255  */
256 template <typename... T>
257 using OptionList = internal::TypeList<T...>;
258 
259 namespace internal {
260 
261 // Wraps `T` in a `OptionList`, unless it was already one.
262 template <typename T>
263 struct WrapTypeList {
264  using Type = OptionList<T>;
265 };
266 template <typename... T>
267 struct WrapTypeList<OptionList<T...>> {
268  using Type = OptionList<T...>; // Note: Doesn't work w/ nested OptionLists.
269 };
270 template <typename T>
271 using WrapTypeListT = typename WrapTypeList<T>::Type;
272 
273 template <typename... T>
274 void CheckExpectedOptionsImpl(OptionList<T...> const&, Options const& opts,
275  char const* caller) {
276  CheckExpectedOptionsImpl({typeid(T)...}, opts, caller);
277 }
278 
279 /**
280  * Checks that `Options` only contains the given expected options or a subset
281  * of them.
282  *
283  * Logs all unexpected options. Note that logging is not always shown
284  * on the console. Set the environment variable
285  * `GOOGLE_CLOUD_CPP_ENABLE_CLOG=yes` to enable logging.
286  *
287  * Options may be specified directly or as a collection in an `OptionList`.
288  * For example,
289  *
290  * @code
291  * struct FooOption { using Type = int; };
292  * struct BarOption { using Type = int; };
293  * using MyOptions = OptionList<FooOption, BarOption>;
294  *
295  * struct BazOption { using Type = int; };
296  *
297  * // All valid ways to call this with varying expectations.
298  * CheckExpectedOptions<FooOption>(opts, "test caller");
299  * CheckExpectedOptions<FooOption, BarOption>(opts, "test caller");
300  * CheckExpectedOptions<MyOptions>(opts, "test caller");
301  * CheckExpectedOptions<BazOption, MyOptions>(opts, "test caller");
302  * @endcode
303  *
304  * @param opts the `Options` to check.
305  * @param caller some string indicating the callee function; logged IFF there's
306  * an unexpected option
307  */
308 template <typename... T>
309 void CheckExpectedOptions(Options const& opts, char const* caller) {
310  using ExpectedTypes = TypeListCatT<WrapTypeListT<T>...>;
311  CheckExpectedOptionsImpl(ExpectedTypes{}, opts, caller);
312 }
313 
314 /**
315  * Moves the options from @p alternatives into @p preferred and returns the
316  * result. If an option already exists in @p preferred its value is used instead
317  * of the values in @p alternatives.
318  */
319 Options MergeOptions(Options preferred, Options alternatives);
320 
321 /**
322  * The prevailing options for the current operation.
323  */
324 Options const& CurrentOptions();
325 
326 /**
327  * RAII object to set/restore the prevailing options for the enclosing scope.
328  *
329  * @code
330  * struct IntOption { using Type = int; };
331  * assert(!internal::CurrentOptions().has<IntOption>());
332  * {
333  * internal::OptionsSpan span(Options{}.set<IntOption>(1));
334  * assert(internal::CurrentOptions().get<IntOption>() == 1);
335  * {
336  * internal::OptionsSpan span(Options{}.set<IntOption>(2));
337  * assert(internal::CurrentOptions().get<IntOption>() == 2);
338  * }
339  * assert(internal::CurrentOptions().get<IntOption>() == 1);
340  * }
341  * assert(!internal::CurrentOptions().has<IntOption>());
342  * @endcode
343  *
344  * @param opts the `Options` to install.
345  */
346 class ABSL_MUST_USE_RESULT OptionsSpan {
347  public:
348  explicit OptionsSpan(Options opts);
349 
350  // `OptionsSpan` should not be copied/moved.
351  OptionsSpan(OptionsSpan const&) = delete;
352  OptionsSpan(OptionsSpan&&) = delete;
353  OptionsSpan& operator=(OptionsSpan const&) = delete;
354  OptionsSpan& operator=(OptionsSpan&&) = delete;
355 
356  // `OptionsSpan` should only be used for block-scoped objects.
357  static void* operator new(std::size_t) = delete;
358  static void* operator new[](std::size_t) = delete;
359 
360  ~OptionsSpan();
361 
362  private:
363  Options opts_;
364 };
365 
366 } // namespace internal
367 
369 } // namespace cloud
370 } // namespace google
371 
372 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_OPTIONS_H