HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
wrapArray.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 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_WRAP_ARRAY_H
8 #define PXR_BASE_VT_WRAP_ARRAY_H
9 
10 #include "pxr/pxr.h"
11 #include "pxr/base/vt/api.h"
12 #include "pxr/base/vt/array.h"
13 #include "pxr/base/vt/types.h"
14 #include "pxr/base/vt/value.h"
16 
17 #include "pxr/base/arch/math.h"
18 #include "pxr/base/arch/inttypes.h"
19 #include "pxr/base/arch/pragmas.h"
20 #include "pxr/base/gf/half.h"
21 #include "pxr/base/gf/traits.h"
23 #include "pxr/base/tf/pyFunction.h"
24 #include "pxr/base/tf/pyLock.h"
27 #include "pxr/base/tf/pyUtils.h"
28 #include "pxr/base/tf/iterator.h"
29 #include "pxr/base/tf/meta.h"
30 #include "pxr/base/tf/span.h"
32 #include "pxr/base/tf/tf.h"
34 
35 #include "pxr/external/boost/python/class.hpp"
36 #include "pxr/external/boost/python/copy_const_reference.hpp"
37 #include "pxr/external/boost/python/def.hpp"
38 #include "pxr/external/boost/python/detail/api_placeholder.hpp"
39 #include "pxr/external/boost/python/extract.hpp"
40 #include "pxr/external/boost/python/implicit.hpp"
41 #include "pxr/external/boost/python/iterator.hpp"
42 #include "pxr/external/boost/python/make_constructor.hpp"
43 #include "pxr/external/boost/python/object.hpp"
44 #include "pxr/external/boost/python/operators.hpp"
45 #include "pxr/external/boost/python/return_arg.hpp"
46 #include "pxr/external/boost/python/slice.hpp"
47 #include "pxr/external/boost/python/type_id.hpp"
48 #include "pxr/external/boost/python/overloads.hpp"
49 
50 #include <algorithm>
51 #include <numeric>
52 #include <ostream>
53 #include <string>
54 #include <memory>
55 #include <vector>
56 
58 
59 namespace Vt_WrapArray {
60 
61 using namespace pxr_boost::python;
62 
63 using std::unique_ptr;
64 using std::vector;
65 using std::string;
66 
67 template <typename T>
68 object
69 getitem_ellipsis(VtArray<T> const &self, object idx)
70 {
71  object ellipsis = object(handle<>(borrowed(Py_Ellipsis)));
72  if (idx != ellipsis) {
73  PyErr_SetString(PyExc_TypeError, "unsupported index type");
74  throw_error_already_set();
75  }
76  return object(self);
77 }
78 
79 template <typename T>
80 object
81 getitem_index(VtArray<T> const &self, int64_t idx)
82 {
83  static const bool throwError = true;
84  idx = TfPyNormalizeIndex(idx, self.size(), throwError);
85  return object(self[idx]);
86 }
87 
88 template <typename T>
89 object
90 getitem_slice(VtArray<T> const &self, slice idx)
91 {
92  try {
93  slice::range<typename VtArray<T>::const_iterator> range =
94  idx.get_indices(self.begin(), self.end());
95  const size_t setSize = 1 + (range.stop - range.start) / range.step;
96  VtArray<T> result(setSize);
97  size_t i = 0;
98  for (; range.start != range.stop; range.start += range.step, ++i) {
99  result[i] = *range.start;
100  }
101  result[i] = *range.start;
102  return object(result);
103  }
104  catch (std::invalid_argument const &) {
105  return object();
106  }
107 }
108 
109 template <typename T, typename S>
110 void
112  slice::range<T*>& range, size_t setSize, bool tile = false)
113 {
114  // Check size.
115  const size_t length = len(value);
116  if (length == 0)
117  TfPyThrowValueError("No values with which to set array slice.");
118  if (!tile && length < setSize) {
119  string msg = TfStringPrintf
120  ("Not enough values to set slice. Expected %zu, got %zu.",
121  setSize, length);
122  TfPyThrowValueError(msg);
123  }
124 
125  // Extract the values before setting any. If we can extract the
126  // whole vector at once then do that since it should be faster.
127  std::vector<T> extracted;
128  extract<std::vector<T> > vectorExtraction(value);
129  if (vectorExtraction.check()) {
130  std::vector<T> tmp = vectorExtraction();
131  extracted.swap(tmp);
132  }
133  else {
134  extracted.reserve(length);
135  for (size_t i = 0; i != length; ++i) {
136  extracted.push_back(extract<T>(value[i]));
137  }
138  }
139 
140  // We're fine, go through and set them. Handle common case as a fast
141  // path.
142  if (range.step == 1 && length >= setSize) {
143  std::copy(extracted.begin(), extracted.begin() + setSize, range.start);
144  }
145  else {
146  for (size_t i = 0; i != setSize; range.start += range.step, ++i) {
147  *range.start = extracted[i % length];
148  }
149  }
150 }
151 
152 template <typename T>
153 void
154 setArraySlice(VtArray<T> &self, slice idx, object value, bool tile = false)
155 {
156  // Get the range.
157  slice::range<T*> range;
158  try {
159  T* data = self.data();
160  range = idx.get_indices(data, data + self.size());
161  }
162  catch (std::invalid_argument const &) {
163  // Do nothing
164  return;
165  }
166 
167  // Get the number of items to be set.
168  const size_t setSize = 1 + (range.stop - range.start) / range.step;
169 
170  // Copy from VtArray. We only want to take this path if the passed value is
171  // *exactly* a VtArray. That is, we don't want to take this path if it can
172  // merely *convert* to a VtArray, so we check that we can extract a mutable
173  // lvalue reference from the python object, which requires that there be a
174  // real VtArray there.
175  if (extract< VtArray<T> &>(value).check()) {
176  const VtArray<T> val = extract< VtArray<T> >(value);
177  const size_t length = val.size();
178  if (length == 0)
179  TfPyThrowValueError("No values with which to set array slice.");
180  if (!tile && length < setSize) {
181  string msg = TfStringPrintf
182  ("Not enough values to set slice. Expected %zu, got %zu.",
183  setSize, length);
184  TfPyThrowValueError(msg);
185  }
186 
187  // We're fine, go through and set them.
188  for (size_t i = 0; i != setSize; range.start += range.step, ++i) {
189  *range.start = val[i % length];
190  }
191  }
192 
193  // Copy from scalar.
194  else if (extract<T>(value).check()) {
195  if (!tile) {
196  // XXX -- We're allowing implicit tiling; do we want to?
197  //TfPyThrowValueError("can only assign an iterable.");
198  }
199 
200  // Use scalar to fill entire slice.
201  const T val = extract<T>(value);
202  for (size_t i = 0; i != setSize; range.start += range.step, ++i) {
203  *range.start = val;
204  }
205  }
206 
207  // Copy from list.
208  else if (extract<list>(value).check()) {
209  setArraySlice(self, extract<list>(value)(), range, setSize, tile);
210  }
211 
212  // Copy from tuple.
213  else if (extract<tuple>(value).check()) {
214  setArraySlice(self, extract<tuple>(value)(), range, setSize, tile);
215  }
216 
217  // Copy from iterable.
218  else {
219  setArraySlice(self, list(value), range, setSize, tile);
220  }
221 }
222 
223 
224 template <typename T>
225 void
226 setitem_ellipsis(VtArray<T> &self, object idx, object value)
227 {
228  object ellipsis = object(handle<>(borrowed(Py_Ellipsis)));
229  if (idx != ellipsis) {
230  PyErr_SetString(PyExc_TypeError, "unsupported index type");
231  throw_error_already_set();
232  }
233  setArraySlice(self, slice(0, self.size()), value);
234 }
235 
236 template <typename T>
237 void
238 setitem_index(VtArray<T> &self, int64_t idx, object value)
239 {
240  idx = TfPyNormalizeIndex(idx, self.size(), /*throwError=*/true);
241  setArraySlice(self, slice(idx, idx+1), value, /*tile=*/true);
242 }
243 
244 template <typename T>
245 void
246 setitem_slice(VtArray<T> &self, slice idx, object value)
247 {
248  setArraySlice(self, idx, value);
249 }
250 
251 
252 template <class T>
253 VT_API string GetVtArrayName();
254 
255 template <class T, class... Ts>
257  return (std::is_same_v<T, Ts> || ...);
258 }
259 
260 template <class T, class TypeList>
261 constexpr bool Vt_IsAnySame() {
262  return Vt_IsAnySameImpl<T>(TypeList{});
263 }
264 
265 // This is the same types as in VT_INTEGRAL_BUILTIN_VALUE_TYPES with char
266 // and bool types removed.
268  TfMetaList<short, unsigned short,
269  int, unsigned int,
270  long, unsigned long,
271  long long, unsigned long long>;
272 
273 // Explicitly convert half to float here instead of relying on implicit
274 // conversion to float to work around the fact that libc++ only provides
275 // implementations of std::isfinite for types where std::is_arithmetic
276 // is true.
277 template <typename T>
278 inline bool _IsFinite(T const &value) {
279  return std::isfinite(value);
280 }
281 inline bool _IsFinite(GfHalf const &value) {
282  return std::isfinite(static_cast<float>(value));
283 }
284 
285 template <typename T>
286 static void streamValue(std::ostringstream &stream, T const &value) {
287  // To avoid overhead we stream out certain builtin types directly
288  // without calling TfPyRepr().
289  if constexpr(Vt_IsAnySame<T, Vt_OptimizedStreamIntegralTypes>()) {
290  stream << value;
291  }
292  // For float types we need to be make sure to represent infs and nans correctly.
293  else if constexpr(GfIsFloatingPoint<T>::value) {
294  if (_IsFinite(value)) {
295  stream << value;
296  }
297  else {
298  stream << TfPyRepr(value);
299  }
300  }
301  else {
302  stream << TfPyRepr(value);
303  }
304 }
305 
306 VT_API
307 unsigned int
309  Vt_ShapeData const *sd, size_t *lastDimSize);
310 
311 template <typename T>
312 string __repr__(VtArray<T> const &self)
313 {
314  if (self.empty())
315  return TF_PY_REPR_PREFIX +
317 
318  std::ostringstream stream;
319  stream.precision(17);
320  stream << "(";
321  for (size_t i = 0; i < self.size(); ++i) {
322  stream << (i ? ", " : "");
323  streamValue(stream, self[i]);
324  }
325  stream << (self.size() == 1 ? ",)" : ")");
326 
327  const std::string repr = TF_PY_REPR_PREFIX +
328  TfStringPrintf("%s(%zd, %s)",
330  self.size(), stream.str().c_str());
331 
332  // XXX: This is to deal with legacy shaped arrays and should be removed
333  // once all shaped arrays have been eliminated.
334  // There is no nice way to make an eval()able __repr__ for shaped arrays
335  // that preserves the shape information, so put it in <> to make it
336  // clearly not eval()able. That has the advantage that, if somebody passes
337  // the repr into eval(), it'll raise a SyntaxError that clearly points to
338  // the beginning of the __repr__.
339  Vt_ShapeData const *shapeData = self._GetShapeData();
340  size_t lastDimSize = 0;
341  unsigned int rank =
342  Vt_ComputeEffectiveRankAndLastDimSize(shapeData, &lastDimSize);
343  if (rank > 1) {
344  std::string shapeStr = "(";
345  for (size_t i = 0; i != rank-1; ++i) {
346  shapeStr += TfStringPrintf(
347  i ? ", %d" : "%d", shapeData->otherDims[i]);
348  }
349  shapeStr += TfStringPrintf(", %zu)", lastDimSize);
350  return TfStringPrintf("<%s with shape %s>",
351  repr.c_str(), shapeStr.c_str());
352  }
353 
354  return repr;
355 }
356 
357 template <typename T>
359 {
360  // Make an array.
361  unique_ptr<VtArray<T> > ret(new VtArray<T>(len(values)));
362 
363  // Set the values. This is equivalent to saying 'ret[...] = values'
364  // in python, except that we allow tiling here.
365  static const bool tile = true;
366  setArraySlice(*ret, slice(0, ret->size()), values, tile);
367  return ret.release();
368 }
369 template <typename T>
370 VtArray<T> *VtArray__init__2(size_t size, object const &values)
371 {
372  // Make the array.
373  unique_ptr<VtArray<T> > ret(new VtArray<T>(size));
374 
375  // Set the values. This is equivalent to saying 'ret[...] = values'
376  // in python, except that we allow tiling here.
377  static const bool tile = true;
378  setArraySlice(*ret, slice(0, ret->size()), values, tile);
379 
380  return ret.release();
381 }
382 
383 // overloading for operator special methods, to allow tuple / list & array
384 // combinations
388 
389 VTOPERATOR_WRAP(__add__,__radd__)
390 VTOPERATOR_WRAP_NONCOMM(__sub__,__rsub__)
391 VTOPERATOR_WRAP(__mul__,__rmul__)
392 VTOPERATOR_WRAP_NONCOMM(__div__,__rdiv__)
393 VTOPERATOR_WRAP_NONCOMM(__mod__,__rmod__)
394 
396 }
397 
398 template <typename T>
399 static std::string _VtStr(T const &self)
400 {
401  return TfStringify(self);
402 }
403 
404 template <typename T>
406 {
407  using namespace Vt_WrapArray;
408 
409  typedef T This;
410  typedef typename This::ElementType Type;
411 
412  string name = GetVtArrayName<This>();
413  string typeStr = ArchGetDemangled(typeid(Type));
414  string docStr = TfStringPrintf("An array of type %s.", typeStr.c_str());
415 
416  auto selfCls = class_<This>(name.c_str(), docStr.c_str(), no_init)
417  .setattr("_isVtArray", true)
418  .def(TfTypePythonClass())
419  .def(init<>())
420  .def("__init__", make_constructor(VtArray__init__<Type>),
421  (const char *)
422  "__init__(values)\n\n"
423  "values: a sequence (tuple, list, or another VtArray with "
424  "element type convertible to the new array's element type)\n\n"
425  )
426  .def("__init__", make_constructor(VtArray__init__2<Type>))
427  .def(init<unsigned int>())
428 
429  .def("__getitem__", getitem_ellipsis<Type>)
430  .def("__getitem__", getitem_slice<Type>)
431  .def("__getitem__", getitem_index<Type>)
432  .def("__setitem__", setitem_ellipsis<Type>)
433  .def("__setitem__", setitem_slice<Type>)
434  .def("__setitem__", setitem_index<Type>)
435 
436  .def("__len__", &This::size)
437  .def("__iter__", iterator<This>())
438 
439  .def("__repr__", __repr__<Type>)
440 
441 // .def(str(self))
442  .def("__str__", _VtStr<T>)
443  .def(self == self)
444  .def(self != self)
445 
446 #ifdef NUMERIC_OPERATORS
447 #define ADDITION_OPERATOR
448 #define SUBTRACTION_OPERATOR
449 #define MULTIPLICATION_OPERATOR
450 #define DIVISION_OPERATOR
451 #define UNARY_NEG_OPERATOR
452 #endif
453 
454 #ifdef ADDITION_OPERATOR
455  VTOPERATOR_WRAPDECLARE(+,__add__,__radd__)
456 #endif
457 #ifdef SUBTRACTION_OPERATOR
458  VTOPERATOR_WRAPDECLARE(-,__sub__,__rsub__)
459 #endif
460 #ifdef MULTIPLICATION_OPERATOR
461  VTOPERATOR_WRAPDECLARE(*,__mul__,__rmul__)
462 #endif
463 #ifdef DIVISION_OPERATOR
464  VTOPERATOR_WRAPDECLARE(/,__div__,__rdiv__)
465 #endif
466 #ifdef MOD_OPERATOR
467  VTOPERATOR_WRAPDECLARE(%,__mod__,__rmod__)
468 #endif
469 #ifdef DOUBLE_MULT_OPERATOR
470  .def(self * double())
471  .def(double() * self)
472 #endif
473 #ifdef DOUBLE_DIV_OPERATOR
474  .def(self / double())
475 #endif
476 #ifdef UNARY_NEG_OPERATOR
477  .def(- self)
478 #endif
479 
480  ;
481 
482  // Wrap conversions from python sequences.
484  This,
487 
488  // Wrap implicit conversions from VtArray to TfSpan.
489  implicitly_convertible<This, TfSpan<Type> >();
490  implicitly_convertible<This, TfSpan<const Type> >();
491 }
492 
493 template <class Array>
494 VtValue
496 {
497  typedef typename Array::ElementType ElemType;
498  TfPyLock lock;
499  if (PySequence_Check(obj.ptr())) {
500  Py_ssize_t len = PySequence_Length(obj.ptr());
501  Array result(len);
502  ElemType *elem = result.data();
503  for (Py_ssize_t i = 0; i != len; ++i) {
504  pxr_boost::python::handle<> h(PySequence_ITEM(obj.ptr(), i));
505  if (!h) {
506  if (PyErr_Occurred())
507  PyErr_Clear();
508  return VtValue();
509  }
510  pxr_boost::python::extract<ElemType> e(h.get());
511  if (!e.check())
512  return VtValue();
513  *elem++ = e();
514  }
515  return VtValue(result);
516  } else if (PyIter_Check(obj.ptr())) {
517  Array result;
518  while (PyObject *item = PyIter_Next(obj.ptr())) {
519  pxr_boost::python::handle<> h(item);
520  if (!h) {
521  if (PyErr_Occurred())
522  PyErr_Clear();
523  return VtValue();
524  }
525  pxr_boost::python::extract<ElemType> e(h.get());
526  if (!e.check())
527  return VtValue();
528  result.push_back(e());
529  }
530  return VtValue(result);
531  }
532  return VtValue();
533 }
534 
535 template <class Array, class Iter>
536 VtValue
538 {
539  typedef typename Array::ElementType ElemType;
540  Array result(distance(begin, end));
541  for (ElemType *e = result.data(); begin != end; ++begin) {
542  VtValue cast = VtValue::Cast<ElemType>(*begin);
543  if (cast.IsEmpty())
544  return cast;
545  cast.Swap(*e++);
546  }
547  return VtValue(result);
548 }
549 
550 template <class T>
551 VtValue
553  VtValue ret;
554  TfPyObjWrapper obj;
555  // Attempt to convert from either python sequence or vector<VtValue>.
556  if (v.IsHolding<TfPyObjWrapper>()) {
557  ret = Vt_ConvertFromPySequenceOrIter<T>(v.UncheckedGet<TfPyObjWrapper>());
558  } else if (v.IsHolding<std::vector<VtValue> >()) {
559  std::vector<VtValue> const &vec = v.UncheckedGet<std::vector<VtValue> >();
560  ret = Vt_ConvertFromRange<T>(vec.begin(), vec.end());
561  }
562  return ret;
563 }
564 
565 /// Register casts with VtValue from python sequences to VtArray types.
566 template <class Elem>
568 {
569  typedef VtArray<Elem> Array;
570  VtValue::RegisterCast<TfPyObjWrapper, Array>(Vt_CastToArray<Array>);
571  VtValue::RegisterCast<std::vector<VtValue>, Array>(Vt_CastToArray<Array>);
572 }
573 
574 #define VT_WRAP_ARRAY(unused, elem) \
575  VtWrapArray< VtArray< VT_TYPE(elem) > >();
576 
578 
579 #endif // PXR_BASE_VT_WRAP_ARRAY_H
GLuint GLuint stream
Definition: glcorearb.h:1832
TF_API std::string TfStringPrintf(const char *fmt,...)
typedef int(APIENTRYP RE_PFNGLXSWAPINTERVALSGIPROC)(int)
Definition: ImfArray.h:45
#define VTOPERATOR_WRAPDECLARE(op, lmethod, rmethod)
Definition: pyOperators.h:103
GLenum GLint * range
Definition: glcorearb.h:1925
T const & UncheckedGet() const &
Definition: value.h:1104
OIIO_UTIL_API bool copy(string_view from, string_view to, std::string &err)
const GLdouble * v
Definition: glcorearb.h:837
GLsizei const GLfloat * value
Definition: glcorearb.h:824
#define ARCH_PRAGMA_POP
Definition: pragmas.h:159
ARCH_API std::string ArchGetDemangled(const std::string &typeName)
#define VT_API
Definition: api.h:23
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
string __repr__(VtArray< T > const &self)
Definition: wrapArray.h:312
#define ARCH_PRAGMA_UNSAFE_USE_OF_BOOL
Definition: pragmas.h:239
VtValue & Swap(VtValue &rhs) noexcept
Swap this with rhs.
Definition: value.h:955
VT_API unsigned int Vt_ComputeEffectiveRankAndLastDimSize(Vt_ShapeData const *sd, size_t *lastDimSize)
TF_API void TfPyThrowValueError(const char *msg)
**But if you need a result
Definition: thread.h:622
bool IsEmpty() const
Returns true iff this value is empty.
Definition: value.h:1285
bool _IsFinite(T const &value)
Definition: wrapArray.h:278
object getitem_index(VtArray< T > const &self, int64_t idx)
Definition: wrapArray.h:81
VtValue Vt_ConvertFromRange(Iter begin, Iter end)
Definition: wrapArray.h:537
constexpr bool Vt_IsAnySame()
Definition: wrapArray.h:261
VtValue Vt_CastToArray(VtValue const &v)
Definition: wrapArray.h:552
std::string TfPyRepr(T const &t)
Definition: pyUtils.h:163
OIIO_FORCEINLINE bool extract(const vbool4 &a)
Definition: simd.h:3542
void setitem_slice(VtArray< T > &self, slice idx, object value)
Definition: wrapArray.h:246
SYS_FORCE_INLINE const X * cast(const InstancablePtr *o)
object getitem_slice(VtArray< T > const &self, slice idx)
Definition: wrapArray.h:90
#define ARCH_PRAGMA_PUSH
Definition: pragmas.h:155
OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_ENTER class IMF_EXPORT_TEMPLATE_TYPE Array
Definition: ImfForward.h:21
TfMetaList< short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long > Vt_OptimizedStreamIntegralTypes
Definition: wrapArray.h:271
GLuint GLuint end
Definition: glcorearb.h:475
VtArray< T > * VtArray__init__(object const &values)
Definition: wrapArray.h:358
std::string TfStringify(const T &v)
Definition: stringUtils.h:556
void setArraySlice(VtArray< T > &self, S value, slice::range< T * > &range, size_t setSize, bool tile=false)
Definition: wrapArray.h:111
void setitem_ellipsis(VtArray< T > &self, object idx, object value)
Definition: wrapArray.h:226
GLuint const GLchar * name
Definition: glcorearb.h:786
Definition: types.h:153
VT_API string GetVtArrayName()
#define ARCH_PRAGMA_UNARY_MINUS_ON_UNSIGNED
Definition: pragmas.h:243
void VtRegisterValueCastsFromPythonSequencesToArray()
Register casts with VtValue from python sequences to VtArray types.
Definition: wrapArray.h:567
VtArray< T > * VtArray__init__2(size_t size, object const &values)
Definition: wrapArray.h:370
GLsizeiptr size
Definition: glcorearb.h:664
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:2002
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
GLenum GLsizei GLsizei GLint * values
Definition: glcorearb.h:1602
VtValue Vt_ConvertFromPySequenceOrIter(TfPyObjWrapper const &obj)
Definition: wrapArray.h:495
object getitem_ellipsis(VtArray< T > const &self, object idx)
Definition: wrapArray.h:69
void setitem_index(VtArray< T > &self, int64_t idx, object value)
Definition: wrapArray.h:238
bool IsHolding() const
Definition: value.h:1064
GLuint GLfloat * val
Definition: glcorearb.h:1608
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
if(num_boxed_items<=0)
Definition: UT_RTreeImpl.h:697
OIIO_UTIL_API const char * c_str(string_view str)
TF_API int64_t TfPyNormalizeIndex(int64_t index, uint64_t size, bool throwError=false)
#define TF_PY_REPR_PREFIX
Definition: pyUtils.h:42
constexpr bool Vt_IsAnySameImpl(TfMetaList< Ts...>)
Definition: wrapArray.h:256
SIM_API const UT_StringHolder distance
#define VTOPERATOR_WRAP(lmethod, rmethod)
Definition: pyOperators.h:82
Definition: value.h:146
Definition: format.h:1821
void VtWrapArray()
Definition: wrapArray.h:405
#define VTOPERATOR_WRAP_NONCOMM(lmethod, rmethod)
Definition: pyOperators.h:89
PcpNodeRef_ChildrenIterator begin(const PcpNodeRef::child_const_range &r)
Support for range-based for loops for PcpNodeRef children ranges.
Definition: node.h:566