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