Loading...
Searching...
No Matches
array.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_ARRAY_H
8#define PXR_BASE_VT_ARRAY_H
9
11
12#include "pxr/pxr.h"
13#include "pxr/base/vt/api.h"
14#include "pxr/base/vt/hash.h"
15#include "pxr/base/vt/streamOut.h"
16#include "pxr/base/vt/traits.h"
17#include "pxr/base/vt/types.h"
18
23#include "pxr/base/tf/preprocessorUtilsLite.h"
24
25#include <algorithm>
26#include <atomic>
27#include <cstddef>
28#include <cstdlib>
29#include <iterator>
30#include <limits>
31#include <memory>
32#include <new>
33#include <type_traits>
34
35PXR_NAMESPACE_OPEN_SCOPE
36
37// Helper class for clients that create VtArrays referring to foreign-owned
38// data.
39class Vt_ArrayForeignDataSource
40{
41public:
42 explicit Vt_ArrayForeignDataSource(
43 void (*detachedFn)(Vt_ArrayForeignDataSource *self) = nullptr,
44 size_t initRefCount = 0)
45 : _refCount(initRefCount)
46 , _detachedFn(detachedFn) {}
47
48private:
49 template <class T> friend class VtArray;
50 // Invoked when no more arrays share this data source.
51 void _ArraysDetached() { if (_detachedFn) { _detachedFn(this); } }
52protected:
53 std::atomic<size_t> _refCount;
54 void (*_detachedFn)(Vt_ArrayForeignDataSource *self);
55};
56
57// Private base class helper for VtArray implementation.
58class Vt_ArrayBase
59{
60public:
61 Vt_ArrayBase() : _shapeData { 0 }, _foreignSource(nullptr) {}
62
63 Vt_ArrayBase(Vt_ArrayForeignDataSource *foreignSrc)
64 : _shapeData { 0 }, _foreignSource(foreignSrc) {}
65
66 Vt_ArrayBase(Vt_ArrayBase const &other) = default;
67 Vt_ArrayBase(Vt_ArrayBase &&other) : Vt_ArrayBase(other) {
68 other._shapeData.clear();
69 other._foreignSource = nullptr;
70 }
71
72 Vt_ArrayBase &operator=(Vt_ArrayBase const &other) = default;
73 Vt_ArrayBase &operator=(Vt_ArrayBase &&other) {
74 if (this == &other)
75 return *this;
76 *this = other;
77 other._shapeData.clear();
78 other._foreignSource = nullptr;
79 return *this;
80 }
81
82protected:
83 // Control block header for native data representation. Houses refcount and
84 // capacity. For arrays with native data, this structure always lives
85 // immediately preceding the start of the array's _data in memory. See
86 // _GetControlBlock() for details.
87 struct _ControlBlock {
88 _ControlBlock() : nativeRefCount(0), capacity(0) {}
89 _ControlBlock(size_t initCount, size_t initCap)
90 : nativeRefCount(initCount), capacity(initCap) {}
91 mutable std::atomic<size_t> nativeRefCount;
92 size_t capacity;
93 };
94
95 _ControlBlock &_GetControlBlock(void *nativeData) {
96 TF_DEV_AXIOM(!_foreignSource);
97 return *(reinterpret_cast<_ControlBlock *>(nativeData) - 1);
98 }
99
100 _ControlBlock const &_GetControlBlock(void *nativeData) const {
101 TF_DEV_AXIOM(!_foreignSource);
102 return *(reinterpret_cast<_ControlBlock *>(nativeData) - 1);
103 }
104
105 // Mutable ref count, as is standard.
106 std::atomic<size_t> &_GetNativeRefCount(void *nativeData) const {
107 return _GetControlBlock(nativeData).nativeRefCount;
108 }
109
110 size_t &_GetCapacity(void *nativeData) {
111 return _GetControlBlock(nativeData).capacity;
112 }
113 size_t const &_GetCapacity(void *nativeData) const {
114 return _GetControlBlock(nativeData).capacity;
115 }
116
117 VT_API void _DetachCopyHook(char const *funcName) const;
118
119 Vt_ShapeData _shapeData;
120 Vt_ArrayForeignDataSource *_foreignSource;
121};
122
210template<typename ELEM>
211class VtArray : public Vt_ArrayBase {
212 public:
213
215 typedef ELEM ElementType;
216 typedef ELEM value_type;
217
220
225
227 typedef std::reverse_iterator<iterator> reverse_iterator;
229 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
230
239
241
243 VtArray() : _data(nullptr) {}
244
257 template <typename LegacyInputIterator>
258 VtArray(LegacyInputIterator first, LegacyInputIterator last,
259 typename std::enable_if<
260 !std::is_integral<LegacyInputIterator>::value,
261 void>::type* = nullptr)
262 : VtArray() {
263 assign(first, last);
264 }
265
267 VtArray(Vt_ArrayForeignDataSource *foreignSrc,
268 ElementType *data, size_t size, bool addRef = true)
269 : Vt_ArrayBase(foreignSrc)
270 , _data(data) {
271 if (addRef) {
272 foreignSrc->_refCount.fetch_add(1, std::memory_order_relaxed);
273 }
274 _shapeData.totalSize = size;
275 }
276
278 VtArray(VtArray const &other) : Vt_ArrayBase(other)
279 , _data(other._data) {
280 if (!_data)
281 return;
282
283 if (ARCH_LIKELY(!_foreignSource)) {
284 _GetNativeRefCount(_data).fetch_add(1, std::memory_order_relaxed);
285 }
286 else {
287 _foreignSource->_refCount.fetch_add(1, std::memory_order_relaxed);
288 }
289 }
290
293 VtArray(VtArray &&other) : Vt_ArrayBase(std::move(other))
294 , _data(other._data) {
295 other._data = nullptr;
296 }
297
299 VtArray(std::initializer_list<ELEM> initializerList)
300 : VtArray() {
301 assign(initializerList);
302 }
303
305 explicit VtArray(size_t n)
306 : VtArray() {
307 assign(n, value_type());
308 }
309
311 explicit VtArray(size_t n, value_type const &value)
312 : VtArray() {
313 assign(n, value);
314 }
315
318 VtArray &operator=(VtArray const &other) {
319 // This might look recursive but it's really invoking move-assign, since
320 // we create a temporary copy (an rvalue).
321 if (this != &other)
322 *this = VtArray(other);
323 return *this;
324 }
325
329 if (this == &other)
330 return *this;
331 _DecRef();
332 static_cast<Vt_ArrayBase &>(*this) = std::move(other);
333 _data = other._data;
334 other._data = nullptr;
335 return *this;
336 }
337
339 VtArray &operator=(std::initializer_list<ELEM> initializerList) {
340 this->assign(initializerList.begin(), initializerList.end());
341 return *this;
342 }
343
344 ~VtArray() { _DecRef(); }
345
354 VtArray const &AsConst() const noexcept {
355 return *this;
356 }
357
360
363 iterator begin() { return iterator(data()); }
366 iterator end() { return iterator(data() + size()); }
367
371 const_iterator end() const { return const_iterator(data() + size()); }
372
374 const_iterator cbegin() const { return begin(); }
376 const_iterator cend() const { return end(); }
377
384
387 return const_reverse_iterator(end());
388 }
392 }
393
397 const_reverse_iterator crend() const { return rend(); }
398
401 pointer data() { _DetachIfNotUnique(); return _data; }
403 const_pointer data() const { return _data; }
405 const_pointer cdata() const { return _data; }
406
412 template <typename... Args>
413 void emplace_back(Args&&... args) {
414 // If this is a non-pxr array with rank > 1, disallow push_back.
415 if (ARCH_UNLIKELY(_shapeData.otherDims[0])) {
416 TF_CODING_ERROR("Array rank %u != 1", _shapeData.GetRank());
417 return;
418 }
419 // If we don't own the data, or if we need more space, realloc.
420 size_t curSize = size();
421 if (ARCH_UNLIKELY(
422 _foreignSource || !_IsUnique() || curSize == capacity())) {
423 value_type *newData = _AllocateCopy(
424 _data, _CapacityForSize(curSize + 1), curSize);
425 ::new (static_cast<void*>(newData + curSize)) value_type(
426 std::forward<Args>(args)...);
427 _DecRef();
428 _data = newData;
429 }
430 else {
431 ::new (static_cast<void*>(_data + curSize)) value_type(
432 std::forward<Args>(args)...);
433 }
434 // Adjust size.
435 ++_shapeData.totalSize;
436 }
437
443 void push_back(ElementType const& element) {
444 emplace_back(element);
445 }
446
452 void push_back(ElementType&& element) {
453 emplace_back(std::move(element));
454 }
455
458 void pop_back() {
459 // If this is a presto array with rank > 1, disallow push_back.
460 if (ARCH_UNLIKELY(_shapeData.otherDims[0])) {
461 TF_CODING_ERROR("Array rank %u != 1", _shapeData.GetRank());
462 return;
463 }
464 _DetachIfNotUnique();
465 // Invoke the destructor.
466 (_data + size() - 1)->~value_type();
467 // Adjust size.
468 --_shapeData.totalSize;
469 }
470
472 size_t size() const { return _shapeData.totalSize; }
473
478 size_t capacity() const {
479 if (!_data) {
480 return 0;
481 }
482 // We do not allow mutation to foreign source data, so always report
483 // foreign sourced arrays as at capacity.
484 return ARCH_UNLIKELY(_foreignSource) ? size() : _GetCapacity(_data);
485 }
486
490 constexpr size_t max_size() const {
491 // The number of value_type elements that can be fit into maximum size_t
492 // bytes minus the size of _ControlBlock.
493 return (std::numeric_limits<size_t>::max() - sizeof(_ControlBlock))
494 / sizeof(value_type);
495 }
496
498 bool empty() const { return size() == 0; }
499
503 void reserve(size_t num) {
504 if (num <= capacity())
505 return;
506
507 value_type *newData =
508 _data ? _AllocateCopy(_data, num, size()) : _AllocateNew(num);
509
510 _DecRef();
511 _data = newData;
512 }
513
517 reference front() { return *begin(); }
520 const_reference front() const { return *begin(); }
523 const_reference cfront() const { return *begin(); }
524
528 reference back() { return *rbegin(); }
531 const_reference back() const { return *rbegin(); }
534 const_reference cback() const { return *rbegin(); }
535
541 void resize(size_t newSize) {
542 return resize(newSize, value_type());
543 }
544
547 void resize(size_t newSize, value_type const &value) {
548 return resize(newSize,
549 [&value](pointer b, pointer e) {
550 std::uninitialized_fill(b, e, value);
551 });
552 }
553
556 void resize(size_t newSize, value_type &value) {
557 return resize(newSize, const_cast<value_type const &>(value));
558 }
559
562 void resize(size_t newSize, value_type &&value) {
563 return resize(newSize, const_cast<value_type const &>(value));
564 }
565
570 template <class FillElemsFn>
571 void resize(size_t newSize, FillElemsFn &&fillElems) {
572 const size_t oldSize = size();
573 if (oldSize == newSize) {
574 return;
575 }
576 if (newSize == 0) {
577 clear();
578 return;
579 }
580
581 const bool growing = newSize > oldSize;
582 value_type *newData = _data;
583
584 if (!_data) {
585 // Allocate newSize elements and initialize.
586 newData = _AllocateNew(newSize);
587 std::forward<FillElemsFn>(fillElems)(newData, newData + newSize);
588 }
589 else if (_IsUnique()) {
590 if (growing) {
591 if (newSize > _GetCapacity(_data)) {
592 newData = _AllocateCopy(_data, newSize, oldSize);
593 }
594 // fill with newly added elements from oldSize to newSize.
595 std::forward<FillElemsFn>(fillElems)(newData + oldSize,
596 newData + newSize);
597 }
598 else {
599 // destroy removed elements
600 for (auto *cur = newData + newSize,
601 *end = newData + oldSize; cur != end; ++cur) {
602 cur->~value_type();
603 }
604 }
605 }
606 else {
607 newData =
608 _AllocateCopy(_data, newSize, growing ? oldSize : newSize);
609 if (growing) {
610 // fill with newly added elements from oldSize to newSize.
611 std::forward<FillElemsFn>(fillElems)(newData + oldSize,
612 newData + newSize);
613 }
614 }
615
616 // If we created new data, clean up the old and move over to the new.
617 if (newData != _data) {
618 _DecRef();
619 _data = newData;
620 }
621 // Adjust size.
622 _shapeData.totalSize = newSize;
623 }
624
626 void clear() {
627 if (!_data)
628 return;
629 if (_IsUnique()) {
630 // Clear out elements, run dtors, keep capacity.
631 for (value_type *p = _data, *e = _data + size(); p != e; ++p) {
632 p->~value_type();
633 }
634 }
635 else {
636 // Detach to empty.
637 _DecRef();
638 }
639 _shapeData.totalSize = 0;
640 }
641
655 TF_DEV_AXIOM(pos != cend());
656 return erase(pos, pos+1);
657 }
658
675 if (first == last){
676 return std::next(begin(), std::distance(cbegin(), last));
677 }
678 if ((first == cbegin()) && (last == cend())){
679 clear();
680 return end();
681 }
682 // Given the previous two conditions, we know that we are removing
683 // at least one element and the result array will contain at least one
684 // element.
685 value_type* removeStart = std::next(_data, std::distance(cbegin(), first));
686 value_type* removeEnd = std::next(_data, std::distance(cbegin(), last));
687 value_type* endIt = std::next(_data, size());
688 size_t newSize = size() - std::distance(first, last);
689 if (_IsUnique()){
690 // If the array is unique, we can simply move the tail elements
691 // and free to the end of the array.
692 value_type* deleteIt = std::move(removeEnd, endIt, removeStart);
693 for (; deleteIt != endIt; ++deleteIt) {
694 deleteIt->~value_type();
695 }
696 _shapeData.totalSize = newSize;
697 return iterator(removeStart);
698 } else{
699 // If the array is not unique, we want to avoid copying the
700 // elements in the range we are erasing. We allocate a
701 // new buffer and copy the head and tail ranges, omitting
702 // [first, last)
703 value_type* newData = _AllocateNew(newSize);
704 value_type* newMiddle = std::uninitialized_copy(
705 _data, removeStart, newData);
706 value_type* newEnd = std::uninitialized_copy(
707 removeEnd, endIt, newMiddle);
708 TF_DEV_AXIOM(newEnd == std::next(newData, newSize));
709 TF_DEV_AXIOM(std::distance(newData, newMiddle) ==
710 std::distance(_data, removeStart));
711 _DecRef();
712 _data = newData;
713 _shapeData.totalSize = newSize;
714 return iterator(newMiddle);
715 }
716 }
717
724 template <class ForwardIter>
725 typename std::enable_if<!std::is_integral<ForwardIter>::value>::type
726 assign(ForwardIter first, ForwardIter last) {
727 struct _Copier {
728 void operator()(pointer b, pointer e) const {
729 std::uninitialized_copy(first, last, b);
730 }
731 ForwardIter const &first, &last;
732 };
733 clear();
734 resize(std::distance(first, last), _Copier { first, last });
735 }
736
743 void assign(size_t n, const value_type &fill) {
744 struct _Filler {
745 void operator()(pointer b, pointer e) const {
746 std::uninitialized_fill(b, e, fill);
747 }
748 const value_type &fill;
749 };
750 clear();
751 resize(n, _Filler { fill });
752 }
753
759 void assign(std::initializer_list<ELEM> initializerList) {
760 assign(initializerList.begin(), initializerList.end());
761 }
762
764 void swap(VtArray &other) {
765 std::swap(_data, other._data);
766 std::swap(_shapeData, other._shapeData);
767 std::swap(_foreignSource, other._foreignSource);
768 }
769
771
773 ElementType &operator[](size_t index) {
774 return data()[index];
775 }
776
778 ElementType const &operator[](size_t index) const {
779 return data()[index];
780 }
781
784 bool IsIdentical(VtArray const & other) const {
785 return
786 _data == other._data &&
787 _shapeData == other._shapeData &&
788 _foreignSource == other._foreignSource;
789 }
790
792 bool operator == (VtArray const & other) const {
793 return IsIdentical(other) ||
794 (*_GetShapeData() == *other._GetShapeData() &&
795 std::equal(cbegin(), cend(), other.cbegin()));
796 }
797
799 bool operator != (VtArray const &other) const {
800 return !(*this == other);
801 }
802
803 public:
804 // XXX -- Public so VtValue::_ArrayHelper<T,U>::GetShapeData() has access.
805 Vt_ShapeData const *_GetShapeData() const {
806 return &_shapeData;
807 }
808 Vt_ShapeData *_GetShapeData() {
809 return &_shapeData;
810 }
811
812 private:
813 class _Streamer {
814 public:
815 explicit _Streamer(const_pointer data) : _p(data) { }
816 void operator()(std::ostream &out) const {
817 VtStreamOut(*_p++, out);
818 }
819
820 private:
821 mutable const_pointer _p;
822 };
823
825 friend std::ostream &operator <<(std::ostream &out, const VtArray &self) {
826 VtArray::_Streamer streamer(self.cdata());
827 VtStreamOutArray(out, self._GetShapeData(), streamer);
828 return out;
829 }
830
832 friend void swap(VtArray &lhs, VtArray &rhs) {
833 lhs.swap(rhs);
834 }
835
836 void _DetachIfNotUnique() {
837 if (_IsUnique())
838 return;
839 // Copy to local.
840 _DetachCopyHook(__ARCH_PRETTY_FUNCTION__);
841 auto *newData = _AllocateCopy(_data, size(), size());
842 _DecRef();
843 _data = newData;
844 }
845
846 inline bool _IsUnique() const {
847 return !_data ||
848 (ARCH_LIKELY(!_foreignSource) && _GetNativeRefCount(_data) == 1);
849 }
850
851 inline size_t _CapacityForSize(size_t sz) const {
852 // Currently just successive powers of two.
853 size_t cap = 1;
854 while (cap < sz) {
855 cap += cap;
856 }
857 return cap;
858 }
859
860 value_type *_AllocateNew(size_t capacity) {
861 TfAutoMallocTag2 tag("VtArray::_AllocateNew", __ARCH_PRETTY_FUNCTION__);
862 // Need space for the control block and capacity elements.
863 // Exceptionally large capacity requests can overflow the arithmetic
864 // here. If that happens we'll just attempt to allocate the max size_t
865 // value and let new() throw.
866 size_t numBytes = (capacity <= max_size())
867 ? sizeof(_ControlBlock) + capacity * sizeof(value_type)
868 : std::numeric_limits<size_t>::max();
869 void *data = ::operator new(numBytes);
870 // Placement-new a control block.
871 ::new (data) _ControlBlock(/*count=*/1, capacity);
872 // Data starts after the block.
873 return reinterpret_cast<value_type *>(
874 static_cast<_ControlBlock *>(data) + 1);
875 }
876
877 value_type *_AllocateCopy(value_type *src, size_t newCapacity,
878 size_t numToCopy) {
879 // Allocate and copy elements.
880 value_type *newData = _AllocateNew(newCapacity);
881 std::uninitialized_copy(src, src + numToCopy, newData);
882 return newData;
883 }
884
885 void _DecRef() {
886 if (!_data)
887 return;
888 if (ARCH_LIKELY(!_foreignSource)) {
889 // Drop the refcount. If we take it to zero, destroy the data.
890 if (_GetNativeRefCount(_data).fetch_sub(
891 1, std::memory_order_release) == 1) {
892 std::atomic_thread_fence(std::memory_order_acquire);
893 for (value_type *p = _data, *e = _data + _shapeData.totalSize;
894 p != e; ++p) {
895 p->~value_type();
896 }
897 ::operator delete(static_cast<void *>(
898 std::addressof(_GetControlBlock(_data))));
899 }
900 }
901 else {
902 // Drop the refcount in the foreign source. If we take it to zero,
903 // invoke the function pointer to alert the foreign source.
904 if (_foreignSource->_refCount.fetch_sub(
905 1, std::memory_order_release) == 1) {
906 std::atomic_thread_fence(std::memory_order_acquire);
907 _foreignSource->_ArraysDetached();
908 }
909 }
910 _foreignSource = nullptr;
911 _data = nullptr;
912 }
913
914 value_type *_data;
915};
916
917// Declare basic array instantiations as extern templates. They are explicitly
918// instantiated in array.cpp.
919#define VT_ARRAY_EXTERN_TMPL(unused, elem) \
920 VT_API_TEMPLATE_CLASS(VtArray< VT_TYPE(elem) >);
921TF_PP_SEQ_FOR_EACH(VT_ARRAY_EXTERN_TMPL, ~, VT_SCALAR_VALUE_TYPES)
922
923template <class HashState, class ELEM>
924inline std::enable_if_t<VtIsHashable<ELEM>()>
925TfHashAppend(HashState &h, VtArray<ELEM> const &array)
926{
927 h.Append(array.size());
928 h.AppendContiguous(array.cdata(), array.size());
929}
930
931template <class ELEM>
932typename std::enable_if<VtIsHashable<ELEM>(), size_t>::type
933hash_value(VtArray<ELEM> const &array) {
934 return TfHash()(array);
935}
936
937// Specialize traits so others can figure out that VtArray is an array.
938template <typename T>
939struct VtIsArray< VtArray <T> > : public std::true_type {};
940
941template <class T>
942struct Vt_ArrayOpHelp {
943 static T Add(T l, T r) { return l + r; }
944 static T Sub(T l, T r) { return l - r; }
945 static T Mul(T l, T r) { return l * r; }
946 static T Div(T l, T r) { return l / r; }
947 static T Mod(T l, T r) { return l % r; }
948};
949
950template <class T>
951struct Vt_ArrayOpHelpScalar {
952 static T Mul(T l, double r) { return l * r; }
953 static T Mul(double l, T r) { return l * r; }
954 static T Div(T l, double r) { return l / r; }
955 static T Div(double l, T r) { return l / r; }
956};
957
958// These operations on bool-arrays are highly questionable, but this preserves
959// existing behavior in the name of Hyrum's Law.
960template <>
961struct Vt_ArrayOpHelp<bool> {
962 static bool Add(bool l, bool r) { return l | r; }
963 static bool Sub(bool l, bool r) { return l ^ r; }
964 static bool Mul(bool l, bool r) { return l & r; }
965 static bool Div(bool l, bool r) { return l; }
966 static bool Mod(bool l, bool r) { return false; }
967};
968
969template <>
970struct Vt_ArrayOpHelpScalar<bool> {
971 static bool Mul(bool l, double r) { return l && (r != 0.0); }
972 static bool Mul(double l, bool r) { return (l != 0.0) && r; }
973 static bool Div(bool l, double r) { return (r == 0.0) || l; }
974 static bool Div(double l, bool r) { return !r || (l != 0.0); }
975};
976
977#define VTOPERATOR_CPPARRAY(op, opName) \
978 template <class T> \
979 VtArray<T> \
980 operator op (VtArray<T> const &lhs, VtArray<T> const &rhs) \
981 { \
982 using Op = Vt_ArrayOpHelp<T>; \
983 /* accept empty vecs */ \
984 if (!lhs.empty() && !rhs.empty() && lhs.size() != rhs.size()) { \
985 TF_CODING_ERROR("Non-conforming inputs for operator %s", #op); \
986 return VtArray<T>(); \
987 } \
988 /* promote empty vecs to vecs of zeros */ \
989 const bool leftEmpty = lhs.size() == 0, rightEmpty = rhs.size() == 0; \
990 VtArray<T> ret(leftEmpty ? rhs.size() : lhs.size()); \
991 T zero = VtZero<T>(); \
992 if (leftEmpty) { \
993 std::transform( \
994 rhs.begin(), rhs.end(), ret.begin(), \
995 [zero](T const &r) { return Op:: opName (zero, r); }); \
996 } \
997 else if (rightEmpty) { \
998 std::transform( \
999 lhs.begin(), lhs.end(), ret.begin(), \
1000 [zero](T const &l) { return Op:: opName (l, zero); }); \
1001 } \
1002 else { \
1003 std::transform( \
1004 lhs.begin(), lhs.end(), rhs.begin(), ret.begin(), \
1005 [](T const &l, T const &r) { return Op:: opName (l, r); }); \
1006 } \
1007 return ret; \
1008 }
1009
1010ARCH_PRAGMA_PUSH
1011ARCH_PRAGMA_FORCING_TO_BOOL
1012ARCH_PRAGMA_UNSAFE_USE_OF_BOOL
1013ARCH_PRAGMA_UNARY_MINUS_ON_UNSIGNED
1014
1015VTOPERATOR_CPPARRAY(+, Add);
1016VTOPERATOR_CPPARRAY(-, Sub);
1017VTOPERATOR_CPPARRAY(*, Mul);
1018VTOPERATOR_CPPARRAY(/, Div);
1019VTOPERATOR_CPPARRAY(%, Mod);
1020
1021template <class T>
1023operator-(VtArray<T> const &a) {
1024 VtArray<T> ret(a.size());
1025 std::transform(a.begin(), a.end(), ret.begin(),
1026 [](T const &x) { return -x; });
1027 return ret;
1028}
1029
1030ARCH_PRAGMA_POP
1031
1032// Operations on scalars and arrays
1033// These are free functions defined in Array.h
1034#define VTOPERATOR_CPPSCALAR(op,opName) \
1035 template<typename T> \
1036 VtArray<T> operator op (T const &scalar, VtArray<T> const &arr) { \
1037 using Op = Vt_ArrayOpHelp<T>; \
1038 VtArray<T> ret(arr.size()); \
1039 std::transform(arr.begin(), arr.end(), ret.begin(), \
1040 [&scalar](T const &aObj) { \
1041 return Op:: opName (scalar, aObj); \
1042 }); \
1043 return ret; \
1044 } \
1045 template<typename T> \
1046 VtArray<T> operator op (VtArray<T> const &arr, T const &scalar) { \
1047 using Op = Vt_ArrayOpHelp<T>; \
1048 VtArray<T> ret(arr.size()); \
1049 std::transform(arr.begin(), arr.end(), ret.begin(), \
1050 [&scalar](T const &aObj) { \
1051 return Op:: opName (aObj, scalar); \
1052 }); \
1053 return ret; \
1054 }
1055
1056// define special-case operators on arrays and doubles - except if the array
1057// holds doubles, in which case we already defined the operator (with
1058// VTOPERATOR_CPPSCALAR above) so we can't do it again!
1059#define VTOPERATOR_CPPSCALAR_DOUBLE(op,opName) \
1060 template<typename T> \
1061 std::enable_if_t<!std::is_same<T, double>::value, VtArray<T>> \
1062 operator op (double const &scalar, VtArray<T> const &arr) { \
1063 using Op = Vt_ArrayOpHelpScalar<T>; \
1064 VtArray<T> ret(arr.size()); \
1065 std::transform(arr.begin(), arr.end(), ret.begin(), \
1066 [&scalar](T const &aObj) { \
1067 return Op:: opName (scalar, aObj); \
1068 }); \
1069 return ret; \
1070 } \
1071 template<typename T> \
1072 std::enable_if_t<!std::is_same<T, double>::value, VtArray<T>> \
1073 operator op (VtArray<T> const &arr, double const &scalar) { \
1074 using Op = Vt_ArrayOpHelpScalar<T>; \
1075 VtArray<T> ret(arr.size()); \
1076 std::transform(arr.begin(), arr.end(), ret.begin(), \
1077 [&scalar](T const &aObj) { \
1078 return Op:: opName (aObj, scalar); \
1079 }); \
1080 return ret; \
1081 }
1082
1083// free functions for operators combining scalar and array types
1084ARCH_PRAGMA_PUSH
1085ARCH_PRAGMA_FORCING_TO_BOOL
1086ARCH_PRAGMA_UNSAFE_USE_OF_BOOL
1087ARCH_PRAGMA_UNARY_MINUS_ON_UNSIGNED
1088VTOPERATOR_CPPSCALAR(+, Add)
1089VTOPERATOR_CPPSCALAR(-, Sub)
1090VTOPERATOR_CPPSCALAR(*, Mul)
1091VTOPERATOR_CPPSCALAR_DOUBLE(*, Mul)
1092VTOPERATOR_CPPSCALAR(/, Div)
1093VTOPERATOR_CPPSCALAR_DOUBLE(/, Div)
1094VTOPERATOR_CPPSCALAR(%, Mod)
1095ARCH_PRAGMA_POP
1096
1097PXR_NAMESPACE_CLOSE_SCOPE
1098
1099#endif // PXR_BASE_VT_ARRAY_H
Low-level utilities for informing users of various internal and external diagnostic conditions.
Defines all the types "TYPED" for which Vt creates a VtTYPEDArray typedef.
A user-extensible hashing mechanism for use with runtime hash tables.
Definition: hash.h:460
Scoped (i.e.
Definition: mallocTag.h:249
Represents an arbitrary dimensional rectangular container class.
Definition: array.h:211
VtArray(Vt_ArrayForeignDataSource *foreignSrc, ElementType *data, size_t size, bool addRef=true)
Create an array with foreign source.
Definition: array.h:267
ELEM ElementType
Type this array holds.
Definition: array.h:215
VtArray const & AsConst() const noexcept
Return *this as a const reference.
Definition: array.h:354
VtArray & operator=(VtArray &&other)
Move assign from other.
Definition: array.h:328
VtArray & operator=(std::initializer_list< ELEM > initializerList)
Replace current array contents with those in initializerList.
Definition: array.h:339
friend void swap(VtArray &lhs, VtArray &rhs)
Swap array contents.
Definition: array.h:832
bool IsIdentical(VtArray const &other) const
Tests if two arrays are identical, i.e.
Definition: array.h:784
friend std::ostream & operator<<(std::ostream &out, const VtArray &self)
Outputs a comma-separated list of the values in the array.
Definition: array.h:825
bool operator!=(VtArray const &other) const
Tests two arrays for inequality.
Definition: array.h:799
VtArray(std::initializer_list< ELEM > initializerList)
Initialize array from the contents of a initializerList.
Definition: array.h:299
VtArray(VtArray const &other)
Copy other. The new array shares underlying data with other.
Definition: array.h:278
VtArray(size_t n)
Create an array filled with n value-initialized elements.
Definition: array.h:305
VtArray(LegacyInputIterator first, LegacyInputIterator last, typename std::enable_if< !std::is_integral< LegacyInputIterator >::value, void >::type *=nullptr)
Create an array from a pair of iterators.
Definition: array.h:258
ElementType & operator[](size_t index)
Allows usage of [i].
Definition: array.h:773
VtArray()
Create an empty array.
Definition: array.h:243
VtArray(VtArray &&other)
Move from other.
Definition: array.h:293
ElementType const & operator[](size_t index) const
Allows usage of [i].
Definition: array.h:778
VtArray & operator=(VtArray const &other)
Copy assign from other.
Definition: array.h:318
bool operator==(VtArray const &other) const
Tests two arrays for equality. See also IsIdentical().
Definition: array.h:792
VtArray(size_t n, value_type const &value)
Create an array filled with n copies of value.
Definition: array.h:311
Define preprocessor function name macros.
void resize(size_t newSize, value_type &value)
Resize this array.
Definition: array.h:556
void assign(std::initializer_list< ELEM > initializerList)
Assign array contents via intializer list Equivalent to:
Definition: array.h:759
void pop_back()
Remove the last element of an array.
Definition: array.h:458
ElementType & reference
Reference type.
Definition: array.h:232
const_reverse_iterator rend() const
Return a const reverse iterator to the start of the array.
Definition: array.h:390
const_reference front() const
Return a const reference to the first element in this array.
Definition: array.h:520
const_pointer cdata() const
Return a const pointer to the data held by this array.
Definition: array.h:405
ElementType * iterator
Iterator type.
Definition: array.h:222
size_t size() const
Return the total number of elements in this array.
Definition: array.h:472
const_iterator begin() const
Return a const iterator to the start of the array.
Definition: array.h:369
iterator erase(const_iterator pos)
Removes a single element at pos from the array.
Definition: array.h:654
constexpr size_t max_size() const
Return a theoretical maximum size limit for the container.
Definition: array.h:490
const_pointer data() const
Return a const pointer to this array's data.
Definition: array.h:403
pointer data()
Return a non-const pointer to this array's data.
Definition: array.h:401
void reserve(size_t num)
Ensure enough memory is allocated to hold num elements.
Definition: array.h:503
const_iterator cbegin() const
Return a const iterator to the start of the array.
Definition: array.h:374
std::reverse_iterator< const_iterator > const_reverse_iterator
Reverse const iterator type.
Definition: array.h:229
void swap(VtArray &other)
Swap the contents of this array with other.
Definition: array.h:764
void push_back(ElementType const &element)
Appends an element at the end of the array.
Definition: array.h:443
ElementType * pointer
Pointer type.
Definition: array.h:236
ElementType const * const_iterator
Const iterator type.
Definition: array.h:224
void assign(size_t n, const value_type &fill)
Assign array contents.
Definition: array.h:743
const_reference back() const
Return a const reference to the last element in this array.
Definition: array.h:531
ElementType const & const_reference
Const reference type.
Definition: array.h:234
bool empty() const
Return true if this array contains no elements, false otherwise.
Definition: array.h:498
reverse_iterator rend()
Return a reverse iterator to the start of the array.
Definition: array.h:383
reference front()
Return a non-const reference to the first element in this array.
Definition: array.h:517
std::enable_if<!std::is_integral< ForwardIter >::value >::type assign(ForwardIter first, ForwardIter last)
Assign array contents.
Definition: array.h:726
const_reference cback() const
Return a const reference to the last element in this array.
Definition: array.h:534
size_t capacity() const
Return the number of items this container can grow to hold without triggering a (re)allocation.
Definition: array.h:478
void resize(size_t newSize)
Resize this array.
Definition: array.h:541
void resize(size_t newSize, value_type &&value)
Resize this array.
Definition: array.h:562
const_reverse_iterator crbegin() const
Return a const reverse iterator to the end of the array.
Definition: array.h:395
ElementType const * const_pointer
Const pointer type.
Definition: array.h:238
std::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
Definition: array.h:227
const_iterator cend() const
Return a const iterator to the end of the array.
Definition: array.h:376
iterator erase(const_iterator first, const_iterator last)
Remove a range of elements [first, last) from the array.
Definition: array.h:674
void resize(size_t newSize, value_type const &value)
Resize this array.
Definition: array.h:547
const_reverse_iterator crend() const
Return a const reverse iterator to the start of the array.
Definition: array.h:397
void emplace_back(Args &&... args)
Initializes a new element at the end of the array.
Definition: array.h:413
void clear()
Equivalent to resize(0).
Definition: array.h:626
iterator end()
Returns a non-const iterator to the end of the array.
Definition: array.h:366
const_iterator end() const
Return a const iterator to the end of the array.
Definition: array.h:371
reverse_iterator rbegin()
Return a non-const reverse iterator to the end of the array.
Definition: array.h:380
void push_back(ElementType &&element)
Appends an element at the end of the array.
Definition: array.h:452
iterator begin()
Return a non-const iterator to the start of the array.
Definition: array.h:363
void resize(size_t newSize, FillElemsFn &&fillElems)
Resize this array.
Definition: array.h:571
const_reference cfront() const
Return a const reference to the first element in this array.
Definition: array.h:523
const_reverse_iterator rbegin() const
Return a const reverse iterator to the end of the array.
Definition: array.h:386
reference back()
Return a reference to the last element in this array.
Definition: array.h:528
#define TF_DEV_AXIOM(cond)
The same as TF_AXIOM, but compiled only in dev builds.
Definition: diagnostic.h:205
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:68
size_t hash_value(const half h)
Overload hash_value for half.
Definition: half.h:28
STL namespace.
Pragmas for controlling compiler-specific behaviors.
Array concept. By default, types are not arrays.
Definition: traits.h:22