Poplar and PopLibs
SSOPointer.hpp
1// Copyright (c) 2020 Graphcore Ltd. All rights reserved.
2
3#ifndef poplar_SSOPointer_hpp
4#define poplar_SSOPointer_hpp
5
6#include <memory>
7#include <type_traits>
8
9namespace poplar {
10
11// small space optimised pointer. stores T inline (with an alignment of A) if
12// it is N bytes or smaller and the alignment A is a multiple of T's natural
13// alignment. otherwise T will be allocated on the heap.
14template <typename T, std::size_t N = 8, std::size_t A = 8>
15class SSOPointer final {
16public:
17 // same semantics as a unique_ptr: can't copy, can move.
18 // additionally: cannot be null, must be constructed via the make factory
19 // function below. however a moved from SSOPointer will have a undefined
20 // null state.
21 SSOPointer(const SSOPointer &) = delete;
22 SSOPointer &operator=(const SSOPointer &) = delete;
23
24 SSOPointer(SSOPointer &&o) noexcept {
25 void *b = &buf;
26 if (isPtr()) {
27 auto &src = *reinterpret_cast<T **>(&o.buf);
28 auto &dst = *reinterpret_cast<T **>(b);
29 dst = src;
30 src = nullptr;
31 } else {
32 new (b) T(std::move(*reinterpret_cast<T *>(&o.buf)));
33 }
34 }
35
36 SSOPointer &operator=(SSOPointer &&o) noexcept {
37 if (this != &o) {
38 if (isPtr()) {
39 auto &src = *reinterpret_cast<T **>(&o.buf);
40 auto &dst = *reinterpret_cast<T **>(&buf);
41 delete dst;
42 dst = src;
43 src = nullptr;
44 } else {
45 *reinterpret_cast<T *>(&buf) =
46 std::move(*reinterpret_cast<T *>(&o.buf));
47 }
48 }
49 return *this;
50 }
51
52 ~SSOPointer() {
53 if (isPtr()) {
54 delete *reinterpret_cast<T **>(&buf);
55 } else {
56 reinterpret_cast<T *>(&buf)->~T();
57 }
58 }
59
60 template <typename... Args> static SSOPointer make(Args &&...args) {
61 SSOPointer p;
62 void *b = &p.buf;
63 if (isPtr()) {
64 new (b) T *(new T(std::forward<Args>(args)...));
65 } else {
66 new (b) T(std::forward<Args>(args)...);
67 }
68 return p;
69 }
70
71 T *get() noexcept {
72 if (isPtr()) {
73 return *reinterpret_cast<T **>(&buf);
74 } else {
75 return reinterpret_cast<T *>(&buf);
76 }
77 }
78
79 const T *get() const noexcept {
80 if (isPtr()) {
81 return *reinterpret_cast<T *const *>(&buf);
82 } else {
83 return reinterpret_cast<const T *>(&buf);
84 }
85 }
86
87 T *operator->() noexcept { return get(); }
88 const T *operator->() const noexcept { return get(); }
89
90 T &operator*() noexcept { return *get(); }
91 const T &operator*() const noexcept { return *get(); }
92
93 constexpr static bool isPtr() noexcept {
94 return sizeof(T) > N || A % alignof(T) != 0;
95 }
96
97private:
98 SSOPointer() = default;
99
100 static_assert(N >= sizeof(T *), "Must be at least as big as a pointer");
101 static_assert(A % alignof(T *) == 0,
102 "Alignment must be a valid alignment of a pointer");
103 typename std::aligned_storage<N, A>::type buf;
104};
105
106} // namespace poplar
107
108#endif // poplar_SSOPointer_hpp
Poplar classes and functions.
Definition: ArrayRef.hpp:14