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 - 2025 (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#pragma once
13
15#include "comms/Assert.h"
16#include "comms/dispatch.h"
17#include "comms/util/Tuple.h"
19#include "comms/details/tag.h"
20
21#include <algorithm>
22#include <limits>
23#include <array>
24#include <memory>
25#include <type_traits>
26#include <utility>
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() : m_id(TId()), m_idx(InvalidIdx) {}
163 NoVirtualDestructorDeleter(TId id, unsigned idx) : m_id(id), m_idx(idx) {}
164
165 void operator()(TInterface* obj)
166 {
167 COMMS_ASSERT(obj != nullptr);
168 COMMS_ASSERT(m_idx != InvalidIdx);
169 TDeleteHandler handler;
170 comms::dispatchMsgStaticBinSearch<TAllMessages>(m_id, m_idx, *obj, handler);
171 }
172private:
173 TId m_id;
174 unsigned m_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), m_allocated(&allocated) {}
190
191 NoVirtualDestructorInPlaceDeleter(const NoVirtualDestructorInPlaceDeleter&) = delete;
192 NoVirtualDestructorInPlaceDeleter(NoVirtualDestructorInPlaceDeleter&& other) :
193 Base(std::move(other)),
194 m_allocated(other.m_allocated)
195 {
196 other.m_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 m_allocated = other.m_allocated;
212 other.m_allocated = nullptr;
213 return *this;
214 }
215
216 void operator()(TInterface* obj)
217 {
218 COMMS_ASSERT(m_allocated != nullptr);
219 COMMS_ASSERT(*m_allocated);
220 Base::operator()(obj);
221 *m_allocated = false;
222 m_allocated = nullptr;
223 }
224private:
225 bool* m_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 : m_allocated(allocated)
237 {
238 }
239
240 InPlaceDeleter(const InPlaceDeleter& other) = delete;
241
242 template <typename U>
243 InPlaceDeleter(InPlaceDeleter<U>&& other)
244 : m_allocated(other.m_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.m_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(m_allocated == nullptr);
277 m_allocated = other.m_allocated;
278 other.m_allocated = nullptr;
279 return *this;
280 }
281
282 void operator()(T* obj) {
283 COMMS_ASSERT(m_allocated != nullptr);
284 COMMS_ASSERT(*m_allocated);
285 obj->~T();
286 *m_allocated = false;
287 m_allocated = nullptr;
288 }
289
290private:
291 bool* m_allocated = nullptr;
292};
293
294} // namespace details
295
301template <typename TInterface>
303{
304public:
306 using Ptr = std::unique_ptr<TInterface>;
307
314 template <typename TObj, typename... TArgs>
315 static Ptr alloc(TArgs&&... args)
316 {
317 static_assert(std::is_base_of<TInterface, TObj>::value,
318 "TObj does not inherit from TInterface");
319 return Ptr(new TObj(std::forward<TArgs>(args)...));
320 }
321
327 template <typename TObj>
328 static Ptr wrap(TObj* obj)
329 {
330 static_assert(std::is_base_of<TInterface, TObj>::value,
331 "TObj does not inherit from TInterface");
332 return Ptr(obj);
333 }
334
337 static constexpr bool canAllocate()
338 {
339 return true;
340 }
341};
342
352template <typename TInterface, typename TAllMessages, typename TId, typename TDefaultType = void>
354{
355 using Deleter =
356 details::NoVirtualDestructorDeleter<
357 TInterface,
358 TAllMessages,
359 details::DynMemoryDeleteHandler<TInterface, TDefaultType>,
360 TId>;
361public:
363 using Ptr = std::unique_ptr<TInterface, Deleter>;
364
374 template <typename TObj, typename... TArgs>
375 static Ptr alloc(TId id, unsigned idx, TArgs&&... args)
376 {
377 static_assert(std::is_base_of<TInterface, TObj>::value,
378 "TObj does not inherit from TInterface");
379 return Ptr(new TObj(std::forward<TArgs>(args)...), Deleter(id, idx));
380 }
381
384 static constexpr bool canAllocate()
385 {
386 return true;
387 }
388};
389
400template <typename TInterface, typename TAllTypes>
402{
403public:
407 using Ptr = std::unique_ptr<TInterface, details::InPlaceDeleter<TInterface> >;
408
411 {
412 // Not supposed to be destructed while elemenent is still allocated
413 COMMS_ASSERT(!m_allocated);
414 }
415
424 template <typename TObj, typename... TArgs>
425 Ptr alloc(TArgs&&... args)
426 {
427 if (m_allocated) {
428 return Ptr();
429 }
430
431 static_assert(std::is_base_of<TInterface, TObj>::value,
432 "TObj does not inherit from TInterface");
433
434 static_assert(comms::util::IsInTuple<TAllTypes>::template Type<TObj>::value,
435 "TObj must be in provided tuple of supported types");
436
437 static_assert(
438 std::has_virtual_destructor<TInterface>::value ||
439 std::is_same<TInterface, TObj>::value,
440 "TInterface is expected to have virtual destructor");
441
442 static_assert(sizeof(TObj) <= sizeof(m_place), "Object is too big");
443
444 new (&m_place) TObj(std::forward<TArgs>(args)...);
445 Ptr obj(
446 reinterpret_cast<TInterface*>(&m_place),
447 details::InPlaceDeleter<TInterface>(&m_allocated));
448 m_allocated = true;
449 return obj;
450 }
451
453 bool allocated() const
454 {
455 return m_allocated;
456 }
457
459 const void* allocAddr() const
460 {
461 return &m_place;
462 }
463
469 template <typename TObj>
470 Ptr wrap(TObj* obj)
471 {
472 if (obj == nullptr) {
473 return Ptr();
474 }
475
476 static_assert(std::is_base_of<TInterface, TObj>::value,
477 "TObj does not inherit from TInterface");
478 COMMS_ASSERT(obj == reinterpret_cast<TInterface*>(&m_place)); // Wrong object if fails
479 COMMS_ASSERT(m_allocated); // Error if not set
480 return Ptr(
481 reinterpret_cast<TInterface*>(&m_place),
482 details::InPlaceDeleter<TInterface>(&m_allocated));
483 }
484
486 bool canAllocate() const
487 {
488 return !m_allocated;
489 }
490
491private:
492 using AlignedStorage = typename TupleAsAlignedUnion<TAllTypes>::Type;
493
494 alignas(8) AlignedStorage m_place;
495 bool m_allocated = false;
496
497};
498
514template <
515 typename TInterface,
516 typename TAllocMessages,
517 typename TOrigMessages,
518 typename TId,
519 typename TDefaultType = void>
521{
522 using Deleter =
523 details::NoVirtualDestructorInPlaceDeleter<
524 TInterface,
525 TOrigMessages,
526 details::InPlaceDeleteHandler<TInterface, TDefaultType>,
527 TId>;
528
529public:
533 using Ptr = std::unique_ptr<TInterface, Deleter>;
534
544 template <typename TObj, typename... TArgs>
545 Ptr alloc(TId id, unsigned idx, TArgs&&... args)
546 {
547 if (m_allocated) {
548 return Ptr();
549 }
550
551 static_assert(std::is_base_of<TInterface, TObj>::value,
552 "TObj does not inherit from TInterface");
553
554 static_assert(comms::util::IsInTuple<TAllocMessages>::template Type<TObj>::value, ""
555 "TObj must be in provided tuple of supported types");
556
557 static_assert(sizeof(TObj) <= sizeof(m_place), "Object is too big");
558
559 new (&m_place) TObj(std::forward<TArgs>(args)...);
560 Ptr obj(
561 reinterpret_cast<TInterface*>(&m_place),
562 Deleter(id, idx, m_allocated));
563 m_allocated = true;
564 return obj;
565 }
566
568 bool allocated() const
569 {
570 return m_allocated;
571 }
572
574 const void* allocAddr() const
575 {
576 return &m_place;
577 }
578
580 bool canAllocate() const
581 {
582 return !m_allocated;
583 }
584
585private:
586 using AlignedStorage = typename TupleAsAlignedUnion<TAllocMessages>::Type;
587
588 alignas(8) AlignedStorage m_place;
589 bool m_allocated = false;
590
591};
592
601template <typename TInterface, std::size_t TSize, typename TAllTypes = std::tuple<TInterface> >
603{
605 using Pool = std::array<PoolElem, TSize>;
606public:
607
610 using Ptr = typename PoolElem::Ptr;
611
613 template <typename TObj, typename... TArgs>
614 Ptr alloc(TArgs&&... args)
615 {
616 auto iter = std::find_if(
617 m_pool.begin(), m_pool.end(),
618 [](const PoolElem& elem) -> bool
619 {
620 return !elem.allocated();
621 });
622
623 if (iter == m_pool.end()) {
624 return Ptr();
625 }
626
627 return iter->template alloc<TObj>(std::forward<TArgs>(args)...);
628 }
629
635 template <typename TObj>
636 Ptr wrap(TObj* obj)
637 {
638 auto iter =
639 std::find_if(
640 m_pool.begin(), m_pool.end(),
641 [obj](const PoolElem& elem) -> bool
642 {
643 return elem.allocated() && (elem.allocAddr() == obj);
644 });
645
646 if (iter == m_pool.end()) {
647 return Ptr();
648 }
649
650 return iter->wrap(obj);
651 }
652
653private:
654 Pool m_pool;
655};
656
657namespace details
658{
659
660template <typename...>
661struct InPlaceSingleDeepCondWrap
662{
663 template <typename TInterface, typename TAllTypes, typename...>
665};
666
667template <typename...>
668struct InPlaceSingleNoVirtualDestructorDeepCondWrap
669{
670 template <
671 typename TInterface,
672 typename TAllocMessages,
673 typename TOrigMessages,
674 typename TId,
675 typename TDefaultType,
676 typename...>
677 using Type =
679 TInterface,
680 TAllocMessages,
681 TOrigMessages,
682 TId,
683 TDefaultType
684 >;
685};
686
687template <typename...>
688struct DynMemoryDeepCondWrap
689{
690 template <typename TInterface, typename...>
692};
693
694template <typename...>
695struct DynMemoryNoVirtualDestructorDeepCondWrap
696{
697 template <
698 typename TInterface,
699 typename TAllMessages,
700 typename TId,
701 typename TDefaultType,
702 typename...
703 >
704 using Type =
706 TInterface,
707 TAllMessages,
708 TId,
709 TDefaultType
710 >;
711};
712
713} // namespace details
714
715} // namespace alloc
716
717} // namespace util
718
719} // namespace comms
720
721COMMS_MSVC_WARNING_POP
This file contains classes required for generic custom assertion functionality.
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:168
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:354
std::unique_ptr< TInterface, Deleter > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:363
static Ptr alloc(TId id, unsigned idx, TArgs &&... args)
Allocation function.
Definition alloc.h:375
static constexpr bool canAllocate()
Inquiry whether allocation is possible.
Definition alloc.h:384
Dynamic memory allocator.
Definition alloc.h:303
static Ptr wrap(TObj *obj)
Function used to wrap raw pointer into a smart one.
Definition alloc.h:328
std::unique_ptr< TInterface > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:306
static Ptr alloc(TArgs &&... args)
Allocation function.
Definition alloc.h:315
static constexpr bool canAllocate()
Inquiry whether allocation is possible.
Definition alloc.h:337
In-place object pool allocator.
Definition alloc.h:603
Ptr wrap(TObj *obj)
Function used to wrap raw pointer into a smart one.
Definition alloc.h:636
Ptr alloc(TArgs &&... args)
Allocation function.
Definition alloc.h:614
typename PoolElem::Ptr Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:610
In-place single object allocator for message objects without virtual destructor.
Definition alloc.h:521
Ptr alloc(TId id, unsigned idx, TArgs &&... args)
Allocation function.
Definition alloc.h:545
bool canAllocate() const
Inquiry whether allocation is possible.
Definition alloc.h:580
std::unique_ptr< TInterface, Deleter > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:533
const void * allocAddr() const
Get address of the objects being allocated using this allocator.
Definition alloc.h:574
bool allocated() const
Inquire whether the object is already allocated.
Definition alloc.h:568
In-place single object allocator.
Definition alloc.h:402
bool allocated() const
Inquire whether the object is already allocated.
Definition alloc.h:453
~InPlaceSingle()
Destructor.
Definition alloc.h:410
Ptr wrap(TObj *obj)
Function used to wrap raw pointer into a smart one.
Definition alloc.h:470
bool canAllocate() const
Inquiry whether allocation is possible.
Definition alloc.h:486
const void * allocAddr() const
Get address of the objects being allocated using this allocator.
Definition alloc.h:459
std::unique_ptr< TInterface, details::InPlaceDeleter< TInterface > > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition alloc.h:407
Ptr alloc(TArgs &&... args)
Allocation function.
Definition alloc.h:425
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.