HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
fixedSizePolymorphicHolder.h
Go to the documentation of this file.
1 //
2 // Copyright 2025 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_EXEC_ESF_FIXED_SIZE_POLYMORPHIC_HOLDER_H
8 #define PXR_EXEC_ESF_FIXED_SIZE_POLYMORPHIC_HOLDER_H
9 
10 /// \file
11 
12 #include "pxr/pxr.h"
13 
14 #include "pxr/exec/esf/api.h"
15 
17 #include "pxr/base/tf/tf.h"
18 
19 #include <cstddef>
20 #include <new>
21 #include <type_traits>
22 #include <utility>
23 
25 
26 /// Enables a base class to be used with EsfFixedSizePolymorphicHolder.
27 ///
28 /// This class defines private virtual methods that are used internally by
29 /// EsfFixedSizePolymorphicHolder. Clients must not override these methods.
30 ///
32 {
33 public:
35 
36 private:
37  template <class Base, size_t BufferSize>
39 
40  // Copies this object to the storage of another
41  // EsfFixedSizePolymorphicHolder.
42  //
43  // This class provides a default implementation so that derived types can be
44  // instantiated without having to override it. The proper overridden
45  // implementation will be defined by EsfFixedSizePolymorphicHolder.
46  //
47  ESF_API virtual void _CopyTo(std::byte *storage) const;
48 
49  // Moves this object to the storage of another
50  // EsfFixedSizePolymorphicHolder.
51  //
52  // This class provides a default implementation so that derived types can be
53  // instantiated without having to override it. The proper overridden
54  // implementation will be defined by EsfFixedSizePolymorphicHolder.
55  //
56  ESF_API virtual void _MoveTo(std::byte *storage);
57 };
58 
59 /// Stores polymorphic objects in a fixed-size buffer.
60 ///
61 /// If code attempts to instantiate an object that is too large to
62 /// fit in the allotted space, that code will generate a compile-time error.
63 /// Additionally, any instance of a derived class stored in this holder must
64 /// have its \p Base object at the same address. Currently this requirement is
65 /// enforced only at runtime.
66 ///
67 /// \p Base itself must inherit from EsfFixedSizePolymorphicBase.
68 ///
69 /// Instances of this class *always* contain a derived object. This class
70 /// supports copy/move construction and copy/move assignment, so long as the
71 /// derived type held by the "source" holder also supports copy/move
72 /// construction. Copying/moving of EsfFixedSizePolymorphicHolder%s is only
73 /// supported between identical template specializations (both must have
74 /// the same \p Base and \p BufferSize).
75 ///
76 template <class Base, size_t BufferSize = sizeof(Base)>
78 {
79  static_assert(std::is_base_of_v<EsfFixedSizePolymorphicBase, Base>);
80  constexpr static size_t _REQUIRED_ALIGNMENT = alignof(Base);
81 
82 public:
84 
85  /// The default constructor is deleted because instances must *always*
86  /// contain a derived object.
87  ///
89 
90  /// Construct a holder emplaced with a new \p Derived instance.
91  ///
92  /// The \p Derived type is deduced from the \p derivedTypeHint parameter,
93  /// usually passed as `std::in_place_type<Derived>`. This is necessary
94  /// because constructors cannot be invoked with explicit template arguments.
95  ///
96  /// The \p Args are forwarded to the \p Derived instance's constructor.
97  ///
98  template <class Derived, class... Args>
100  std::in_place_type_t<Derived> derivedTypeHint, Args &&...args);
101 
102  /// Construct a holder containing a derived type instance that is copy-
103  /// constructed from another holder.
104  ///
106  other->_CopyTo(_storage);
107  }
108 
109  /// Construct a holder containing a derived type that is move-constructed
110  /// from another holder.
111  ///
112  /// The moved-from holder continues to hold an object, but that object is
113  /// moved-from.
114  ///
116  other->_MoveTo(_storage);
117  }
118 
119  /// Destroys the derived instance held by this object.
120  ///
122  Get()->~Base();
123  }
124 
125  /// Construct a new instance that is copy-constructed from the instance in
126  /// another holder.
127  ///
128  /// This holder destroys its current instance before constructing the
129  /// new instance.
130  ///
131  This &operator=(const This &other) {
132  if (this != &other) {
133  Get()->~Base();
134  other->_CopyTo(_storage);
135  }
136  return *this;
137  }
138 
139  /// Construct a new instance that is move-constructed from the instance in
140  /// another holder.
141  ///
142  /// This holder destroys its current instance before constructing the new
143  /// instance. The moved-from holder continues to hold an object, but that
144  /// object is moved-from.
145  ///
146  This &operator=(This &&other) {
147  if (this != &other) {
148  Get()->~Base();
149  other->_MoveTo(_storage);
150  }
151  return *this;
152  }
153 
154  /// \name Held object accessors
155  ///
156  /// Returns a pointer or reference to the held Base instance.
157  ///
158  /// \{
159  inline Base *Get() {
160  return reinterpret_cast<Base *>(_storage);
161  }
162 
163  inline const Base *Get() const {
164  return reinterpret_cast<const Base *>(_storage);
165  }
166 
167  inline Base *operator->() {
168  return Get();
169  }
170 
171  inline const Base *operator->() const {
172  return Get();
173  }
174 
175  inline Base &operator*() {
176  return *Get();
177  }
178 
179  inline const Base &operator*() const {
180  return *Get();
181  }
182  /// \}
183 
184  /// Checks if instances of \p Derived can be stored in this holder.
185  ///
186  /// If any member of this class is false, then instances of \p Derived
187  /// cannot be stored in this holder. These checks are made public for
188  /// testing.
189  ///
191  {
192  template <class Derived>
193  constexpr static bool FITS_IN_BUFFER = sizeof(Derived) <= BufferSize;
194 
195  template <class Derived>
196  constexpr static bool DERIVES_FROM_BASE =
197  std::is_base_of_v<Base, Derived>;
198 
199  template <class Derived>
200  constexpr static bool HAS_ALIGNMENT =
201  alignof(Derived) == _REQUIRED_ALIGNMENT;
202 
203  /// Verifies that a Derived instance has the same address as its Base.
204  ///
205  /// This check would fail for certain cases of multiple-inheritance.
206  /// C++ does not currently allow checking this at compile-time, so this
207  /// check can only happen at runtime.
208  ///
209  template <class Derived>
210  static bool HasBaseAtSameAddress(const Derived &derived);
211  };
212 
213 private:
214  // Implements the _CopyTo and _MoveTo methods for a derived type.
215  template <class Derived>
216  class _Holder : public Derived
217  {
218  public:
219  // Constructs a Derived instance from the provided constructor Args.
220  template <class... Args>
221  _Holder(Args &&...args) : Derived(std::forward<Args>(args)...) {
223  }
224 
225  private:
226  void _CopyTo(std::byte *storage) const final {
227  const Derived *thisDerived = static_cast<const Derived *>(this);
228  ::new (static_cast<void *>(storage)) _Holder<Derived>(*thisDerived);
229  }
230 
231  void _MoveTo(std::byte *storage) final {
232  Derived *thisDerived = static_cast<Derived *>(this);
233  ::new (static_cast<void *>(storage)) _Holder<Derived>(
234  std::move(*thisDerived));
235  }
236  };
237 
238  // Held instances are emplaced in this byte array.
239  alignas(_REQUIRED_ALIGNMENT) std::byte _storage[BufferSize];
240 };
241 
242 template <class Base, size_t BufferSize>
243 template <class Derived, class... Args>
245  std::in_place_type_t<Derived> derivedTypeHint, Args &&...args)
246 {
247  TF_UNUSED(derivedTypeHint);
248 
249  static_assert(
250  Compatibility::template FITS_IN_BUFFER<Derived>,
251  "The size of the derived type is larger than the availble "
252  "storage.");
253  static_assert(
254  Compatibility::template DERIVES_FROM_BASE<Derived>,
255  "Derived is not a derived class of Base.");
256  static_assert(
257  Compatibility::template HAS_ALIGNMENT<Derived>,
258  "The derived type has incompatible alignment.");
259 
260  ::new (static_cast<void *>(_storage)) _Holder<Derived>(
261  std::forward<Args>(args)...);
262 }
263 
264 template <class Base, size_t BufferSize>
265 template <class Derived>
266 bool
268  HasBaseAtSameAddress(const Derived &derived)
269 {
270  if constexpr (!DERIVES_FROM_BASE<Derived>) {
271  return false;
272  }
273 
274  const Base *base = static_cast<const Base *>(&derived);
275  const void *baseAddress = static_cast<const void *>(base);
276  const void *derivedAddress = static_cast<const void *>(&derived);
277  return baseAddress == derivedAddress;
278 }
279 
281 
282 #endif
*get result *(waiting if necessary)*A common idiom is to fire a bunch of sub tasks at the and then *wait for them to all complete We provide a helper class
Definition: thread.h:632
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
#define ESF_API_TYPE
Definition: api.h:26
#define TF_AXIOM(cond)
#define TF_UNUSED(x)
Definition: tf.h:168
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
**If you just want to fire and args
Definition: thread.h:618
#define ESF_API
Definition: api.h:25
static bool HasBaseAtSameAddress(const Derived &derived)
unsigned char byte
Definition: UT_Span.h:163