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