TRIQS/nda 1.3.0
Multi-dimensional array library for C++
Loading...
Searching...
No Matches
handle.hpp
Go to the documentation of this file.
1// Copyright (c) 2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2024 Simons Foundation
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0.txt
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16//
17// Authors: Thomas Hahn, Olivier Parcollet, Nils Wentzell
18
25#pragma once
26
27#include "./allocators.hpp"
28#include "./memcpy.hpp"
29#include "../concepts.hpp"
30#include "../macros.hpp"
31
32#include <array>
33#include <memory>
34#include <type_traits>
35#include <utility>
36
37namespace nda::mem {
38
45 static constexpr bool init_dcmplx = true;
46
53 template <typename T, int Al>
54 struct alignas(Al) aligner {
56 T x;
57
59 [[nodiscard]] T &get() noexcept { return x; }
60
62 [[nodiscard]] T const &get() const noexcept { return x; }
63 };
64
67
69 inline static constexpr do_not_initialize_t do_not_initialize{};
70
72 struct init_zero_t {};
73
75 inline static constexpr init_zero_t init_zero{};
76
85 // Forward declaration.
86 template <typename T, AddressSpace AdrSp = Host>
87 struct handle_shared;
89
98 template <typename T, Allocator A = mallocator<>>
99 struct handle_heap {
100 static_assert(std::is_nothrow_destructible_v<T>, "nda::mem::handle_heap requires the value_type to have a non-throwing destructor");
101
102 private:
103 // Pointer to the start of the actual data.
104 T *_data = nullptr;
105
106 // Size of the data (number of T elements). Invariant: size > 0 iif data != nullptr.
107 size_t _size = 0;
108
109 // Allocator to use.
110#ifndef NDA_DEBUG_LEAK_CHECK
111 static inline A allocator; // NOLINT (allocator is not specific to a single instance)
112#else
113 static inline leak_check<A> allocator; // NOLINT (allocator is not specific to a single instance)
114#endif
115
116 // For shared ownership (points to a blk_T_t).
117 mutable std::shared_ptr<void> sptr;
118
119 // Type of the memory block, i.e. a pointer to the data and its size.
120 using blk_T_t = std::pair<T *, size_t>;
121
122 // Release the handled memory (data pointer and size are not set to null here).
123 static void destruct(blk_T_t b) noexcept {
124 auto [data, size] = b;
125
126 // do nothing if the data is null
127 if (data == nullptr) return;
128
129 // if needed, call the destructors of the objects stored
130 if constexpr (A::address_space == Host and !(std::is_trivial_v<T> or nda::is_complex_v<T>)) {
131 for (size_t i = 0; i < size; ++i) data[i].~T();
132 }
133
134 // deallocate the memory block
135 allocator.deallocate({(char *)data, size * sizeof(T)});
136 }
137
138 // Deleter for the shared pointer.
139 static void deleter(void *p) noexcept { destruct(*((blk_T_t *)p)); }
140
141 public:
143 using value_type = T;
144
146 using allocator_type = A;
147
149 static constexpr auto address_space = allocator_type::address_space;
150
155 std::shared_ptr<void> get_sptr() const {
156 if (not sptr) sptr.reset(new blk_T_t{_data, _size}, deleter);
157 return sptr;
158 }
159
165 ~handle_heap() noexcept {
166 if (not sptr and not(is_null())) destruct({_data, _size});
167 }
168
170 handle_heap() = default;
171
176 handle_heap(handle_heap &&h) noexcept : _data(h._data), _size(h._size), sptr(std::move(h.sptr)) {
177 h._data = nullptr;
178 h._size = 0;
179 }
180
188 // release current resources if they are not shared and not null
189 if (not sptr and not(is_null())) destruct({_data, _size});
190
191 // move the resources from the source handle
192 _data = h._data;
193 _size = h._size;
194 sptr = std::move(h.sptr);
195
196 // reset the source handle to a null state
197 h._data = nullptr;
198 h._size = 0;
199 return *this;
200 }
201
206 explicit handle_heap(handle_heap const &h) : handle_heap(h.size(), do_not_initialize) {
207 if (is_null()) return;
208 if constexpr (std::is_trivially_copyable_v<T>) {
209 memcpy<address_space, address_space>(_data, h.data(), h.size() * sizeof(T));
210 } else {
211 for (size_t i = 0; i < _size; ++i) new (_data + i) T(h[i]);
212 }
213 }
214
222 *this = handle_heap{h};
223 return *this;
224 }
225
232 template <OwningHandle<value_type> H>
233 explicit handle_heap(H const &h) : handle_heap(h.size(), do_not_initialize) {
234 if (is_null()) return;
235 if constexpr (std::is_trivially_copyable_v<T>) {
236 memcpy<address_space, H::address_space>((void *)_data, (void *)h.data(), _size * sizeof(T));
237 } else {
238 static_assert(address_space == H::address_space,
239 "Constructing an nda::mem::handle_heap from a handle of a different address space requires a trivially copyable value_type");
240 for (size_t i = 0; i < _size; ++i) new (_data + i) T(h[i]);
241 }
242 }
243
251 template <Allocator AS>
253 *this = handle_heap{h};
254 return *this;
255 }
256
262 if (size == 0) return;
263 auto b = allocator.allocate(size * sizeof(T));
264 if (not b.ptr) throw std::bad_alloc{};
265 _data = (T *)b.ptr;
266 _size = size;
267 }
268
274 if (size == 0) return;
275 auto b = allocator.allocate_zero(size * sizeof(T));
276 if (not b.ptr) throw std::bad_alloc{};
277 _data = (T *)b.ptr;
278 _size = size;
279 }
280
292 handle_heap(long size) {
293 if (size == 0) return;
294 blk_t b;
295 if constexpr (is_complex_v<T> && init_dcmplx)
296 b = allocator.allocate_zero(size * sizeof(T));
297 else
298 b = allocator.allocate(size * sizeof(T));
299 if (not b.ptr) throw std::bad_alloc{};
300 _data = (T *)b.ptr;
301 _size = size;
302
303 // call placement new for non trivial and non complex types
304 if constexpr (!std::is_trivial_v<T> and !is_complex_v<T>) {
305 for (size_t i = 0; i < size; ++i) new (_data + i) T();
306 }
307 }
308
315 [[nodiscard]] T &operator[](long i) noexcept { return _data[i]; }
316
323 [[nodiscard]] T const &operator[](long i) const noexcept { return _data[i]; }
324
329 [[nodiscard]] bool is_null() const noexcept {
330#ifdef NDA_DEBUG
331 // check the invariants in debug mode
332 EXPECTS((_data == nullptr) == (_size == 0));
333#endif
334 return _data == nullptr;
335 }
336
341 [[nodiscard]] T *data() const noexcept { return _data; }
342
347 [[nodiscard]] long size() const noexcept { return _size; }
348 };
349
358 template <typename T, size_t Size>
360 static_assert(std::is_copy_constructible_v<T>, "nda::mem::handle_stack requires the value_type to be copy constructible");
361 static_assert(std::is_nothrow_destructible_v<T>, "nda::mem::handle_stack requires the value_type to have a non-throwing destructor");
362
363 private:
364 // Memory buffer on the stack to store the data.
365 std::array<char, sizeof(T) * Size> buffer;
366
367 public:
369 using value_type = T;
370
372 static constexpr auto address_space = Host;
373
378 ~handle_stack() noexcept {
379 if constexpr (!std::is_trivial_v<T>) {
380 // explicitly call the destructor for each non-trivial object
381 for (size_t i = 0; i < Size; ++i) data()[i].~T();
382 }
383 }
384
386 handle_stack() = default;
387
393 handle_stack(handle_stack &&h) noexcept { operator=(h); }
394
401 operator=(h);
402 return *this;
403 }
404
410 handle_stack(handle_stack const &h) noexcept { operator=(h); }
411
417 for (size_t i = 0; i < Size; ++i) new (data() + i) T(h[i]);
418 return *this;
419 }
420
423
425 handle_stack(long /*size*/, init_zero_t) {
426 static_assert(std::is_scalar_v<T> or is_complex_v<T>, "nda::mem::handle_stack can only be initialized to zero for scalar and complex types");
427 for (size_t i = 0; i < Size; ++i) data()[i] = 0;
428 }
429
437 handle_stack(long /*size*/) : handle_stack{} {
438 if constexpr (!std::is_trivial_v<T> and !is_complex_v<T>) {
439 for (size_t i = 0; i < Size; ++i) new (data() + i) T();
440 }
441 }
442
449 [[nodiscard]] T &operator[](long i) noexcept { return data()[i]; }
450
457 [[nodiscard]] T const &operator[](long i) const noexcept { return data()[i]; }
458
463 static constexpr bool is_null() noexcept { return false; }
464
469 [[nodiscard]] T *data() const noexcept { return (T *)buffer.data(); }
470
475 static constexpr long size() noexcept { return Size; }
476 };
477
491 template <typename T, size_t Size>
492 struct handle_sso { // struct alignas(alignof(T)) handle_sso {
493 static_assert(Size > 0, "Size == 0 makes no sense in nda::mem::handle_sso");
494 static_assert(std::is_copy_constructible_v<T>, "nda::mem::handle_sso requires the value_type to be copy constructible");
495 static_assert(std::is_nothrow_destructible_v<T>, "nda::mem::handle_sso requires the value_type to have a non-throwing destructor");
496
497 private:
498 // Memory buffer on the stack to store the data.
499 std::array<char, sizeof(T) * Size> buffer;
500
501 // Pointer to the start of the actual data.
502 T *_data = nullptr;
503
504 // Size of the data (number of T elements). Invariant: size > 0 iif data != nullptr.
505 size_t _size = 0;
506
507 // Release the handled memory.
508 void clean() noexcept {
509 if (is_null()) return;
510 if constexpr (!std::is_trivial_v<T>) {
511 // explicitly call the destructor for each non-trivial object
512 for (size_t i = 0; i < _size; ++i) data()[i].~T();
513 }
514 if (on_heap()) mallocator<>::deallocate({(char *)_data, _size * sizeof(T)});
515 _data = nullptr;
516 _size = 0;
517 }
518
519 public:
521 using value_type = T;
522
524 static constexpr auto address_space = Host;
525
527 handle_sso(){}; // NOLINT (user-defined constructor to avoid value initialization of the buffer)
528
535 ~handle_sso() noexcept { clean(); }
536
542 handle_sso(handle_sso &&h) noexcept : _size(h._size) {
543 if (on_heap()) {
544 _data = h._data;
545 } else {
546 if (_size != 0) {
547 _data = (T *)buffer.data();
548 for (size_t i = 0; i < _size; ++i) new (data() + i) T(h[i]);
549 }
550 }
551 h._data = nullptr;
552 h._size = 0;
553 }
554
564 clean();
565 _size = h._size;
566 if (on_heap()) {
567 _data = h._data;
568 } else {
569 if (_size != 0) {
570 _data = (T *)buffer.data();
571 for (size_t i = 0; i < _size; ++i) new (_data + i) T(h[i]);
572 }
573 }
574 h._data = nullptr;
575 h._size = 0;
576 return *this;
577 }
578
584 if (is_null()) return;
585 if constexpr (std::is_trivially_copyable_v<T>) {
586 memcpy<address_space, address_space>((void *)_data, (void *)h.data(), _size * sizeof(T));
587 } else {
588 for (size_t i = 0; i < _size; ++i) new (_data + i) T(h[i]);
589 }
590 }
591
599 if (this == &h) return *this; // self assignment
600 clean();
601 _size = h._size;
602 if (_size == 0) return *this;
603 if (on_heap()) {
604 auto b = mallocator<>::allocate(_size * sizeof(T));
605 if (not b.ptr) throw std::bad_alloc{};
606 _data = (T *)b.ptr;
607 } else {
608 _data = (T *)buffer.data();
609 }
610 for (size_t i = 0; i < _size; ++i) new (_data + i) T(h[i]);
611 return *this;
612 }
613
619 template <OwningHandle<value_type> H>
620 explicit handle_sso(H const &h) : handle_sso(h.size(), do_not_initialize) {
621 if (is_null()) return;
622 if constexpr (std::is_trivially_copyable_v<T>) {
623 memcpy<address_space, H::address_space>((void *)_data, (void *)h.data(), _size * sizeof(T));
624 } else {
625 static_assert(address_space == H::address_space,
626 "Constructing an nda::mem::handle_sso from a handle of a different address space requires a trivially copyable value_type");
627 for (size_t i = 0; i < _size; ++i) new (_data + i) T(h[i]);
628 }
629 }
630
637 if (size == 0) return;
638 _size = size;
639 if (not on_heap()) {
640 _data = (T *)buffer.data();
641 } else {
642 auto b = mallocator<>::allocate(size * sizeof(T));
643 if (not b.ptr) throw std::bad_alloc{};
644 _data = (T *)b.ptr;
645 }
646 }
647
657 static_assert(std::is_scalar_v<T> or is_complex_v<T>, "nda::mem::handle_sso can only be initialized to zero for scalar and complex types");
658 if (size == 0) return;
659 _size = size;
660 if (not on_heap()) {
661 _data = (T *)buffer.data();
662 for (size_t i = 0; i < _size; ++i) data()[i] = 0;
663 } else {
664 auto b = mallocator<>::allocate_zero(size * sizeof(T)); //, alignof(T));
665 if (not b.ptr) throw std::bad_alloc{};
666 _data = (T *)b.ptr;
667 }
668 }
669
680 handle_sso(long size) {
681 if (size == 0) return;
682 _size = size;
683 if (not on_heap()) {
684 _data = (T *)buffer.data();
685 } else {
686 blk_t b;
687 if constexpr (is_complex_v<T> && init_dcmplx)
688 b = mallocator<>::allocate_zero(size * sizeof(T));
689 else
690 b = mallocator<>::allocate(size * sizeof(T));
691 if (not b.ptr) throw std::bad_alloc{};
692 _data = (T *)b.ptr;
693 }
694
695 // call placement new for non trivial and non complex types
696 if constexpr (!std::is_trivial_v<T> and !is_complex_v<T>) {
697 for (size_t i = 0; i < size; ++i) new (_data + i) T();
698 }
699 }
700
707 [[nodiscard]] T &operator[](long i) noexcept { return _data[i]; }
708
715 [[nodiscard]] T const &operator[](long i) const noexcept { return _data[i]; }
716
721 [[nodiscard]] bool on_heap() const { return _size > Size; }
722
727 [[nodiscard]] bool is_null() const noexcept {
728#ifdef NDA_DEBUG
729 EXPECTS((_data == nullptr) == (_size == 0));
730#endif
731 return _data == nullptr;
732 }
733
738 [[nodiscard]] T *data() const noexcept { return _data; }
739
744 [[nodiscard]] long size() const noexcept { return _size; }
745 };
746
753 template <typename T, AddressSpace AdrSp>
755 static_assert(std::is_nothrow_destructible_v<T>, "nda::mem::handle_shared requires the value_type to have a non-throwing destructor");
756
757 private:
758 // Pointer to the start of the actual data.
759 T *_data = nullptr;
760
761 // Size of the data (number of T elements). Invariant: size > 0 iif data != 0.
762 size_t _size = 0;
763
764 // Type of the memory block, i.e. a pointer to the data and its size.
765 using blk_t = std::pair<T *, size_t>;
766
767 // For shared ownership (points to a blk_T_t).
768 std::shared_ptr<void> sptr;
769
770 public:
772 using value_type = T;
773
775 static constexpr auto address_space = AdrSp;
776
778 handle_shared() = default;
779
788 handle_shared(T *data, size_t size, void *foreign_handle, void (*foreign_decref)(void *)) noexcept
789 : _data(data), _size(size), sptr{foreign_handle, foreign_decref} {}
790
797 template <Allocator A>
799 requires(A::address_space == address_space)
800 : _data(h.data()), _size(h.size()) {
801 if (not h.is_null()) sptr = h.get_sptr();
802 }
803
810 [[nodiscard]] T &operator[](long i) noexcept { return _data[i]; }
811
818 [[nodiscard]] T const &operator[](long i) const noexcept { return _data[i]; }
819
824 [[nodiscard]] bool is_null() const noexcept {
825#ifdef NDA_DEBUG
826 // Check the Invariants in Debug Mode
827 EXPECTS((_data == nullptr) == (_size == 0));
828#endif
829 return _data == nullptr;
830 }
831
836 [[nodiscard]] long refcount() const noexcept { return sptr.use_count(); }
837
842 [[nodiscard]] T *data() const noexcept { return _data; }
843
848 [[nodiscard]] long size() const noexcept { return _size; }
849 };
850
857 template <typename T, AddressSpace AdrSp = Host>
859 private:
860 // Value type of the data with const removed.
861 using T0 = std::remove_const_t<T>;
862
863 // Parent handle (required for regular -> shared promotion in Python Converter).
864 handle_heap<T0> const *_parent = nullptr;
865
866 // Pointer to the start of the actual data.
867 T *_data = nullptr;
868
869 public:
871 using value_type = T;
872
874 static constexpr auto address_space = AdrSp;
875
877 handle_borrowed() = default;
878
881
884
887
892 handle_borrowed(T *ptr) noexcept : _data(ptr) {}
893
901 template <Handle H>
902 requires(address_space == H::address_space and (std::is_const_v<value_type> or !std::is_const_v<typename H::value_type>)
903 and std::is_same_v<const value_type, const typename H::value_type>)
904 handle_borrowed(H const &h, long offset = 0) noexcept : _data(h.data() + offset) {
905 if constexpr (std::is_same_v<H, handle_heap<T0>>) _parent = &h;
906 }
907
914 [[nodiscard]] T &operator[](long i) noexcept { return _data[i]; }
915
922 [[nodiscard]] T const &operator[](long i) const noexcept { return _data[i]; }
923
928 [[nodiscard]] bool is_null() const noexcept { return _data == nullptr; }
929
934 [[nodiscard]] handle_heap<T0> const *parent() const { return _parent; }
935
940 [[nodiscard]] T *data() const noexcept { return _data; }
941 };
942
945} // namespace nda::mem
Provides custom allocators for the nda library.
Wrap an allocator to check for memory leaks.
Custom allocator that uses nda::mem::malloc to allocate memory.
Provides concepts for the nda library.
static constexpr bool init_dcmplx
Should we initialize memory for complex double types to zero.
Definition handle.hpp:45
static constexpr do_not_initialize_t do_not_initialize
Instance of nda::mem::do_not_initialize_t.
Definition handle.hpp:69
static constexpr init_zero_t init_zero
Instance of nda::mem::init_zero_t.
Definition handle.hpp:75
constexpr bool is_complex_v
Constexpr variable that is true if type T is a std::complex type.
Definition traits.hpp:75
Macros used in the nda library.
Provides a generic memcpy and memcpy2D function for different address spaces.
Wraps an arbitrary type to have a specified alignment.
Definition handle.hpp:54
T & get() noexcept
Get reference to the wrapped object.
Definition handle.hpp:59
T const & get() const noexcept
Get const reference to the wrapped object.
Definition handle.hpp:62
T x
Wrapped object of type T.
Definition handle.hpp:56
Memory block consisting of a pointer and its size.
char * ptr
Pointer to the memory block.
Tag used in constructors to indicate that the memory should not be initialized.
Definition handle.hpp:66
A non-owning handle for a memory block on the heap.
Definition handle.hpp:858
handle_borrowed(T *ptr) noexcept
Construct a borrowed handle from a pointer to the data.
Definition handle.hpp:892
handle_heap< T0 > const * parent() const
Get a pointer to the parent handle.
Definition handle.hpp:934
handle_borrowed(H const &h, long offset=0) noexcept
Construct a borrowed handle from a another handle.
Definition handle.hpp:904
handle_borrowed & operator=(handle_borrowed &&)=default
Default move assignment operator.
handle_borrowed & operator=(handle_borrowed const &)=default
Default copy assignment operator.
handle_borrowed()=default
Default constructor leaves the handle in a null state (nullptr).
T & operator[](long i) noexcept
Subscript operator to access the data.
Definition handle.hpp:914
bool is_null() const noexcept
Check if the handle is in a null state.
Definition handle.hpp:928
T const & operator[](long i) const noexcept
Subscript operator to access the data.
Definition handle.hpp:922
handle_borrowed(handle_borrowed const &)=default
Default copy constructor.
T value_type
Value type of the data.
Definition handle.hpp:871
T * data() const noexcept
Get a pointer to the stored data.
Definition handle.hpp:940
A handle for a memory block on the heap.
Definition handle.hpp:99
long size() const noexcept
Get the size of the handle.
Definition handle.hpp:347
~handle_heap() noexcept
Destructor for the handle.
Definition handle.hpp:165
A allocator_type
nda::mem::Allocator type.
Definition handle.hpp:146
handle_heap & operator=(handle_heap const &h)
Copy assignment operator utilizes the copy constructor and move assignment operator to make a deep co...
Definition handle.hpp:221
T const & operator[](long i) const noexcept
Subscript operator to access the data.
Definition handle.hpp:323
handle_heap(handle_heap &&h) noexcept
Move constructor simply copies the pointers and size and resets the source handle to a null state.
Definition handle.hpp:176
handle_heap(long size, init_zero_t)
Construct a handle by allocating memory for the data of a given size and initializing it to zero.
Definition handle.hpp:273
T * data() const noexcept
Get a pointer to the stored data.
Definition handle.hpp:341
handle_heap(long size)
Construct a handle by allocating memory for the data of a given size and initializing it depending on...
Definition handle.hpp:292
handle_heap & operator=(handle_heap< T, AS > const &h)
Assignment operator utilizes another constructor and move assignment to make a deep copy of the data ...
Definition handle.hpp:252
handle_heap(long size, do_not_initialize_t)
Construct a handle by allocating memory for the data of a given size but without initializing it.
Definition handle.hpp:261
T value_type
Value type of the data.
Definition handle.hpp:143
bool is_null() const noexcept
Check if the handle is in a null state.
Definition handle.hpp:329
handle_heap(handle_heap const &h)
Copy constructor makes a deep copy of the data from another handle.
Definition handle.hpp:206
T & operator[](long i) noexcept
Subscript operator to access the data.
Definition handle.hpp:315
handle_heap & operator=(handle_heap &&h) noexcept
Move assignment operator first releases the resources held by the current handle and then moves the r...
Definition handle.hpp:187
handle_heap()=default
Default constructor leaves the handle in a null state (nullptr and size 0).
std::shared_ptr< void > get_sptr() const
Get a shared pointer to the memory block.
Definition handle.hpp:155
handle_heap(H const &h)
Construct a handle by making a deep copy of the data from another handle.
Definition handle.hpp:233
A handle for a memory block on the heap with shared ownership.
Definition handle.hpp:754
handle_shared(handle_heap< T, A > const &h) noexcept
Construct a shared handle from an nda::mem::handle_heap.
Definition handle.hpp:798
handle_shared()=default
Default constructor leaves the handle in a null state (nullptr and size 0).
T & operator[](long i) noexcept
Subscript operator to access the data.
Definition handle.hpp:810
long refcount() const noexcept
Get the reference count of the shared object.
Definition handle.hpp:836
bool is_null() const noexcept
Check if the handle is in a null state.
Definition handle.hpp:824
T value_type
Value type of the data.
Definition handle.hpp:772
T const & operator[](long i) const noexcept
Subscript operator to access the data.
Definition handle.hpp:818
handle_shared(T *data, size_t size, void *foreign_handle, void(*foreign_decref)(void *)) noexcept
Construct a handle from a shared object from a foreign library.
Definition handle.hpp:788
T * data() const noexcept
Get a pointer to the stored data.
Definition handle.hpp:842
long size() const noexcept
Get the size of the handle.
Definition handle.hpp:848
A handle for a memory block on the heap or stack depending on the size of the data.
Definition handle.hpp:492
handle_sso()
Default constructor.
Definition handle.hpp:527
long size() const noexcept
Get the size of the handle.
Definition handle.hpp:744
~handle_sso() noexcept
Destructor for the handle.
Definition handle.hpp:535
handle_sso & operator=(handle_sso const &h)
Copy assignment operator first cleans up the current handle and then makes a deep copy of the data fr...
Definition handle.hpp:598
T & operator[](long i) noexcept
Subscript operator to access the data.
Definition handle.hpp:707
bool is_null() const noexcept
Check if the handle is in a null state.
Definition handle.hpp:727
handle_sso & operator=(handle_sso &&h) noexcept
Move assignment operator first releases the resources held by the current handle and then either copi...
Definition handle.hpp:563
handle_sso(long size)
Construct a handle for the data of a given size and initialize it depending on the value type.
Definition handle.hpp:680
handle_sso(handle_sso const &h)
Copy construct a handle by making a deep copy of the data from the source handle.
Definition handle.hpp:583
T value_type
Value type of the data.
Definition handle.hpp:521
T * data() const noexcept
Get a pointer to the stored data.
Definition handle.hpp:738
bool on_heap() const
Check if the data is/should be stored on the heap.
Definition handle.hpp:721
handle_sso(long size, do_not_initialize_t)
Construct a handle for the data of a given size and do not initialize it.
Definition handle.hpp:636
handle_sso(long size, init_zero_t)
Construct a handle for the data of a given size and initialize it to zero (only for scalar and comple...
Definition handle.hpp:656
handle_sso(handle_sso &&h) noexcept
Move constructor either copies the heap pointers or makes a deep copy of the stack data.
Definition handle.hpp:542
handle_sso(H const &h)
Construct a handle by making a deep copy of the data from another owning handle.
Definition handle.hpp:620
T const & operator[](long i) const noexcept
Subscript operator to access the data.
Definition handle.hpp:715
A handle for a memory block on the stack.
Definition handle.hpp:359
handle_stack()=default
Default constructor leaves the data uninitialized.
handle_stack(long)
Construct a handle and initialize the data depending on the value type.
Definition handle.hpp:437
T & operator[](long i) noexcept
Subscript operator to access the data.
Definition handle.hpp:449
T * data() const noexcept
Get a pointer to the stored data.
Definition handle.hpp:469
handle_stack & operator=(handle_stack const &h)
Copy assignment operator makes a deep copy of the data from the source handle using placement new.
Definition handle.hpp:416
~handle_stack() noexcept
Destructor for the handle.
Definition handle.hpp:378
handle_stack(handle_stack const &h) noexcept
Copy constructor simply calls the copy assignment operator.
Definition handle.hpp:410
handle_stack(long, do_not_initialize_t)
Construct a handle and do not initialize the data.
Definition handle.hpp:422
handle_stack(handle_stack &&h) noexcept
Move constructor simply calls the copy assignment operator.
Definition handle.hpp:393
handle_stack & operator=(handle_stack &&h) noexcept
Move assignment operator simply calls the copy assignment operator.
Definition handle.hpp:400
handle_stack(long, init_zero_t)
Construct a handle and initialize the data to zero (only for scalar and complex types).
Definition handle.hpp:425
static constexpr long size() noexcept
Get the size of the handle.
Definition handle.hpp:475
static constexpr bool is_null() noexcept
Check if the handle is in a null state.
Definition handle.hpp:463
T value_type
Value type of the data.
Definition handle.hpp:369
T const & operator[](long i) const noexcept
Subscript operator to access the data.
Definition handle.hpp:457
Tag used in constructors to indicate that the memory should be initialized to zero.
Definition handle.hpp:72