• 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/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