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