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 
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 
24 PXR_NAMESPACE_OPEN_SCOPE
25 
31 class ESF_API_TYPE EsfFixedSizePolymorphicBase
32 {
33 public:
34  ESF_API virtual ~EsfFixedSizePolymorphicBase();
35 
36 private:
37  template <class Base, size_t BufferSize>
38  friend class EsfFixedSizePolymorphicHolder;
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 
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 
89 
98  template <class Derived, class... Args>
100  std::in_place_type_t<Derived> derivedTypeHint, Args &&...args);
101 
106  other->_CopyTo(_storage);
107  }
108 
116  other->_MoveTo(_storage);
117  }
118 
122  Get()->~Base();
123  }
124 
131  This &operator=(const This &other) {
132  if (this != &other) {
133  Get()->~Base();
134  other->_CopyTo(_storage);
135  }
136  return *this;
137  }
138 
146  This &operator=(This &&other) {
147  if (this != &other) {
148  Get()->~Base();
149  other->_MoveTo(_storage);
150  }
151  return *this;
152  }
153 
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  }
183 
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 
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 
280 PXR_NAMESPACE_CLOSE_SCOPE
281 
282 #endif
~EsfFixedSizePolymorphicHolder()
Destroys the derived instance held by this object.
EsfFixedSizePolymorphicHolder(const This &other)
Construct a holder containing a derived type instance that is copy- constructed from another holder.
EsfFixedSizePolymorphicHolder(This &&other)
Construct a holder containing a derived type that is move-constructed from another holder.
#define TF_UNUSED(x)
Stops compiler from producing unused argument or variable warnings.
Definition: tf.h:168
Stores polymorphic objects in a fixed-size buffer.
This & operator=(const This &other)
Construct a new instance that is copy-constructed from the instance in another holder.
This & operator=(This &&other)
Construct a new instance that is move-constructed from the instance in another holder.
Enables a base class to be used with EsfFixedSizePolymorphicHolder.
Checks if instances of Derived can be stored in this holder.
#define TF_AXIOM(cond)
Aborts if the condition cond is not met.
Definition: diagnostic.h:194
static bool HasBaseAtSameAddress(const Derived &derived)
Verifies that a Derived instance has the same address as its Base.
EsfFixedSizePolymorphicHolder()=delete
The default constructor is deleted because instances must always contain a derived object.
A file containing basic constants and definitions.
Stripped down version of diagnostic.h that doesn't define std::string.