Google Cloud C++ Client  2.6.0
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  * @ingroup options
90  */
91 class Options {
92  private:
93  template <typename T>
94  using ValueTypeT = typename T::Type;
95 
96  public:
97  /// Constructs an empty instance.
98  Options() = default;
99 
100  Options(Options const& rhs) {
101  for (auto const& kv : rhs.m_) m_.emplace(kv.first, kv.second->clone());
102  }
103  Options& operator=(Options const& rhs) {
104  Options tmp(rhs);
105  std::swap(m_, tmp.m_);
106  return *this;
107  }
108  Options(Options&&) = default;
109  Options& operator=(Options&&) = default;
110 
111  /**
112  * Sets option `T` to the value @p v and returns a reference to `*this`.
113  *
114  * @code
115  * struct FooOption {
116  * using Type = int;
117  * };
118  * auto opts = Options{}.set<FooOption>(123);
119  * @endcode
120  *
121  * @tparam T the option type
122  * @param v the value to set the option T
123  */
124  template <typename T>
125  Options& set(ValueTypeT<T> v) {
126  m_[typeid(T)] = absl::make_unique<Data<T>>(std::move(v));
127  return *this;
128  }
129 
130  /**
131  * Returns true IFF an option with type `T` exists.
132  *
133  * @tparam T the option type
134  */
135  template <typename T>
136  bool has() const {
137  return m_.find(typeid(T)) != m_.end();
138  }
139 
140  /**
141  * Erases the option specified by the type `T`.
142  *
143  * @tparam T the option type
144  */
145  template <typename T>
146  void unset() {
147  m_.erase(typeid(T));
148  }
149 
150  /**
151  * Returns a reference to the value for `T`, or a value-initialized default
152  * if `T` was not set.
153  *
154  * This method will always return a reference to a valid value of the correct
155  * type for option `T`, whether or not `T` has actually been set. Use
156  * `has<T>()` to check whether or not the option has been set.
157  *
158  * @code
159  * struct FooOption {
160  * using Type = std::set<std::string>;
161  * };
162  * Options opts;
163  * std::set<std::string> const& x = opts.get<FooOption>();
164  * assert(x.empty());
165  * assert(!x.has<FooOption>());
166  *
167  * opts.set<FooOption>({"foo"});
168  * assert(opts.get<FooOption>().size() == 1);
169  * @endcode
170  *
171  * @tparam T the option type
172  */
173  template <typename T>
174  ValueTypeT<T> const& get() const {
175  auto const it = m_.find(typeid(T));
176  if (it == m_.end()) return internal::DefaultValue<ValueTypeT<T>>();
177  auto const* value = it->second->data_address();
178  return *reinterpret_cast<ValueTypeT<T> const*>(value);
179  }
180 
181  /**
182  * Returns a reference to the value for option `T`, setting the value to @p
183  * init_value if necessary.
184  *
185  * @code
186  * struct BigOption {
187  * using Type = std::set<std::string>;
188  * };
189  * Options opts;
190  * std::set<std::string>& x = opts.lookup<BigOption>();
191  * assert(x.empty());
192  *
193  * x.insert("foo");
194  * opts.lookup<BigOption>().insert("bar");
195  * assert(x.size() == 2);
196  * @endcode
197  *
198  * @tparam T the option type
199  * @param value the initial value to use if `T` is not set (optional)
200  */
201  template <typename T>
202  ValueTypeT<T>& lookup(ValueTypeT<T> value = {}) {
203  auto p = m_.find(typeid(T));
204  if (p == m_.end()) {
205  p = m_.emplace(typeid(T), absl::make_unique<Data<T>>(std::move(value)))
206  .first;
207  }
208  auto* v = p->second->data_address();
209  return *reinterpret_cast<ValueTypeT<T>*>(v);
210  }
211 
212  private:
213  friend Options internal::MergeOptions(Options, Options);
214  friend void internal::CheckExpectedOptionsImpl(
215  std::set<std::type_index> const&, Options const&, char const*);
216  friend bool internal::IsEmpty(Options const&);
217 
218  // The type-erased data holder of all the option values.
219  class DataHolder {
220  public:
221  virtual ~DataHolder() = default;
222  virtual void const* data_address() const = 0;
223  virtual void* data_address() = 0;
224  virtual std::unique_ptr<DataHolder> clone() const = 0;
225  };
226 
227  // The data holder for all the option values.
228  template <typename T>
229  class Data : public DataHolder {
230  public:
231  explicit Data(ValueTypeT<T> v) : value_(std::move(v)) {}
232  ~Data() override = default;
233 
234  void const* data_address() const override { return &value_; }
235  void* data_address() override { return &value_; }
236  std::unique_ptr<DataHolder> clone() const override {
237  return absl::make_unique<Data<T>>(*this);
238  }
239 
240  private:
241  ValueTypeT<T> value_;
242  };
243 
244  // Note that (1) `typeid(T)` returns a `std::type_info const&`, but that
245  // implicitly converts to a `std::type_index`, and (2) `std::hash<>` is
246  // specialized for `std::type_index` to use `std::type_index::hash_code()`.
247  std::unordered_map<std::type_index, std::unique_ptr<DataHolder>> m_;
248 };
249 
250 /**
251  * A template to hold a list of "option" types.
252  *
253  * This can be a useful way to create meaningful lists of options. For example,
254  * there could be a list containing all the gRPC options. Or a list of all
255  * ProductX options. This gives us a way to link to lists of options with
256  * doxygen, and to do some checking about what options a function may expect.
257  */
258 template <typename... T>
259 using OptionList = internal::TypeList<T...>;
260 
261 namespace internal {
262 
263 // Wraps `T` in a `OptionList`, unless it was already one.
264 template <typename T>
265 struct WrapTypeList {
266  using Type = OptionList<T>;
267 };
268 template <typename... T>
269 struct WrapTypeList<OptionList<T...>> {
270  using Type = OptionList<T...>; // Note: Doesn't work w/ nested OptionLists.
271 };
272 template <typename T>
273 using WrapTypeListT = typename WrapTypeList<T>::Type;
274 
275 template <typename... T>
276 void CheckExpectedOptionsImpl(OptionList<T...> const&, Options const& opts,
277  char const* caller) {
278  CheckExpectedOptionsImpl({typeid(T)...}, opts, caller);
279 }
280 
281 /**
282  * Checks that `Options` only contains the given expected options or a subset
283  * of them.
284  *
285  * Logs all unexpected options. Note that logging is not always shown
286  * on the console. Set the environment variable
287  * `GOOGLE_CLOUD_CPP_ENABLE_CLOG=yes` to enable logging.
288  *
289  * Options may be specified directly or as a collection in an `OptionList`.
290  * For example,
291  *
292  * @code
293  * struct FooOption { using Type = int; };
294  * struct BarOption { using Type = int; };
295  * using MyOptions = OptionList<FooOption, BarOption>;
296  *
297  * struct BazOption { using Type = int; };
298  *
299  * // All valid ways to call this with varying expectations.
300  * CheckExpectedOptions<FooOption>(opts, "test caller");
301  * CheckExpectedOptions<FooOption, BarOption>(opts, "test caller");
302  * CheckExpectedOptions<MyOptions>(opts, "test caller");
303  * CheckExpectedOptions<BazOption, MyOptions>(opts, "test caller");
304  * @endcode
305  *
306  * @param opts the `Options` to check.
307  * @param caller some string indicating the callee function; logged IFF there's
308  * an unexpected option
309  */
310 template <typename... T>
311 void CheckExpectedOptions(Options const& opts, char const* caller) {
312  using ExpectedTypes = TypeListCatT<WrapTypeListT<T>...>;
313  CheckExpectedOptionsImpl(ExpectedTypes{}, opts, caller);
314 }
315 
316 /**
317  * Moves the options from @p alternatives into @p preferred and returns the
318  * result. If an option already exists in @p preferred its value is used instead
319  * of the values in @p alternatives.
320  */
321 Options MergeOptions(Options preferred, Options alternatives);
322 
323 /**
324  * The prevailing options for the current operation.
325  */
326 Options const& CurrentOptions();
327 
328 /**
329  * RAII object to set/restore the prevailing options for the enclosing scope.
330  *
331  * @code
332  * struct IntOption { using Type = int; };
333  * assert(!internal::CurrentOptions().has<IntOption>());
334  * {
335  * internal::OptionsSpan span(Options{}.set<IntOption>(1));
336  * assert(internal::CurrentOptions().get<IntOption>() == 1);
337  * {
338  * internal::OptionsSpan span(Options{}.set<IntOption>(2));
339  * assert(internal::CurrentOptions().get<IntOption>() == 2);
340  * }
341  * assert(internal::CurrentOptions().get<IntOption>() == 1);
342  * }
343  * assert(!internal::CurrentOptions().has<IntOption>());
344  * @endcode
345  *
346  * @param opts the `Options` to install.
347  */
348 class ABSL_MUST_USE_RESULT OptionsSpan {
349  public:
350  explicit OptionsSpan(Options opts);
351 
352  // `OptionsSpan` should not be copied/moved.
353  OptionsSpan(OptionsSpan const&) = delete;
354  OptionsSpan(OptionsSpan&&) = delete;
355  OptionsSpan& operator=(OptionsSpan const&) = delete;
356  OptionsSpan& operator=(OptionsSpan&&) = delete;
357 
358  // `OptionsSpan` should only be used for block-scoped objects.
359  static void* operator new(std::size_t) = delete;
360  static void* operator new[](std::size_t) = delete;
361 
362  ~OptionsSpan();
363 
364  private:
365  Options opts_;
366 };
367 
368 } // namespace internal
369 
371 } // namespace cloud
372 } // namespace google
373 
374 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_OPTIONS_H