• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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