Google Cloud C++ Client 2.10.1
C++ Client Library for Google Cloud Platform
Loading...
Searching...
No Matches
options.h
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 <memory>
22#include <set>
23#include <typeindex>
24#include <typeinfo>
25#include <unordered_map>
26
27namespace google {
28namespace cloud {
30
31class Options;
32namespace internal {
33Options MergeOptions(Options, Options);
34void CheckExpectedOptionsImpl(std::set<std::type_index> const&, Options const&,
35 char const*);
36// TODO(#8800) - Remove when bigtable::Table no longer uses bigtable::DataClient
37bool IsEmpty(Options const&);
38template <typename T>
39inline 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 */
91class 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)] = std::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), std::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 std::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 */
258template <typename... T>
259using OptionList = internal::TypeList<T...>;
260
261namespace internal {
262
263// Wraps `T` in a `OptionList`, unless it was already one.
264template <typename T>
265struct WrapTypeList {
266 using Type = OptionList<T>;
267};
268template <typename... T>
269struct WrapTypeList<OptionList<T...>> {
270 using Type = OptionList<T...>; // Note: Doesn't work w/ nested OptionLists.
271};
272template <typename T>
273using WrapTypeListT = typename WrapTypeList<T>::Type;
274
275template <typename... T>
276void 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 */
310template <typename... T>
311void 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 */
321Options MergeOptions(Options preferred, Options alternatives);
322
323/**
324 * The prevailing options for the current operation.
325 */
326Options 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 */
348class 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
A class that holds option structs indexed by their type.
Definition: options.h:91
bool has() const
Returns true IFF an option with type T exists.
Definition: options.h:136
ValueTypeT< T > const & get() const
Returns a reference to the value for T, or a value-initialized default if T was not set.
Definition: options.h:174
ValueTypeT< T > & lookup(ValueTypeT< T > value={})
Returns a reference to the value for option T, setting the value to init_value if necessary.
Definition: options.h:202
Options & set(ValueTypeT< T > v)
Sets option T to the value v and returns a reference to *this.
Definition: options.h:125
Options & operator=(Options const &rhs)
Definition: options.h:103
Options(Options const &rhs)
Definition: options.h:100
Options & operator=(Options &&)=default
void unset()
Erases the option specified by the type T.
Definition: options.h:146
Options()=default
Constructs an empty instance.
Options(Options &&)=default
Contains all the Google Cloud C++ Library APIs.
Definition: async_operation.h:23
Definition: async_operation.h:22
#define GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
Definition: version.h:45
#define GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
Definition: version.h:43