HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
visitValue.h
Go to the documentation of this file.
1 //
2 // Copyright 2022 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_VT_VISIT_VALUE_H
8 #define PXR_BASE_VT_VISIT_VALUE_H
9 
10 #include "pxr/pxr.h"
11 
12 #include "pxr/base/vt/value.h"
13 
15 
16 namespace Vt_ValueVisitDetail {
17 
18 // These two overloads do SFINAE to detect whether the visitor can be invoked
19 // with the given held type T. If the visitor cannot be invoked with T, it is
20 // instead invoked with the VtValue itself. We use the int/long trick and pass
21 // 0 in the caller to disambiguate in case both are viable.
22 template <class T, class Visitor, class ...Args>
23 auto
24 Visit(VtValue const &val, Visitor &&visitor, int, Args &&...args) ->
25  decltype(std::forward<Visitor>(visitor)(
26  val.UncheckedGet<T>(), std::forward<Args>(args)...))
27 {
28  return std::forward<Visitor>(visitor)(
29  val.UncheckedGet<T>(), std::forward<Args>(args)...);
30 }
31 
32 template <class T, class Visitor, class ...Args>
33 auto
34 Visit(VtValue const &val, Visitor &&visitor, long, Args &&...args) {
35  return std::forward<Visitor>(visitor)(val, std::forward<Args>(args)...);
36 }
37 
38 } // Vt_ValueVisitDetail
39 
40 
41 /// Invoke \p visitor with \p value's held object if \p value holds an object of
42 /// one of the "known" value types (those in VT_VALUE_TYPES, see vt/types.h).
43 /// If \p value does not hold a known type, or if it is empty, or if \p visitor
44 /// cannot be called with an object of the held type, then call \p visitor with
45 /// \p value itself. Note this means that \p visitor must be callable with a
46 /// VtValue argument.
47 ///
48 /// VtVisitValue() can be lower overhead compared to a chained-if of
49 /// VtValue::IsHolding() calls, or a hash-table-lookup dispatch. Additionally,
50 /// visitors can handle related types with a single case, rather than calling
51 /// out all types individually. For example:
52 ///
53 /// \code
54 /// // If the value holds an array return its size, otherwise size_t(-1).
55 /// struct GetArraySize {
56 /// template <class T>
57 /// size_t operator()(VtArray<T> const &array) const {
58 /// return array.size();
59 /// }
60 /// size_t operator()(VtValue const &val) const {
61 /// return size_t(-1);
62 /// }
63 /// };
64 ///
65 /// VtVisitValue(VtValue(VtIntArray(123)), GetArraySize()) -> 123
66 /// VtVisitValue(VtValue(VtDoubleArray(234)), GetArraySize()) -> 234
67 /// VtVisitValue(VtValue(VtVec3fArray(345)), GetArraySize()) -> 345
68 /// VtVisitValue(VtValue("not-a-vt-array"), GetArraySize()) -> size_t(-1)
69 /// \endcode
70 ///
71 /// Note that the visitor is invoked as a normal C++ call expression, so
72 /// implicit conversions and standard overload resolution (including controlling
73 /// overload resolution via techniques like enable_if) can take place. For
74 /// example, consider the following, where the double-specific overload is
75 /// invoked for VtValues holding double, float, and GfHalf.
76 ///
77 /// \code
78 /// struct AsDouble {
79 /// double operator()(double val) const {
80 /// return val;
81 /// }
82 /// double operator()(VtValue const &) const {
83 /// return std::numeric_limits<double>::quiet_NaN();
84 /// }
85 /// };
86 ///
87 /// VtVisitValue(VtValue(1.23), AsDouble()) -> 1.23
88 /// VtVisitValue(VtValue(float(0.5f)), AsDouble()) -> 0.5
89 /// VtVisitValue(VtValue(GfHalf(1.5f)), AsDouble()) -> 1.5
90 /// VtVisitValue(VtValue("not-convertible-to-double"), AsDouble()) -> NaN.
91 ///
92 /// \endcode
93 ///
94 /// Any additional passed arguments following the visitor are forwarded to it.
95 ///
96 /// \code
97 ///
98 /// auto multiply = TfOverloads {
99 /// [](int val, int scl) { return val * scl; },
100 /// [](double val, int scl) { return static_cast<int>(rint(val * scl)); },
101 /// [](VtValue const &val, int scl) { return -1; }
102 /// };
103 ///
104 /// VtVisitValue(VtValue(123), multiply, 2); -> 246 // multiply by 2.
105 /// VtVisitValue(VtValue(1.23), multiply, 3); -> 4 // multiply by 3 & round.
106 ///
107 /// \endcode
108 template <class Visitor, class ...Args>
109 auto VtVisitValue(VtValue const &value, Visitor &&visitor, Args&&...args)
110 {
111  // This generally gets the compiler to emit a jump table to dispatch
112  // directly to the code for each known value type.
113  switch (value.GetKnownValueTypeIndex()) {
114 
115 // Cases for known types.
116 #define VT_CASE_FOR_TYPE_INDEX(unused, elem) \
117  case VtGetKnownValueTypeIndex<VT_TYPE(elem)>(): \
118  return Vt_ValueVisitDetail::Visit<VT_TYPE(elem)>( \
119  value, std::forward<Visitor>(visitor), 0, \
120  std::forward<Args>(args)...); \
121  break;
123 #undef VT_CASE_FOR_TYPE_INDEX
124 
125  default:
126  // Invoke visitor with value itself.
127  return Vt_ValueVisitDetail::Visit<VtValue>(
128  value, std::forward<Visitor>(visitor), 0,
129  std::forward<Args>(args)...);
130  break;
131  };
132 }
133 
134 /// Invoke `Visitor<T, ...>::Visit(...)` with \p T being \p value 's held
135 /// object's type if \p value holds an object of one of the "known" value types
136 /// (those in VT_VALUE_TYPES, see vt/types.h). If \p value does not hold a
137 /// known type or if it is empty, then invoke \p Visitor with `T == VtValue`.
138 ///
139 /// VtVisitValueType() can be lower overhead compared to a chained-if of
140 /// VtValue::IsHolding() calls, or a hash-table-lookup dispatch. Additionally,
141 /// visitors can use partial template specialization to handle related types
142 /// with a single case, rather than calling out all types individually. For
143 /// example:
144 ///
145 /// \code
146 /// // Return single-value sizes or array element sizes.
147 /// template <class T>
148 /// struct GetElementSize {
149 /// static constexpr size_t size = sizeof(T);
150 /// };
151 ///
152 /// template <class T>
153 /// struct GetElementSize<VtArray<T>> {
154 /// static constexpr size_t size = sizeof(T);
155 /// };
156 ///
157 /// template <>
158 /// struct GetElementSize<VtValue> {
159 /// static constexpr size_t size = 0; // unknown type.
160 /// };
161 ///
162 /// VtVisitValueType<GetElementSize>(VtValue(123)) -> 4
163 /// VtVisitValueType<GetElementSize>(VtValue(VtIntArray {})) -> 4
164 /// VtVisitValueType<GetElementSize>(VtValue(1.23)) -> 8
165 /// VtVisitValueType<GetElementSize>(VtValue(VtDoubleArray {})) -> 8
166 /// VtVisitValueType<GetElementSize>(VtValue(UnknownType {})) -> 0
167 /// \endcode
168 ///
169 /// Any explicitly passed template arguments are passed to \p Visitor and
170 /// function are forwarded to Visit().
171 ///
172 /// \code
173 /// // Return true if the common_type<T, U> is T.
174 /// template <class T, class U>
175 /// struct IsCommonTypeWith {
176 /// return std::is_same_v<std::common_type_t<T, U>, T>;
177 /// };
178 ///
179 /// VtVisitValueType<IsCommonTypeWith, float>(VtValue(123)) -> false
180 /// VtVisitValueType<IsCommonTypeWith, int>(VtValue(1.23)) -> true
181 /// \endcode
182 template <
183  template <class T, class ...> class Visitor,
184  typename ...TypeArgs,
185  typename ...FnArgs
186  >
187 auto VtVisitValueType(VtValue const &value, FnArgs&&...args)
188 {
189  // This generally gets the compiler to emit a jump table to dispatch
190  // directly to the code for each known value type.
191  switch (value.GetKnownValueTypeIndex()) {
192 
193 // Cases for known types.
194 #define VT_CASE_FOR_TYPE_INDEX(unused, elem) \
195  case VtGetKnownValueTypeIndex<VT_TYPE(elem)>(): \
196  return Visitor<VT_TYPE(elem), TypeArgs...>::Visit( \
197  std::forward<FnArgs>(args)...); \
198  break;
200 #undef VT_CASE_FOR_TYPE_INDEX
201 
202  default:
203  // Invoke visitor with VtValue itself.
205  std::forward<FnArgs>(args)...);
206  };
207 }
208 
209 /// \overload
210 ///
211 /// This overload accepts a leading class template argument that is passed along
212 /// to the Visitor as its second template argument. This can be useful to
213 /// invoke factories that instantiate types dependent on the VtValue held-type.
214 /// For example:
215 ///
216 /// \code
217 /// template <class T, template <class> class Template>
218 /// struct MakeNew {
219 /// static auto Visit(size_t count) {
220 /// return Template<T>::New(count);
221 /// }
222 /// };
223 ///
224 /// class WrapperBase {
225 /// public:
226 /// virtual ~WrapperBase() = default;
227 /// };
228 ///
229 /// template <class T>
230 /// class Wrapper : public WrapperBase {
231 /// std::unique_ptr<Wrapper> New(size_t count);
232 /// };
233 ///
234 /// // Returns Wrapper<T>::New(count) where T is the held-type of `val`.
235 /// return VtVisitValue<MakeNew, MyWrapper>(val, count);
236 /// \endcode
237 template <
238  template <class T, template <class...> class, class ...> class Visitor,
239  template <class...> class Tmpl,
240  typename ...TypeArgs,
241  typename ...FnArgs
242  >
243 auto VtVisitValueType(VtValue const &value, FnArgs&&...args)
244 {
245  // This generally gets the compiler to emit a jump table to dispatch
246  // directly to the code for each known value type.
247  switch (value.GetKnownValueTypeIndex()) {
248 
249 // Cases for known types.
250 #define VT_CASE_FOR_TYPE_INDEX(unused, elem) \
251  case VtGetKnownValueTypeIndex<VT_TYPE(elem)>(): \
252  return Visitor<VT_TYPE(elem), Tmpl, TypeArgs...>::Visit( \
253  std::forward<FnArgs>(args)...); \
254  break;
256 #undef VT_CASE_FOR_TYPE_INDEX
257 
258  default:
259  // Invoke visitor with VtValue itself.
261  std::forward<FnArgs>(args)...);
262  };
263 }
264 
266 
267 #endif // PXR_BASE_VT_VISIT_VALUE_H
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
GLsizei const GLfloat * value
Definition: glcorearb.h:824
auto VtVisitValueType(VtValue const &value, FnArgs &&...args)
Definition: visitValue.h:187
auto VtVisitValue(VtValue const &value, Visitor &&visitor, Args &&...args)
Definition: visitValue.h:109
#define VT_CASE_FOR_TYPE_INDEX(unused, elem)
#define VT_FOR_EACH_VALUE_TYPE(_macro)
Definition: types.h:207
int GetKnownValueTypeIndex() const
Definition: value.h:1034
GLuint GLfloat * val
Definition: glcorearb.h:1608
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
**If you just want to fire and args
Definition: thread.h:618
auto Visit(VtValue const &val, Visitor &&visitor, long, Args &&...args)
Definition: visitValue.h:34
Definition: value.h:89
auto Visit(VtValue const &val, Visitor &&visitor, int, Args &&...args) -> decltype(std::forward< Visitor >(visitor)(val.UncheckedGet< T >(), std::forward< Args >(args)...))
Definition: visitValue.h:24