COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
alloc.h
Go to the documentation of this file.
1//
2// Copyright 2016 - 2024 (C). Alex Robenko. All rights reserved.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
11
12
13#pragma once
14
15#include <memory>
16#include <type_traits>
17#include <array>
18#include <algorithm>
19#include <limits>
20
22#include "comms/Assert.h"
23#include "comms/dispatch.h"
24#include "comms/util/Tuple.h"
26#include "comms/details/tag.h"
27
28COMMS_MSVC_WARNING_PUSH
29COMMS_MSVC_WARNING_DISABLE(4324) // Disable warning about alignment padding
30
31namespace comms
32{
33
34namespace util
35{
36
37namespace alloc
38{
39
40namespace details
41{
42
43template <typename TInterfaceType, typename TDefaultType>
44struct DynMemoryDeleteHandler
45{
46 template <typename TObj>
47 void handle(TObj& obj) const
48 {
49 using HandleTag =
50 typename comms::util::LazyShallowConditional<
51 std::is_void<TDefaultType>::value
52 >::template Type<
53 NoDefaultCastTag,
54 DefaultCastCheckTag
55 >;
56
57 handleInternal(obj, HandleTag());
58 }
59
60private:
61 template <typename... TParams>
62 using NoDefaultCastTag = comms::details::tag::Tag1<>;
63
64 template <typename... TParams>
65 using DefaultCastCheckTag = comms::details::tag::Tag2<>;
66
67 template <typename... TParams>
68 using ForcedDefaultCastTag = comms::details::tag::Tag3<>;
69
70 template <typename TObj, typename... TParams>
71 void handleInternal(TObj& obj, NoDefaultCastTag<TParams...>) const
72 {
73 delete (&obj);
74 }
75
76 template <typename TObj, typename... TParams>
77 void handleInternal(TObj& obj, ForcedDefaultCastTag<TParams...>) const
78 {
79 delete static_cast<TDefaultType*>(&obj);
80 }
81
82 template <typename TObj, typename... TParams>
83 void handleInternal(TObj& obj, DefaultCastCheckTag<TParams...>) const
84 {
85 using ObjType = typename std::decay<decltype(obj)>::type;
86 using Tag =
87 typename comms::util::LazyShallowConditional<
88 std::is_same<ObjType, TInterfaceType>::value
89 >::template Type<
90 ForcedDefaultCastTag,
91 NoDefaultCastTag
92 >;
93 handleInternal(obj, Tag());
94 }
95
96};
97
98template <typename TInterfaceType, typename TDefaultType>
99struct InPlaceDeleteHandler
100{
101 template <typename TObj>
102 void handle(TObj& obj) const
103 {
104 using HandleTag =
105 typename comms::util::LazyShallowConditional<
106 std::is_void<TDefaultType>::value
107 >::template Type<
108 NoDefaultCastTag,
109 DefaultCastCheckTag
110 >;
111
112 handleInternal(obj, HandleTag());
113 }
114
115private:
116 template <typename... TParams>
117 using NoDefaultCastTag = comms::details::tag::Tag1<>;
118
119 template <typename... TParams>
120 using DefaultCastCheckTag = comms::details::tag::Tag2<>;
121
122 template <typename... TParams>
123 using ForcedDefaultCastTag = comms::details::tag::Tag3<>;
124
125 template <typename TObj, typename... TParams>
126 void handleInternal(TObj& obj, NoDefaultCastTag<TParams...>) const
127 {
128 static_cast<void>(obj);
129 obj.~TObj();
130 }
131
132 template <typename TObj, typename... TParams>
133 void handleInternal(TObj& obj, ForcedDefaultCastTag<TParams...>) const
134 {
135 static_cast<TDefaultType&>(obj).~TDefaultType();
136 }
137
138 template <typename TObj, typename... TParams>
139 void handleInternal(TObj& obj, DefaultCastCheckTag<TParams...>) const
140 {
141 using ObjType = typename std::decay<decltype(obj)>::type;
142 using Tag =
143 typename comms::util::LazyShallowConditional<
144 std::is_same<ObjType, TInterfaceType>::value
145 >::template Type<
146 ForcedDefaultCastTag,
147 NoDefaultCastTag
148 >;
149 handleInternal(obj, Tag());
150 }
151};
152
153template <
154 typename TInterface,
155 typename TAllMessages,
156 typename TDeleteHandler,
157 typename TId>
158class NoVirtualDestructorDeleter
159{
160 static const unsigned InvalidIdx = std::numeric_limits<unsigned>::max();
161public:
162 NoVirtualDestructorDeleter() : id_(TId()), idx_(InvalidIdx) {}
163 NoVirtualDestructorDeleter(TId id, unsigned idx) : id_(id), idx_(idx) {}
164
165 void operator()(TInterface* obj)
166 {
167 COMMS_ASSERT(obj != nullptr);
168 COMMS_ASSERT(idx_ != InvalidIdx);
169 TDeleteHandler handler;
170 comms::dispatchMsgStaticBinSearch<TAllMessages>(id_, idx_, *obj, handler);
171 }
172private:
173 TId id_;
174 unsigned idx_ = 0;
175};
176
177template <
178 typename TInterface,
179 typename TAllMessages,
180 typename TDeleteHandler,
181 typename TId>
182class NoVirtualDestructorInPlaceDeleter : public
183 NoVirtualDestructorDeleter<TInterface, TAllMessages, TDeleteHandler, TId>
184{
185 using Base = NoVirtualDestructorDeleter<TInterface, TAllMessages, TDeleteHandler, TId>;
186 static const unsigned InvalidIdx = std::numeric_limits<unsigned>::max();
187public:
188 NoVirtualDestructorInPlaceDeleter() = default;
189 NoVirtualDestructorInPlaceDeleter(TId id, unsigned idx, bool& allocated) : Base(id, idx), allocated_(&allocated) {}
190
191 NoVirtualDestructorInPlaceDeleter(const NoVirtualDestructorInPlaceDeleter&) = delete;
192 NoVirtualDestructorInPlaceDeleter(NoVirtualDestructorInPlaceDeleter&& other) :
193 Base(std::move(other)),
194 allocated_(other.allocated_)
195 {
196 other.allocated_ = nullptr;
197 }
198
199 ~NoVirtualDestructorInPlaceDeleter()
200 {
201 }
202
203 NoVirtualDestructorInPlaceDeleter& operator=(const NoVirtualDestructorInPlaceDeleter&) = delete;
204 NoVirtualDestructorInPlaceDeleter& operator=(NoVirtualDestructorInPlaceDeleter&& other)
205 {
206 if (reinterpret_cast<void*>(this) == reinterpret_cast<const void*>(&other)) {
207 return *this;
208 }
209
210 Base::operator=(std::move(other));
211 allocated_ = other.allocated_;
212 other.allocated_ = nullptr;
213 return *this;
214 }
215
216 void operator()(TInterface* obj)
217 {
218 COMMS_ASSERT(allocated_ != nullptr);
219 COMMS_ASSERT(*allocated_);
220 Base::operator()(obj);
221 *allocated_ = false;
222 allocated_ = nullptr;
223 }
224private:
225 bool* allocated_ = nullptr;
226};
227
228template <typename T>
229class InPlaceDeleter
230{
231 template<typename U>
232 friend class InPlaceDeleter;
233
234public:
235 InPlaceDeleter(bool* allocated = nullptr)
236 : allocated_(allocated)
237 {
238 }
239
240 InPlaceDeleter(const InPlaceDeleter& other) = delete;
241
242 template <typename U>
243 InPlaceDeleter(InPlaceDeleter<U>&& other)
244 : allocated_(other.allocated_)
245 {
246 static_assert(std::is_base_of<T, U>::value ||
247 std::is_base_of<U, T>::value ||
248 std::is_convertible<U, T>::value ||
249 std::is_convertible<T, U>::value ,
250 "To make Deleter convertible, their template parameters "
251 "must be convertible.");
252
253 other.allocated_ = nullptr;
254 }
255
256 ~InPlaceDeleter() noexcept
257 {
258 }
259
260 InPlaceDeleter& operator=(const InPlaceDeleter& other) = delete;
261
262 template <typename U>
263 InPlaceDeleter& operator=(InPlaceDeleter<U>&& other)
264 {
265 static_assert(std::is_base_of<T, U>::value ||
266 std::is_base_of<U, T>::value ||
267 std::is_convertible<U, T>::value ||
268 std::is_convertible<T, U>::value ,
269 "To make Deleter convertible, their template parameters "
270 "must be convertible.");
271
272 if (reinterpret_cast<void*>(this) == reinterpret_cast<const void*>(&other)) {
273 return *this;
274 }
275
276 COMMS_ASSERT(allocated_ == nullptr);
277 allocated_ = other.allocated_;
278 other.allocated_ = nullptr;
279 return *this;
280 }
281
282 void operator()(T* obj) {
283 COMMS_ASSERT(allocated_ != nullptr);
284 COMMS_ASSERT(*allocated_);
285 obj->~T();
286 *allocated_ = false;
287 allocated_ = nullptr;
288 }
289
290private:
291 bool* allocated_ = nullptr;
292};
293
294
295} // namespace details
296
302template <typename TInterface>
304{
305public:
307 using Ptr = std::unique_ptr<TInterface>;
308
315 template <typename TObj, typename... TArgs>
316 static Ptr alloc(TArgs&&... args)
317 {
318 static_assert(std::is_base_of<TInterface, TObj>::value,
319 "TObj does not inherit from TInterface");
320 return Ptr(new TObj(std::forward<TArgs>(args)...));
321 }
322
328 template <typename TObj>
329 static Ptr wrap(TObj* obj)
330 {
331 static_assert(std::is_base_of<TInterface, TObj>::value,
332 "TObj does not inherit from TInterface");
333 return Ptr(obj);
334 }
335
338 static constexpr bool canAllocate()
339 {
340 return true;
341 }
342};
343
353template <typename TInterface, typename TAllMessages, typename TId, typename TDefaultType = void>
355{
356 using Deleter =
357 details::NoVirtualDestructorDeleter<
358 TInterface,
359 TAllMessages,
360 details::DynMemoryDeleteHandler<TInterface, TDefaultType>,
361 TId>;
362public:
364 using Ptr = std::unique_ptr<TInterface, Deleter>;
365
375 template <typename TObj, typename... TArgs>
376 static Ptr alloc(TId id, unsigned idx, TArgs&&... args)
377 {
378 static_assert(std::is_base_of<TInterface, TObj>::value,
379 "TObj does not inherit from TInterface");
380 return Ptr(new TObj(std::forward<TArgs>(args)...), Deleter(id, idx));
381 }
382
385 static constexpr bool canAllocate()
386 {
387 return true;
388 }
389};
390
401template <typename TInterface, typename TAllTypes>
403{
404public:
408 using Ptr = std::unique_ptr<TInterface, details::InPlaceDeleter<TInterface> >;
409
412 {
413 // Not supposed to be destructed while elemenent is still allocated
414 COMMS_ASSERT(!allocated_);
415 }
416
425 template <typename TObj, typename... TArgs>
426 Ptr alloc(TArgs&&... args)
427 {
428 if (allocated_) {
429 return Ptr();
430 }
431
432 static_assert(std::is_base_of<TInterface, TObj>::value,
433 "TObj does not inherit from TInterface");
434
435 static_assert(comms::util::IsInTuple<TAllTypes>::template Type<TObj>::value,
436 "TObj must be in provided tuple of supported types");
437
438 static_assert(
439 std::has_virtual_destructor<TInterface>::value ||
440 std::is_same<TInterface, TObj>::value,
441 "TInterface is expected to have virtual destructor");
442
443 static_assert(sizeof(TObj) <= sizeof(place_), "Object is too big");
444
445 new (&place_) TObj(std::forward<TArgs>(args)...);
446 Ptr obj(
447 reinterpret_cast<TInterface*>(&place_),
448 details::InPlaceDeleter<TInterface>(&allocated_));
449 allocated_ = true;
450 return obj;
451 }
452
454 bool allocated() const
455 {
456 return allocated_;
457 }
458
460 const void* allocAddr() const
461 {
462 return &place_;
463 }
464
470 template <typename TObj>
471 Ptr wrap(TObj* obj)
472 {
473 if (obj == nullptr) {
474 return Ptr();
475 }
476
477 static_assert(std::is_base_of<TInterface, TObj>::value,
478 "TObj does not inherit from TInterface");
479 COMMS_ASSERT(obj == reinterpret_cast<TInterface*>(&place_)); // Wrong object if fails
480 COMMS_ASSERT(allocated_); // Error if not set
481 return Ptr(
482 reinterpret_cast<TInterface*>(&place_),
483 details::InPlaceDeleter<TInterface>(&allocated_));
484 }
485
487 bool canAllocate() const
488 {
489 return !allocated_;
490 }
491
492private:
493 using AlignedStorage = typename TupleAsAlignedUnion<TAllTypes>::Type;
494
495 alignas(8) AlignedStorage place_;
496 bool allocated_ = false;
497
498};
499
515template <
516 typename TInterface,
517 typename TAllocMessages,
518 typename TOrigMessages,
519 typename TId,
520 typename TDefaultType = void>
522{
523 using Deleter =
524 details::NoVirtualDestructorInPlaceDeleter<
525 TInterface,
526 TOrigMessages,
527 details::InPlaceDeleteHandler<TInterface, TDefaultType>,
528 TId>;
529
530public:
534 using Ptr = std::unique_ptr<TInterface, Deleter>;
535
545 template <typename TObj, typename... TArgs>
546 Ptr alloc(TId id, unsigned idx, TArgs&&... args)
547 {
548 if (allocated_) {
549 return Ptr();
550 }
551
552 static_assert(std::is_base_of<TInterface, TObj>::value,
553 "TObj does not inherit from TInterface");
554
555 static_assert(comms::util::IsInTuple<TAllocMessages>::template Type<TObj>::value, ""
556 "TObj must be in provided tuple of supported types");
557
558 static_assert(sizeof(TObj) <= sizeof(place_), "Object is too big");
559
560 new (&place_) TObj(std::forward<TArgs>(args)...);
561 Ptr obj(
562 reinterpret_cast<TInterface*>(&place_),
563 Deleter(id, idx, allocated_));
564 allocated_ = true;
565 return obj;
566 }
567
569 bool allocated() const
570 {
571 return allocated_;
572 }
573
575 const void* allocAddr() const
576 {
577 return &place_;
578 }
579
581 bool canAllocate() const
582 {
583 return !allocated_;
584 }
585
586private:
587 using AlignedStorage = typename TupleAsAlignedUnion<TAllocMessages>::Type;
588
589 alignas(8) AlignedStorage place_;
590 bool allocated_ = false;
591
592};
593
602template <typename TInterface, std::size_t TSize, typename TAllTypes = std::tuple<TInterface> >
604{
606 using Pool = std::array<PoolElem, TSize>;
607public:
608
611 using Ptr = typename PoolElem::Ptr;
612
614 template <typename TObj, typename... TArgs>
615 Ptr alloc(TArgs&&... args)
616 {
617 auto iter = std::find_if(
618 pool_.begin(), pool_.end(),
619 [](const PoolElem& elem) -> bool
620 {
621 return !elem.allocated();
622 });
623
624 if (iter == pool_.end()) {
625 return Ptr();
626 }
627
628 return iter->template alloc<TObj>(std::forward<TArgs>(args)...);
629 }
630
636 template <typename TObj>
637 Ptr wrap(TObj* obj)
638 {
639 auto iter =
640 std::find_if(
641 pool_.begin(), pool_.end(),
642 [obj](const PoolElem& elem) -> bool
643 {
644 return elem.allocated() && (elem.allocAddr() == obj);
645 });
646
647 if (iter == pool_.end()) {
648 return Ptr();
649 }
650
651 return iter->wrap(obj);
652 }
653
654private:
655 Pool pool_;
656};
657
658namespace details
659{
660
661template <typename...>
662struct InPlaceSingleDeepCondWrap
663{
664 template <typename TInterface, typename TAllTypes, typename...>
666};
667
668template <typename...>
669struct InPlaceSingleNoVirtualDestructorDeepCondWrap
670{
671 template <
672 typename TInterface,
673 typename TAllocMessages,
674 typename TOrigMessages,
675 typename TId,
676 typename TDefaultType,
677 typename...>
678 using Type =
680 TInterface,
681 TAllocMessages,
682 TOrigMessages,
683 TId,
684 TDefaultType
685 >;
686};
687
688template <typename...>
689struct DynMemoryDeepCondWrap
690{
691 template <typename TInterface, typename...>
693};
694
695template <typename...>
696struct DynMemoryNoVirtualDestructorDeepCondWrap
697{
698 template <
699 typename TInterface,
700 typename TAllMessages,
701 typename TId,
702 typename TDefaultType,
703 typename...
704 >
705 using Type =
707 TInterface,
708 TAllMessages,
709 TId,
710 TDefaultType
711 >;
712};
713
714
715} // namespace details
716
717} // namespace alloc
718
719} // namespace util
720
721} // namespace comms
722
723COMMS_MSVC_WARNING_POP
This file contains classes required for generic custom assertion functionality.
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:170
Contains various compiler related definitions.
Contains various tuple type manipulation classes and functions.
Dynamic memory allocator for message types without virtual destructor.
Definition alloc.h:355
std::unique_ptr< TInterface, Deleter > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:364
static Ptr alloc(TId id, unsigned idx, TArgs &&... args)
Allocation function.
Definition alloc.h:376
static constexpr bool canAllocate()
Inquiry whether allocation is possible.
Definition alloc.h:385
Dynamic memory allocator.
Definition alloc.h:304
static Ptr wrap(TObj *obj)
Function used to wrap raw pointer into a smart one.
Definition alloc.h:329
std::unique_ptr< TInterface > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:307
static Ptr alloc(TArgs &&... args)
Allocation function.
Definition alloc.h:316
static constexpr bool canAllocate()
Inquiry whether allocation is possible.
Definition alloc.h:338
In-place object pool allocator.
Definition alloc.h:604
Ptr wrap(TObj *obj)
Function used to wrap raw pointer into a smart one.
Definition alloc.h:637
Ptr alloc(TArgs &&... args)
Allocation function.
Definition alloc.h:615
typename PoolElem::Ptr Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:611
In-place single object allocator for message objects without virtual destructor.
Definition alloc.h:522
Ptr alloc(TId id, unsigned idx, TArgs &&... args)
Allocation function.
Definition alloc.h:546
bool canAllocate() const
Inquiry whether allocation is possible.
Definition alloc.h:581
std::unique_ptr< TInterface, Deleter > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:534
const void * allocAddr() const
Get address of the objects being allocated using this allocator.
Definition alloc.h:575
bool allocated() const
Inquire whether the object is already allocated.
Definition alloc.h:569
In-place single object allocator.
Definition alloc.h:403
bool allocated() const
Inquire whether the object is already allocated.
Definition alloc.h:454
~InPlaceSingle()
Destructor.
Definition alloc.h:411
Ptr wrap(TObj *obj)
Function used to wrap raw pointer into a smart one.
Definition alloc.h:471
bool canAllocate() const
Inquiry whether allocation is possible.
Definition alloc.h:487
const void * allocAddr() const
Get address of the objects being allocated using this allocator.
Definition alloc.h:460
std::unique_ptr< TInterface, details::InPlaceDeleter< TInterface > > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:408
Ptr alloc(TArgs &&... args)
Allocation function.
Definition alloc.h:426
Contains extra logic to help with dispatching message types and objects.
Main namespace for all classes / functions of COMMS library.
STL namespace.
Check whether TType type is included in the tuple TTuple.
Definition Tuple.h:95
void Type
Type definition is invalid for any type that is not std::tuple, will be specialised to proper value.
Definition Tuple.h:166
Replacement to some types from standard type_traits.