1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #ifndef GRPC_SRC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H
20 #define GRPC_SRC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H
21
22 #include <grpc/event_engine/event_engine.h>
23 #include <grpc/grpc.h>
24 #include <grpc/support/port_platform.h>
25 #include <stddef.h>
26 #include <stdint.h>
27
28 #include <algorithm> // IWYU pragma: keep
29 #include <iosfwd>
30 #include <memory>
31 #include <string>
32 #include <type_traits>
33 #include <utility>
34
35 #include "absl/meta/type_traits.h"
36 #include "absl/strings/string_view.h"
37 #include "absl/types/optional.h"
38 #include "src/core/lib/surface/channel_stack_type.h"
39 #include "src/core/util/avl.h"
40 #include "src/core/util/debug_location.h"
41 #include "src/core/util/dual_ref_counted.h"
42 #include "src/core/util/ref_counted.h"
43 #include "src/core/util/ref_counted_ptr.h"
44 #include "src/core/util/ref_counted_string.h"
45 #include "src/core/util/time.h"
46 #include "src/core/util/useful.h"
47
48 // TODO(hork): When we're ready to allow setting via a channel arg from the
49 // application, replace this with a macro in
50 // include/grpc/impl/codegen/grpc_types.h.
51 #define GRPC_INTERNAL_ARG_EVENT_ENGINE "grpc.internal.event_engine"
52
53 // Channel args are intentionally immutable, to avoid the need for locking.
54
55 namespace grpc_core {
56
57 // Define a traits object for vtable lookup - allows us to integrate with
58 // existing code easily (just define the trait!) and allows some magic in
59 // ChannelArgs to automatically derive a vtable from a T*.
60 // To participate as a pointer, instances should expose the function:
61 // // Gets the vtable for this type
62 // static const grpc_arg_pointer_vtable* VTable();
63 // // Performs any mutations required for channel args to own a pointer
64 // // Only needed if ChannelArgs::Set is to be called with a raw pointer.
65 // static void* TakeUnownedPointer(T* p);
66 template <typename T, typename Ignored = void /* for SFINAE */>
67 struct ChannelArgTypeTraits;
68
69 namespace channel_args_detail {
PointerCompare(void * a_ptr,const grpc_arg_pointer_vtable * a_vtable,void * b_ptr,const grpc_arg_pointer_vtable * b_vtable)70 inline int PointerCompare(void* a_ptr, const grpc_arg_pointer_vtable* a_vtable,
71 void* b_ptr,
72 const grpc_arg_pointer_vtable* b_vtable) {
73 int c = QsortCompare(a_ptr, b_ptr);
74 if (c == 0) return 0;
75 c = QsortCompare(a_vtable, b_vtable);
76 if (c != 0) return c;
77 return a_vtable->cmp(a_ptr, b_ptr);
78 }
79
80 // The type returned by calling Ref() on a T - used to determine the basest-type
81 // before the crt refcount base class.
82 template <typename T>
83 using RefType = absl::remove_cvref_t<decltype(*std::declval<T>().Ref())>;
84
85 template <typename T, typename Ignored = void /* for SFINAE */>
86 struct IsRawPointerTagged {
87 static constexpr bool kValue = false;
88 };
89 template <typename T>
90 struct IsRawPointerTagged<T,
91 absl::void_t<typename T::RawPointerChannelArgTag>> {
92 static constexpr bool kValue = true;
93 };
94 } // namespace channel_args_detail
95
96 // Specialization for ref-counted pointers.
97 // Types should expose:
98 // static int ChannelArgsCompare(const T* a, const T* b);
99 template <typename T>
100 struct ChannelArgTypeTraits<
101 T, absl::enable_if_t<
102 !channel_args_detail::IsRawPointerTagged<T>::kValue &&
103 (std::is_base_of<RefCounted<channel_args_detail::RefType<T>>,
104 channel_args_detail::RefType<T>>::value ||
105 std::is_base_of<RefCounted<channel_args_detail::RefType<T>,
106 NonPolymorphicRefCount>,
107 channel_args_detail::RefType<T>>::value ||
108 std::is_base_of<DualRefCounted<channel_args_detail::RefType<T>>,
109 channel_args_detail::RefType<T>>::value),
110 void>> {
111 static const grpc_arg_pointer_vtable* VTable() {
112 static const grpc_arg_pointer_vtable tbl = {
113 // copy
114 [](void* p) -> void* {
115 return p == nullptr ? nullptr
116 : static_cast<T*>(p)
117 ->Ref(DEBUG_LOCATION, "ChannelArgs copy")
118 .release();
119 },
120 // destroy
121 [](void* p) {
122 if (p != nullptr) {
123 static_cast<T*>(p)->Unref(DEBUG_LOCATION, "ChannelArgs destroy");
124 }
125 },
126 // compare
127 [](void* p1, void* p2) {
128 return T::ChannelArgsCompare(static_cast<const T*>(p1),
129 static_cast<const T*>(p2));
130 },
131 };
132 return &tbl;
133 };
134 };
135
136 // Define a check for shared_ptr supported types, which must extend
137 // enable_shared_from_this.
138 template <typename T>
139 struct SupportedSharedPtrType
140 : std::integral_constant<
141 bool, std::is_base_of<std::enable_shared_from_this<T>, T>::value> {};
142 template <>
143 struct SupportedSharedPtrType<grpc_event_engine::experimental::EventEngine>
144 : std::true_type {};
145
146 // Specialization for shared_ptr
147 // Incurs an allocation because shared_ptr.release is not a thing.
148 template <typename T>
149 struct is_shared_ptr : std::false_type {};
150 template <typename T>
151 struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
152 template <typename T>
153 struct ChannelArgTypeTraits<T,
154 absl::enable_if_t<is_shared_ptr<T>::value, void>> {
155 static void* TakeUnownedPointer(T* p) { return p; }
156 static const grpc_arg_pointer_vtable* VTable() {
157 static const grpc_arg_pointer_vtable tbl = {
158 // copy
159 [](void* p) -> void* { return new T(*static_cast<T*>(p)); },
160 // destroy
161 [](void* p) { delete static_cast<T*>(p); },
162 // compare
163 [](void* p1, void* p2) {
164 return QsortCompare(static_cast<const T*>(p1)->get(),
165 static_cast<const T*>(p2)->get());
166 },
167 };
168 return &tbl;
169 };
170 };
171
172 // If a type declares some member 'struct RawPointerChannelArgTag {}' then
173 // we automatically generate a vtable for it that does not do any ownership
174 // management and compares the type by pointer identity.
175 // This is intended to be relatively ugly because *most types should worry about
176 // ownership*.
177 template <typename T>
178 struct ChannelArgTypeTraits<T,
179 absl::void_t<typename T::RawPointerChannelArgTag>> {
180 static void* TakeUnownedPointer(T* p) { return p; }
181 static const grpc_arg_pointer_vtable* VTable() {
182 static const grpc_arg_pointer_vtable tbl = {
183 // copy
184 [](void* p) -> void* { return p; },
185 // destroy
186 [](void*) {},
187 // compare
188 [](void* p1, void* p2) { return QsortCompare(p1, p2); },
189 };
190 return &tbl;
191 };
192 };
193
194 // Determine if the pointer for a channel arg name should be const or not
195 template <typename T, typename SfinaeVoid = void>
196 struct ChannelArgPointerShouldBeConst {
197 static constexpr bool kValue = false;
198 };
199
200 template <typename T>
201 struct ChannelArgPointerShouldBeConst<
202 T, absl::void_t<decltype(T::ChannelArgUseConstPtr())>> {
203 static constexpr bool kValue = T::ChannelArgUseConstPtr();
204 };
205
206 // GetObject support for shared_ptr and RefCountedPtr
207 template <typename T, typename Ignored = void /* for SFINAE */>
208 struct GetObjectImpl;
209 // std::shared_ptr implementation
210 template <typename T>
211 struct GetObjectImpl<
212 T, absl::enable_if_t<!ChannelArgPointerShouldBeConst<T>::kValue &&
213 SupportedSharedPtrType<T>::value,
214 void>> {
215 using Result = T*;
216 using ReffedResult = std::shared_ptr<T>;
217 using StoredType = std::shared_ptr<T>*;
218 static Result Get(StoredType p) {
219 if (p == nullptr) return nullptr;
220 return p->get();
221 };
222 static ReffedResult GetReffed(StoredType p) {
223 if (p == nullptr) return nullptr;
224 return ReffedResult(*p);
225 };
226 static ReffedResult GetReffed(StoredType p,
227 const DebugLocation& /* location */,
228 const char* /* reason */) {
229 return GetReffed(*p);
230 };
231 };
232 // RefCountedPtr
233 template <typename T>
234 struct GetObjectImpl<
235 T, absl::enable_if_t<!ChannelArgPointerShouldBeConst<T>::kValue &&
236 !SupportedSharedPtrType<T>::value,
237 void>> {
238 using Result = T*;
239 using ReffedResult = RefCountedPtr<T>;
240 using StoredType = Result;
241 static Result Get(StoredType p) { return p; };
242 static ReffedResult GetReffed(StoredType p) {
243 if (p == nullptr) return nullptr;
244 return p->template RefAsSubclass<T>();
245 };
246 static ReffedResult GetReffed(StoredType p, const DebugLocation& location,
247 const char* reason) {
248 if (p == nullptr) return nullptr;
249 return p->template RefAsSubclass<T>(location, reason);
250 };
251 };
252
253 template <typename T>
254 struct GetObjectImpl<
255 T, absl::enable_if_t<ChannelArgPointerShouldBeConst<T>::kValue &&
256 !SupportedSharedPtrType<T>::value,
257 void>> {
258 using Result = const T*;
259 using ReffedResult = RefCountedPtr<const T>;
260 using StoredType = Result;
261 static Result Get(StoredType p) { return p; };
262 static ReffedResult GetReffed(StoredType p) {
263 if (p == nullptr) return nullptr;
264 return p->Ref();
265 };
266 static ReffedResult GetReffed(StoredType p, const DebugLocation& location,
267 const char* reason) {
268 if (p == nullptr) return nullptr;
269 return p->Ref(location, reason);
270 };
271 };
272
273 // Provide the canonical name for a type's channel arg key
274 template <typename T>
275 struct ChannelArgNameTraits {
276 static absl::string_view ChannelArgName() { return T::ChannelArgName(); }
277 };
278 template <typename T>
279 struct ChannelArgNameTraits<std::shared_ptr<T>> {
280 static absl::string_view ChannelArgName() { return T::ChannelArgName(); }
281 };
282 // Specialization for the EventEngine
283 template <>
284 struct ChannelArgNameTraits<grpc_event_engine::experimental::EventEngine> {
285 static absl::string_view ChannelArgName() {
286 return GRPC_INTERNAL_ARG_EVENT_ENGINE;
287 }
288 };
289
290 class ChannelArgs {
291 public:
292 class Pointer {
293 public:
294 Pointer(void* p, const grpc_arg_pointer_vtable* vtable);
295 ~Pointer() { vtable_->destroy(p_); }
296
297 Pointer(const Pointer& other);
298 Pointer& operator=(Pointer other) {
299 std::swap(p_, other.p_);
300 std::swap(vtable_, other.vtable_);
301 return *this;
302 }
303 Pointer(Pointer&& other) noexcept;
304 Pointer& operator=(Pointer&& other) noexcept {
305 std::swap(p_, other.p_);
306 std::swap(vtable_, other.vtable_);
307 return *this;
308 }
309
310 friend int QsortCompare(const Pointer& a, const Pointer& b) {
311 return channel_args_detail::PointerCompare(a.p_, a.vtable_, b.p_,
312 b.vtable_);
313 }
314
315 bool operator==(const Pointer& rhs) const {
316 return QsortCompare(*this, rhs) == 0;
317 }
318 bool operator<(const Pointer& rhs) const {
319 return QsortCompare(*this, rhs) < 0;
320 }
321 bool operator!=(const Pointer& rhs) const {
322 return QsortCompare(*this, rhs) != 0;
323 }
324
325 void* c_pointer() const { return p_; }
326 const grpc_arg_pointer_vtable* c_vtable() const { return vtable_; }
327
328 private:
329 static const grpc_arg_pointer_vtable* EmptyVTable();
330
331 void* p_;
332 const grpc_arg_pointer_vtable* vtable_;
333 };
334
335 // Helper to create a `Pointer` object to an object that is not owned by the
336 // `ChannelArgs` object. Useful for tests, a code smell for production code.
337 template <typename T>
338 static Pointer UnownedPointer(T* p) {
339 static const grpc_arg_pointer_vtable vtable = {
340 [](void* p) -> void* { return p; },
341 [](void*) {},
342 [](void* p, void* q) { return QsortCompare(p, q); },
343 };
344 return Pointer(p, &vtable);
345 }
346
347 class Value {
348 public:
349 explicit Value(int n)
350 : rep_(reinterpret_cast<void*>(static_cast<intptr_t>(n)),
351 &int_vtable_) {}
352 explicit Value(std::string s)
353 : rep_(RefCountedString::Make(s).release(), &string_vtable_) {}
354 explicit Value(Pointer p) : rep_(std::move(p)) {}
355
356 absl::optional<int> GetIfInt() const {
357 if (rep_.c_vtable() != &int_vtable_) return absl::nullopt;
358 return reinterpret_cast<intptr_t>(rep_.c_pointer());
359 }
360 RefCountedPtr<RefCountedString> GetIfString() const {
361 if (rep_.c_vtable() != &string_vtable_) return nullptr;
362 return static_cast<RefCountedString*>(rep_.c_pointer())->Ref();
363 }
364 const Pointer* GetIfPointer() const {
365 if (rep_.c_vtable() == &int_vtable_) return nullptr;
366 if (rep_.c_vtable() == &string_vtable_) return nullptr;
367 return &rep_;
368 }
369
370 absl::string_view ToString(std::list<std::string>& backing) const;
371
372 grpc_arg MakeCArg(const char* name) const;
373
374 bool operator<(const Value& rhs) const { return rep_ < rhs.rep_; }
375 bool operator==(const Value& rhs) const { return rep_ == rhs.rep_; }
376 bool operator!=(const Value& rhs) const { return !this->operator==(rhs); }
377 bool operator==(absl::string_view rhs) const {
378 auto str = GetIfString();
379 if (str == nullptr) return false;
380 return str->as_string_view() == rhs;
381 }
382
383 private:
384 static const grpc_arg_pointer_vtable int_vtable_;
385 static const grpc_arg_pointer_vtable string_vtable_;
386
387 Pointer rep_;
388 };
389
390 struct ChannelArgsDeleter {
391 void operator()(const grpc_channel_args* p) const;
392 };
393 using CPtr =
394 std::unique_ptr<const grpc_channel_args, ChannelArgs::ChannelArgsDeleter>;
395
396 ChannelArgs();
397 ~ChannelArgs();
398 ChannelArgs(const ChannelArgs&);
399 ChannelArgs& operator=(const ChannelArgs&);
400 ChannelArgs(ChannelArgs&&) noexcept;
401 ChannelArgs& operator=(ChannelArgs&&) noexcept;
402
403 static ChannelArgs FromC(const grpc_channel_args* args);
404 static ChannelArgs FromC(const grpc_channel_args& args) {
405 return FromC(&args);
406 }
407 // Construct a new grpc_channel_args struct.
408 CPtr ToC() const;
409
410 // Returns the union of this channel args with other.
411 // If a key is present in both, the value from this is used.
412 GRPC_MUST_USE_RESULT ChannelArgs UnionWith(ChannelArgs other) const;
413
414 // Only used in union_with_test.cc, reference version of UnionWith for
415 // differential fuzzing.
416 GRPC_MUST_USE_RESULT ChannelArgs
417 FuzzingReferenceUnionWith(ChannelArgs other) const;
418
419 const Value* Get(absl::string_view name) const;
420 GRPC_MUST_USE_RESULT ChannelArgs Set(absl::string_view name,
421 Pointer value) const;
422 GRPC_MUST_USE_RESULT ChannelArgs Set(absl::string_view name, int value) const;
423 GRPC_MUST_USE_RESULT ChannelArgs Set(absl::string_view name,
424 absl::string_view value) const;
425 GRPC_MUST_USE_RESULT ChannelArgs Set(absl::string_view name,
426 std::string value) const;
427 GRPC_MUST_USE_RESULT ChannelArgs Set(absl::string_view name,
428 const char* value) const;
429 GRPC_MUST_USE_RESULT ChannelArgs Set(grpc_arg arg) const;
430 template <typename T>
431 GRPC_MUST_USE_RESULT absl::enable_if_t<
432 std::is_same<const grpc_arg_pointer_vtable*,
433 decltype(ChannelArgTypeTraits<T>::VTable())>::value,
434 ChannelArgs>
435 Set(absl::string_view name, T* value) const {
436 return Set(name, Pointer(ChannelArgTypeTraits<T>::TakeUnownedPointer(value),
437 ChannelArgTypeTraits<T>::VTable()));
438 }
439 template <typename T>
440 GRPC_MUST_USE_RESULT auto Set(absl::string_view name,
441 RefCountedPtr<T> value) const
442 -> absl::enable_if_t<
443 !ChannelArgPointerShouldBeConst<T>::kValue &&
444 std::is_same<const grpc_arg_pointer_vtable*,
445 decltype(ChannelArgTypeTraits<
446 absl::remove_cvref_t<T>>::VTable())>::value,
447 ChannelArgs> {
448 return Set(
449 name, Pointer(value.release(),
450 ChannelArgTypeTraits<absl::remove_cvref_t<T>>::VTable()));
451 }
452 template <typename T>
453 GRPC_MUST_USE_RESULT auto Set(absl::string_view name,
454 RefCountedPtr<const T> value) const
455 -> absl::enable_if_t<
456 ChannelArgPointerShouldBeConst<T>::kValue &&
457 std::is_same<const grpc_arg_pointer_vtable*,
458 decltype(ChannelArgTypeTraits<
459 absl::remove_cvref_t<T>>::VTable())>::value,
460 ChannelArgs> {
461 return Set(
462 name, Pointer(const_cast<T*>(value.release()),
463 ChannelArgTypeTraits<absl::remove_cvref_t<T>>::VTable()));
464 }
465 template <typename T>
466 GRPC_MUST_USE_RESULT absl::enable_if_t<
467 std::is_same<
468 const grpc_arg_pointer_vtable*,
469 decltype(ChannelArgTypeTraits<std::shared_ptr<T>>::VTable())>::value,
470 ChannelArgs>
471 Set(absl::string_view name, std::shared_ptr<T> value) const {
472 static_assert(SupportedSharedPtrType<T>::value,
473 "Type T must extend std::enable_shared_from_this to be added "
474 "into ChannelArgs as a shared_ptr<T>");
475 auto* store_value = new std::shared_ptr<T>(value);
476 return Set(
477 name,
478 Pointer(ChannelArgTypeTraits<std::shared_ptr<T>>::TakeUnownedPointer(
479 store_value),
480 ChannelArgTypeTraits<std::shared_ptr<T>>::VTable()));
481 }
482 template <typename T>
483 GRPC_MUST_USE_RESULT ChannelArgs SetIfUnset(absl::string_view name,
484 T value) const {
485 if (Contains(name)) return *this;
486 return Set(name, std::move(value));
487 }
488 GRPC_MUST_USE_RESULT ChannelArgs Remove(absl::string_view name) const;
489 bool Contains(absl::string_view name) const;
490
491 GRPC_MUST_USE_RESULT ChannelArgs
492 RemoveAllKeysWithPrefix(absl::string_view prefix) const;
493
494 template <typename T>
495 bool ContainsObject() const {
496 return Get(ChannelArgNameTraits<T>::ChannelArgName()) != nullptr;
497 }
498
499 absl::optional<int> GetInt(absl::string_view name) const;
500 absl::optional<absl::string_view> GetString(absl::string_view name) const;
501 absl::optional<std::string> GetOwnedString(absl::string_view name) const;
502 // WARNING: this is broken if `name` represents something that was stored as a
503 // RefCounted<const T> - we will discard the const-ness.
504 void* GetVoidPointer(absl::string_view name) const;
505 template <typename T>
506 typename GetObjectImpl<T>::StoredType GetPointer(
507 absl::string_view name) const {
508 return static_cast<typename GetObjectImpl<T>::StoredType>(
509 GetVoidPointer(name));
510 }
511 absl::optional<Duration> GetDurationFromIntMillis(
512 absl::string_view name) const;
513 absl::optional<bool> GetBool(absl::string_view name) const;
514
515 // Object based get/set.
516 // Deal with the common case that we set a pointer to an object under
517 // the same name in every usage.
518 // Expects ChannelArgTypeTraits to exist for T, and T to expose:
519 // static string_view ChannelArgName();
520 template <typename T>
521 GRPC_MUST_USE_RESULT ChannelArgs SetObject(T* p) const {
522 return Set(T::ChannelArgName(), p);
523 }
524 template <typename T>
525 GRPC_MUST_USE_RESULT ChannelArgs SetObject(RefCountedPtr<T> p) const {
526 return Set(T::ChannelArgName(), std::move(p));
527 }
528 template <typename T>
529 GRPC_MUST_USE_RESULT ChannelArgs SetObject(std::shared_ptr<T> p) const {
530 return Set(ChannelArgNameTraits<T>::ChannelArgName(), std::move(p));
531 }
532 template <typename T>
533 typename GetObjectImpl<T>::Result GetObject() const {
534 return GetObjectImpl<T>::Get(
535 GetPointer<T>(ChannelArgNameTraits<T>::ChannelArgName()));
536 }
537 template <typename T>
538 typename GetObjectImpl<T>::ReffedResult GetObjectRef() const {
539 return GetObjectImpl<T>::GetReffed(
540 GetPointer<T>(ChannelArgNameTraits<T>::ChannelArgName()));
541 }
542 template <typename T>
543 typename GetObjectImpl<T>::ReffedResult GetObjectRef(
544 const DebugLocation& location, const char* reason) const {
545 return GetObjectImpl<T>::GetReffed(
546 GetPointer<T>(ChannelArgNameTraits<T>::ChannelArgName()), location,
547 reason);
548 }
549
550 bool operator!=(const ChannelArgs& other) const;
551 bool operator<(const ChannelArgs& other) const;
552 bool operator==(const ChannelArgs& other) const;
553
554 // Helpers for commonly accessed things
555
556 bool WantMinimalStack() const;
557 std::string ToString() const;
558
559 template <typename Sink>
560 friend void AbslStringify(Sink& sink, const ChannelArgs& args) {
561 sink.Append(args.ToString());
562 }
563
564 private:
565 explicit ChannelArgs(AVL<RefCountedStringValue, Value> args);
566
567 GRPC_MUST_USE_RESULT ChannelArgs Set(absl::string_view name,
568 Value value) const;
569
570 AVL<RefCountedStringValue, Value> args_;
571 };
572
573 std::ostream& operator<<(std::ostream& out, const ChannelArgs& args);
574
575 } // namespace grpc_core
576
577 /// Copy the arguments in \a src into a new instance
578 grpc_channel_args* grpc_channel_args_copy(const grpc_channel_args* src);
579
580 /// Copy the arguments in \a src into a new instance, stably sorting keys
581 grpc_channel_args* grpc_channel_args_normalize(const grpc_channel_args* src);
582
583 /// Copy the arguments in \a src and append \a to_add. If \a to_add is NULL, it
584 /// is equivalent to calling \a grpc_channel_args_copy.
585 grpc_channel_args* grpc_channel_args_copy_and_add(const grpc_channel_args* src,
586 const grpc_arg* to_add,
587 size_t num_to_add);
588
589 /// Copies the arguments in \a src except for those whose keys are in
590 /// \a to_remove.
591 grpc_channel_args* grpc_channel_args_copy_and_remove(
592 const grpc_channel_args* src, const char** to_remove, size_t num_to_remove);
593
594 /// Copies the arguments from \a src except for those whose keys are in
595 /// \a to_remove and appends the arguments in \a to_add.
596 grpc_channel_args* grpc_channel_args_copy_and_add_and_remove(
597 const grpc_channel_args* src, const char** to_remove, size_t num_to_remove,
598 const grpc_arg* to_add, size_t num_to_add);
599
600 /// Perform the union of \a a and \a b, prioritizing \a a entries
601 grpc_channel_args* grpc_channel_args_union(const grpc_channel_args* a,
602 const grpc_channel_args* b);
603
604 /// Destroy arguments created by \a grpc_channel_args_copy
605 void grpc_channel_args_destroy(grpc_channel_args* a);
606 inline void grpc_channel_args_destroy(const grpc_channel_args* a) {
607 grpc_channel_args_destroy(const_cast<grpc_channel_args*>(a));
608 }
609
610 int grpc_channel_args_compare(const grpc_channel_args* a,
611 const grpc_channel_args* b);
612
613 /// Returns the value of argument \a name from \a args, or NULL if not found.
614 const grpc_arg* grpc_channel_args_find(const grpc_channel_args* args,
615 const char* name);
616
617 bool grpc_channel_args_want_minimal_stack(const grpc_channel_args* args);
618
619 typedef struct grpc_integer_options {
620 int default_value; // Return this if value is outside of expected bounds.
621 int min_value;
622 int max_value;
623 } grpc_integer_options;
624
625 /// Returns the value of \a arg, subject to the constraints in \a options.
626 int grpc_channel_arg_get_integer(const grpc_arg* arg,
627 const grpc_integer_options options);
628 /// Similar to the above, but needs to find the arg from \a args by the name
629 /// first.
630 int grpc_channel_args_find_integer(const grpc_channel_args* args,
631 const char* name,
632 const grpc_integer_options options);
633
634 /// Returns the value of \a arg if \a arg is of type GRPC_ARG_STRING.
635 /// Otherwise, emits a warning log, and returns nullptr.
636 /// If arg is nullptr, returns nullptr, and does not emit a warning.
637 char* grpc_channel_arg_get_string(const grpc_arg* arg);
638 /// Similar to the above, but needs to find the arg from \a args by the name
639 /// first.
640 char* grpc_channel_args_find_string(const grpc_channel_args* args,
641 const char* name);
642 /// If \a arg is of type GRPC_ARG_INTEGER, returns true if it's non-zero.
643 /// Returns \a default_value if \a arg is of other types.
644 bool grpc_channel_arg_get_bool(const grpc_arg* arg, bool default_value);
645 /// Similar to the above, but needs to find the arg from \a args by the name
646 /// first.
647 bool grpc_channel_args_find_bool(const grpc_channel_args* args,
648 const char* name, bool default_value);
649
650 template <typename T>
651 T* grpc_channel_args_find_pointer(const grpc_channel_args* args,
652 const char* name) {
653 const grpc_arg* arg = grpc_channel_args_find(args, name);
654 if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return nullptr;
655 return static_cast<T*>(arg->value.pointer.p);
656 }
657
658 // Helpers for creating channel args.
659 grpc_arg grpc_channel_arg_string_create(char* name, char* value);
660 grpc_arg grpc_channel_arg_integer_create(char* name, int value);
661 grpc_arg grpc_channel_arg_pointer_create(char* name, void* value,
662 const grpc_arg_pointer_vtable* vtable);
663
664 // Returns a string representing channel args in human-readable form.
665 std::string grpc_channel_args_string(const grpc_channel_args* args);
666
667 namespace grpc_core {
668 // Ensure no duplicate channel args (with some backwards compatibility hacks).
669 // Eliminate any grpc.internal.* args.
670 // Return a C++ object.
671 ChannelArgs ChannelArgsBuiltinPrecondition(const grpc_channel_args* src);
672 } // namespace grpc_core
673
674 // Takes ownership of the old_args
675 typedef grpc_core::ChannelArgs (
676 *grpc_channel_args_client_channel_creation_mutator)(
677 const char* target, const grpc_core::ChannelArgs& old_args,
678 grpc_channel_stack_type type);
679
680 // Should be called only once globally before grpc is init'ed.
681 void grpc_channel_args_set_client_channel_creation_mutator(
682 grpc_channel_args_client_channel_creation_mutator cb);
683 // This will be called at the creation of each channel.
684 grpc_channel_args_client_channel_creation_mutator
685 grpc_channel_args_get_client_channel_creation_mutator();
686
687 #endif // GRPC_SRC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H
688