1 #pragma once
2 
3 /// \file AlignedStorage.h
4 /// \brief Base class for utility wrappers (Optional, Variant, ...)
5 /// \author Pavel Sevecek (sevecek at sirrah.troja.mff.cuni.cz)
6 /// \date 2016-2021
7 
8 #include "common/Assert.h"
9 #include "common/Traits.h"
10 #ifndef SPH_WIN
11 #include <mm_malloc.h>
12 #else
13 #include <malloc.h>
14 #endif
15 
16 NAMESPACE_SPH_BEGIN
17 
18 /// \brief Creates a new object of type T on heap, using aligned allocation.
19 template <typename T, typename... TArgs>
alignedNew(TArgs &&...args)20 INLINE T* alignedNew(TArgs&&... args) {
21     constexpr Size size = sizeof(T);
22     constexpr Size alignment = alignof(T);
23     void* ptr = _mm_malloc(size, alignment);
24     SPH_ASSERT(ptr);
25     return new (ptr) T(std::forward<TArgs>(args)...);
26 }
27 
28 /// \brief Deletes an object previously allocated using \ref alignedNew.
29 template <typename T>
alignedDelete(T * ptr)30 INLINE void alignedDelete(T* ptr) {
31     if (!ptr) {
32         return;
33     }
34 
35     ptr->~T();
36     _mm_free(ptr);
37     ptr = nullptr;
38 }
39 
40 template <typename T>
isAligned(const T & value)41 INLINE bool isAligned(const T& value) {
42     return reinterpret_cast<std::size_t>(&value) % alignof(T) == 0;
43 }
44 
45 /// \brief Simple block of memory on stack with size and alignment given by template type
46 
47 /// AlignedStorage can be used to construct an object on stack while sidestepping default construction.
48 /// Objects can be therefore default-constructed even if the underlying type does not have default
49 /// constructor.
50 /// Stored object can be later constructed by calling \ref emplace method. Note that when constructed,
51 /// it has to be later destroyed by explicitly calling \ref destroy method, this is not done
52 /// automatically! This object does NO checks when the stored value is accessed, or whether it is
53 /// constructed multiple times. This is left to the user.
54 
55 // dereferencing type-punned pointer will break strict-aliasing rules
56 #ifndef SPH_WIN
57 #pragma GCC diagnostic push
58 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
59 #endif
60 /// \todo It's weird that GCC issues this warning as we are using __may_alias__ attribute. Perhaps a bug?
61 
62 template <typename Type>
63 class AlignedStorage {
64 private:
65     struct SPH_MAY_ALIAS Holder {
66         alignas(Type) char storage[sizeof(Type)];
67     } holder;
68 
69 public:
70     AlignedStorage() = default;
71 
72     template <typename... TArgs>
emplace(TArgs &&...rest)73     INLINE void emplace(TArgs&&... rest) {
74         new (&holder) Type(std::forward<TArgs>(rest)...);
75     }
76 
destroy()77     INLINE void destroy() {
78         get().~Type();
79     }
80 
81     /// Implicit conversion to stored type
82     INLINE constexpr operator Type&() noexcept {
83         return get();
84     }
85 
86     /// Implicit conversion to stored type, const version
87     INLINE constexpr operator const Type&() const noexcept {
88         return get();
89     }
90 
91     /// Return the reference to the stored value.
get()92     INLINE constexpr Type& get() noexcept {
93         return reinterpret_cast<Type&>(holder);
94     }
95 
96     /// Returns the reference to the stored value, const version.
get()97     INLINE constexpr const Type& get() const noexcept {
98         return reinterpret_cast<const Type&>(holder);
99     }
100 };
101 
102 /// \brief Specialization for l-value references, a simple wrapper of ReferenceWrapper with same interface to
103 /// allow generic usage of AlignedStorage for both values and references.
104 template <typename Type>
105 class AlignedStorage<Type&> {
106     using StorageType = ReferenceWrapper<Type>;
107 
108     StorageType storage;
109 
110 public:
111     AlignedStorage() = default;
112 
113     template <typename T>
emplace(T & ref)114     INLINE void emplace(T& ref) {
115         storage = StorageType(ref);
116     }
117 
118     // no need do explicitly destroy reference wrapper
destroy()119     INLINE void destroy() {}
120 
121     INLINE constexpr operator Type&() noexcept {
122         return get();
123     }
124 
125     /// Implicit conversion to stored type, const version
126     INLINE constexpr operator const Type&() const noexcept {
127         return get();
128     }
129 
130     /// Return the reference to the stored value.
get()131     INLINE constexpr Type& get() noexcept {
132         return storage;
133     }
134 
135     /// Returns the reference to the stored value, const version.
get()136     INLINE constexpr const Type& get() const noexcept {
137         return storage;
138     }
139 };
140 
141 #ifndef SPH_WIN
142 #pragma GCC diagnostic pop
143 #endif
144 
145 NAMESPACE_SPH_END
146