COMMS
Template library intended to help with implementation of communication protocols.
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 
21 #include "comms/Assert.h"
22 #include "comms/dispatch.h"
23 #include "comms/util/Tuple.h"
24 #include "comms/util/type_traits.h"
25 #include "comms/details/tag.h"
26 
27 namespace comms
28 {
29 
30 namespace util
31 {
32 
33 namespace alloc
34 {
35 
36 namespace details
37 {
38 
39 template <typename TInterfaceType, typename TDefaultType>
40 struct DynMemoryDeleteHandler
41 {
42  template <typename TObj>
43  void handle(TObj& obj) const
44  {
45  using HandleTag =
46  typename comms::util::LazyShallowConditional<
47  std::is_void<TDefaultType>::value
48  >::template Type<
49  NoDefaultCastTag,
50  DefaultCastCheckTag
51  >;
52 
53  handleInternal(obj, HandleTag());
54  }
55 
56 private:
57  template <typename... TParams>
58  using NoDefaultCastTag = comms::details::tag::Tag1<>;
59 
60  template <typename... TParams>
61  using DefaultCastCheckTag = comms::details::tag::Tag2<>;
62 
63  template <typename... TParams>
64  using ForcedDefaultCastTag = comms::details::tag::Tag3<>;
65 
66  template <typename TObj, typename... TParams>
67  void handleInternal(TObj& obj, NoDefaultCastTag<TParams...>) const
68  {
69  delete (&obj);
70  }
71 
72  template <typename TObj, typename... TParams>
73  void handleInternal(TObj& obj, ForcedDefaultCastTag<TParams...>) const
74  {
75  delete static_cast<TDefaultType*>(&obj);
76  }
77 
78  template <typename TObj, typename... TParams>
79  void handleInternal(TObj& obj, DefaultCastCheckTag<TParams...>) const
80  {
81  using ObjType = typename std::decay<decltype(obj)>::type;
82  using Tag =
83  typename comms::util::LazyShallowConditional<
84  std::is_same<ObjType, TInterfaceType>::value
85  >::template Type<
86  ForcedDefaultCastTag,
87  NoDefaultCastTag
88  >;
89  handleInternal(obj, Tag());
90  }
91 
92 };
93 
94 template <typename TInterfaceType, typename TDefaultType>
95 struct InPlaceDeleteHandler
96 {
97  template <typename TObj>
98  void handle(TObj& obj) const
99  {
100  using HandleTag =
101  typename comms::util::LazyShallowConditional<
102  std::is_void<TDefaultType>::value
103  >::template Type<
104  NoDefaultCastTag,
105  DefaultCastCheckTag
106  >;
107 
108  handleInternal(obj, HandleTag());
109  }
110 
111 private:
112  template <typename... TParams>
113  using NoDefaultCastTag = comms::details::tag::Tag1<>;
114 
115  template <typename... TParams>
116  using DefaultCastCheckTag = comms::details::tag::Tag2<>;
117 
118  template <typename... TParams>
119  using ForcedDefaultCastTag = comms::details::tag::Tag3<>;
120 
121  template <typename TObj, typename... TParams>
122  void handleInternal(TObj& obj, NoDefaultCastTag<TParams...>) const
123  {
124  static_cast<void>(obj);
125  obj.~TObj();
126  }
127 
128  template <typename TObj, typename... TParams>
129  void handleInternal(TObj& obj, ForcedDefaultCastTag<TParams...>) const
130  {
131  static_cast<TDefaultType&>(obj).~TDefaultType();
132  }
133 
134  template <typename TObj, typename... TParams>
135  void handleInternal(TObj& obj, DefaultCastCheckTag<TParams...>) const
136  {
137  using ObjType = typename std::decay<decltype(obj)>::type;
138  using Tag =
139  typename comms::util::LazyShallowConditional<
140  std::is_same<ObjType, TInterfaceType>::value
141  >::template Type<
142  ForcedDefaultCastTag,
143  NoDefaultCastTag
144  >;
145  handleInternal(obj, Tag());
146  }
147 };
148 
149 template <
150  typename TInterface,
151  typename TAllMessages,
152  typename TDeleteHandler,
153  typename TId>
154 class NoVirtualDestructorDeleter
155 {
156  static const unsigned InvalidIdx = std::numeric_limits<unsigned>::max();
157 public:
158  NoVirtualDestructorDeleter() : id_(TId()), idx_(InvalidIdx) {}
159  NoVirtualDestructorDeleter(TId id, unsigned idx) : id_(id), idx_(idx) {}
160 
161  void operator()(TInterface* obj)
162  {
163  COMMS_ASSERT(obj != nullptr);
164  COMMS_ASSERT(idx_ != InvalidIdx);
165  TDeleteHandler handler;
166  comms::dispatchMsgStaticBinSearch<TAllMessages>(id_, idx_, *obj, handler);
167  }
168 private:
169  TId id_;
170  unsigned idx_ = 0;
171 };
172 
173 template <
174  typename TInterface,
175  typename TAllMessages,
176  typename TDeleteHandler,
177  typename TId>
178 class NoVirtualDestructorInPlaceDeleter : public
179  NoVirtualDestructorDeleter<TInterface, TAllMessages, TDeleteHandler, TId>
180 {
181  using Base = NoVirtualDestructorDeleter<TInterface, TAllMessages, TDeleteHandler, TId>;
182  static const unsigned InvalidIdx = std::numeric_limits<unsigned>::max();
183 public:
184  NoVirtualDestructorInPlaceDeleter() = default;
185  NoVirtualDestructorInPlaceDeleter(TId id, unsigned idx, bool& allocated) : Base(id, idx), allocated_(&allocated) {}
186 
187  NoVirtualDestructorInPlaceDeleter(const NoVirtualDestructorInPlaceDeleter&) = delete;
188  NoVirtualDestructorInPlaceDeleter(NoVirtualDestructorInPlaceDeleter&& other) :
189  Base(std::move(other)),
190  allocated_(other.allocated_)
191  {
192  other.allocated_ = nullptr;
193  }
194 
195  ~NoVirtualDestructorInPlaceDeleter()
196  {
197  }
198 
199  NoVirtualDestructorInPlaceDeleter& operator=(const NoVirtualDestructorInPlaceDeleter&) = delete;
200  NoVirtualDestructorInPlaceDeleter& operator=(NoVirtualDestructorInPlaceDeleter&& other)
201  {
202  if (reinterpret_cast<void*>(this) == reinterpret_cast<const void*>(&other)) {
203  return *this;
204  }
205 
206  Base::operator=(std::move(other));
207  allocated_ = other.allocated_;
208  other.allocated_ = nullptr;
209  return *this;
210  }
211 
212  void operator()(TInterface* obj)
213  {
214  COMMS_ASSERT(allocated_ != nullptr);
215  COMMS_ASSERT(*allocated_);
216  Base::operator()(obj);
217  *allocated_ = false;
218  allocated_ = nullptr;
219  }
220 private:
221  bool* allocated_ = nullptr;
222 };
223 
224 template <typename T>
225 class InPlaceDeleter
226 {
227  template<typename U>
228  friend class InPlaceDeleter;
229 
230 public:
231  InPlaceDeleter(bool* allocated = nullptr)
232  : allocated_(allocated)
233  {
234  }
235 
236  InPlaceDeleter(const InPlaceDeleter& other) = delete;
237 
238  template <typename U>
239  InPlaceDeleter(InPlaceDeleter<U>&& other)
240  : allocated_(other.allocated_)
241  {
242  static_assert(std::is_base_of<T, U>::value ||
243  std::is_base_of<U, T>::value ||
244  std::is_convertible<U, T>::value ||
245  std::is_convertible<T, U>::value ,
246  "To make Deleter convertible, their template parameters "
247  "must be convertible.");
248 
249  other.allocated_ = nullptr;
250  }
251 
252  ~InPlaceDeleter() noexcept
253  {
254  }
255 
256  InPlaceDeleter& operator=(const InPlaceDeleter& other) = delete;
257 
258  template <typename U>
259  InPlaceDeleter& operator=(InPlaceDeleter<U>&& other)
260  {
261  static_assert(std::is_base_of<T, U>::value ||
262  std::is_base_of<U, T>::value ||
263  std::is_convertible<U, T>::value ||
264  std::is_convertible<T, U>::value ,
265  "To make Deleter convertible, their template parameters "
266  "must be convertible.");
267 
268  if (reinterpret_cast<void*>(this) == reinterpret_cast<const void*>(&other)) {
269  return *this;
270  }
271 
272  COMMS_ASSERT(allocated_ == nullptr);
273  allocated_ = other.allocated_;
274  other.allocated_ = nullptr;
275  return *this;
276  }
277 
278  void operator()(T* obj) {
279  COMMS_ASSERT(allocated_ != nullptr);
280  COMMS_ASSERT(*allocated_);
281  obj->~T();
282  *allocated_ = false;
283  allocated_ = nullptr;
284  }
285 
286 private:
287  bool* allocated_ = nullptr;
288 };
289 
290 
291 } // namespace details
292 
298 template <typename TInterface>
300 {
301 public:
303  using Ptr = std::unique_ptr<TInterface>;
304 
311  template <typename TObj, typename... TArgs>
312  static Ptr alloc(TArgs&&... args)
313  {
314  static_assert(std::is_base_of<TInterface, TObj>::value,
315  "TObj does not inherit from TInterface");
316  return Ptr(new TObj(std::forward<TArgs>(args)...));
317  }
318 
324  template <typename TObj>
325  static Ptr wrap(TObj* obj)
326  {
327  static_assert(std::is_base_of<TInterface, TObj>::value,
328  "TObj does not inherit from TInterface");
329  return Ptr(obj);
330  }
331 
334  static constexpr bool canAllocate()
335  {
336  return true;
337  }
338 };
339 
349 template <typename TInterface, typename TAllMessages, typename TId, typename TDefaultType = void>
351 {
352  using Deleter =
353  details::NoVirtualDestructorDeleter<
354  TInterface,
355  TAllMessages,
356  details::DynMemoryDeleteHandler<TInterface, TDefaultType>,
357  TId>;
358 public:
360  using Ptr = std::unique_ptr<TInterface, Deleter>;
361 
371  template <typename TObj, typename... TArgs>
372  static Ptr alloc(TId id, unsigned idx, TArgs&&... args)
373  {
374  static_assert(std::is_base_of<TInterface, TObj>::value,
375  "TObj does not inherit from TInterface");
376  return Ptr(new TObj(std::forward<TArgs>(args)...), Deleter(id, idx));
377  }
378 
381  static constexpr bool canAllocate()
382  {
383  return true;
384  }
385 };
386 
397 template <typename TInterface, typename TAllTypes>
399 {
400 public:
404  using Ptr = std::unique_ptr<TInterface, details::InPlaceDeleter<TInterface> >;
405 
408  {
409  // Not supposed to be destructed while elemenent is still allocated
410  COMMS_ASSERT(!allocated_);
411  }
412 
421  template <typename TObj, typename... TArgs>
422  Ptr alloc(TArgs&&... args)
423  {
424  if (allocated_) {
425  return Ptr();
426  }
427 
428  static_assert(std::is_base_of<TInterface, TObj>::value,
429  "TObj does not inherit from TInterface");
430 
431  static_assert(comms::util::IsInTuple<TAllTypes>::template Type<TObj>::value,
432  "TObj must be in provided tuple of supported types");
433 
434  static_assert(
435  std::has_virtual_destructor<TInterface>::value ||
436  std::is_same<TInterface, TObj>::value,
437  "TInterface is expected to have virtual destructor");
438 
439  static_assert(sizeof(TObj) <= sizeof(place_), "Object is too big");
440 
441  new (&place_) TObj(std::forward<TArgs>(args)...);
442  Ptr obj(
443  reinterpret_cast<TInterface*>(&place_),
444  details::InPlaceDeleter<TInterface>(&allocated_));
445  allocated_ = true;
446  return obj;
447  }
448 
450  bool allocated() const
451  {
452  return allocated_;
453  }
454 
456  const void* allocAddr() const
457  {
458  return &place_;
459  }
460 
466  template <typename TObj>
467  Ptr wrap(TObj* obj)
468  {
469  if (obj == nullptr) {
470  return Ptr();
471  }
472 
473  static_assert(std::is_base_of<TInterface, TObj>::value,
474  "TObj does not inherit from TInterface");
475  COMMS_ASSERT(obj == reinterpret_cast<TInterface*>(&place_)); // Wrong object if fails
476  COMMS_ASSERT(allocated_); // Error if not set
477  return Ptr(
478  reinterpret_cast<TInterface*>(&place_),
479  details::InPlaceDeleter<TInterface>(&allocated_));
480  }
481 
483  bool canAllocate() const
484  {
485  return !allocated_;
486  }
487 
488 private:
489  using AlignedStorage = typename TupleAsAlignedUnion<TAllTypes>::Type;
490 
491  AlignedStorage place_;
492  bool allocated_ = false;
493 
494 };
495 
511 template <
512  typename TInterface,
513  typename TAllocMessages,
514  typename TOrigMessages,
515  typename TId,
516  typename TDefaultType = void>
518 {
519  using Deleter =
520  details::NoVirtualDestructorInPlaceDeleter<
521  TInterface,
522  TOrigMessages,
523  details::InPlaceDeleteHandler<TInterface, TDefaultType>,
524  TId>;
525 
526 public:
530  using Ptr = std::unique_ptr<TInterface, Deleter>;
531 
541  template <typename TObj, typename... TArgs>
542  Ptr alloc(TId id, unsigned idx, TArgs&&... args)
543  {
544  if (allocated_) {
545  return Ptr();
546  }
547 
548  static_assert(std::is_base_of<TInterface, TObj>::value,
549  "TObj does not inherit from TInterface");
550 
551  static_assert(comms::util::IsInTuple<TAllocMessages>::template Type<TObj>::value, ""
552  "TObj must be in provided tuple of supported types");
553 
554  static_assert(sizeof(TObj) <= sizeof(place_), "Object is too big");
555 
556  new (&place_) TObj(std::forward<TArgs>(args)...);
557  Ptr obj(
558  reinterpret_cast<TInterface*>(&place_),
559  Deleter(id, idx, allocated_));
560  allocated_ = true;
561  return obj;
562  }
563 
565  bool allocated() const
566  {
567  return allocated_;
568  }
569 
571  const void* allocAddr() const
572  {
573  return &place_;
574  }
575 
577  bool canAllocate() const
578  {
579  return !allocated_;
580  }
581 
582 private:
583  using AlignedStorage = typename TupleAsAlignedUnion<TAllocMessages>::Type;
584 
585  AlignedStorage place_;
586  bool allocated_ = false;
587 
588 };
589 
598 template <typename TInterface, std::size_t TSize, typename TAllTypes = std::tuple<TInterface> >
600 {
602  using Pool = std::array<PoolElem, TSize>;
603 public:
604 
607  using Ptr = typename PoolElem::Ptr;
608 
610  template <typename TObj, typename... TArgs>
611  Ptr alloc(TArgs&&... args)
612  {
613  auto iter = std::find_if(
614  pool_.begin(), pool_.end(),
615  [](const PoolElem& elem) -> bool
616  {
617  return !elem.allocated();
618  });
619 
620  if (iter == pool_.end()) {
621  return Ptr();
622  }
623 
624  return iter->template alloc<TObj>(std::forward<TArgs>(args)...);
625  }
626 
632  template <typename TObj>
633  Ptr wrap(TObj* obj)
634  {
635  auto iter =
636  std::find_if(
637  pool_.begin(), pool_.end(),
638  [obj](const PoolElem& elem) -> bool
639  {
640  return elem.allocated() && (elem.allocAddr() == obj);
641  });
642 
643  if (iter == pool_.end()) {
644  return Ptr();
645  }
646 
647  return iter->wrap(obj);
648  }
649 
650 private:
651  Pool pool_;
652 };
653 
654 namespace details
655 {
656 
657 template <typename...>
658 struct InPlaceSingleDeepCondWrap
659 {
660  template <typename TInterface, typename TAllTypes, typename...>
662 };
663 
664 template <typename...>
665 struct InPlaceSingleNoVirtualDestructorDeepCondWrap
666 {
667  template <
668  typename TInterface,
669  typename TAllocMessages,
670  typename TOrigMessages,
671  typename TId,
672  typename TDefaultType,
673  typename...>
674  using Type =
676  TInterface,
677  TAllocMessages,
678  TOrigMessages,
679  TId,
680  TDefaultType
681  >;
682 };
683 
684 template <typename...>
685 struct DynMemoryDeepCondWrap
686 {
687  template <typename TInterface, typename...>
689 };
690 
691 template <typename...>
692 struct DynMemoryNoVirtualDestructorDeepCondWrap
693 {
694  template <
695  typename TInterface,
696  typename TAllMessages,
697  typename TId,
698  typename TDefaultType,
699  typename...
700  >
701  using Type =
703  TInterface,
704  TAllMessages,
705  TId,
706  TDefaultType
707  >;
708 };
709 
710 
711 } // namespace details
712 
713 } // namespace alloc
714 
715 } // namespace util
716 
717 } // namespace comms
718 
719 
This file contains classes required for generic custom assertion functionality.
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition: Assert.h:170
Contains various tuple type manipulation classes and functions.
Dynamic memory allocator for message types without virtual destructor.
Definition: alloc.h:351
std::unique_ptr< TInterface, Deleter > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition: alloc.h:360
static Ptr alloc(TId id, unsigned idx, TArgs &&... args)
Allocation function.
Definition: alloc.h:372
static constexpr bool canAllocate()
Inquiry whether allocation is possible.
Definition: alloc.h:381
Dynamic memory allocator.
Definition: alloc.h:300
static Ptr wrap(TObj *obj)
Function used to wrap raw pointer into a smart one.
Definition: alloc.h:325
std::unique_ptr< TInterface > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition: alloc.h:303
static Ptr alloc(TArgs &&... args)
Allocation function.
Definition: alloc.h:312
static constexpr bool canAllocate()
Inquiry whether allocation is possible.
Definition: alloc.h:334
In-place object pool allocator.
Definition: alloc.h:600
Ptr wrap(TObj *obj)
Function used to wrap raw pointer into a smart one.
Definition: alloc.h:633
Ptr alloc(TArgs &&... args)
Allocation function.
Definition: alloc.h:611
typename PoolElem::Ptr Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition: alloc.h:607
In-place single object allocator for message objects without virtual destructor.
Definition: alloc.h:518
Ptr alloc(TId id, unsigned idx, TArgs &&... args)
Allocation function.
Definition: alloc.h:542
const void * allocAddr() const
Get address of the objects being allocated using this allocator.
Definition: alloc.h:571
bool canAllocate() const
Inquiry whether allocation is possible.
Definition: alloc.h:577
std::unique_ptr< TInterface, Deleter > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition: alloc.h:530
bool allocated() const
Inquire whether the object is already allocated.
Definition: alloc.h:565
In-place single object allocator.
Definition: alloc.h:399
bool allocated() const
Inquire whether the object is already allocated.
Definition: alloc.h:450
~InPlaceSingle()
Destructor.
Definition: alloc.h:407
Ptr wrap(TObj *obj)
Function used to wrap raw pointer into a smart one.
Definition: alloc.h:467
bool canAllocate() const
Inquiry whether allocation is possible.
Definition: alloc.h:483
std::unique_ptr< TInterface, details::InPlaceDeleter< TInterface > > Ptr
Smart pointer (std::unique_ptr) to the allocated object.
Definition: alloc.h:404
Ptr alloc(TArgs &&... args)
Allocation function.
Definition: alloc.h:422
const void * allocAddr() const
Get address of the objects being allocated using this allocator.
Definition: alloc.h:456
Contains extra logic to help with dispatching message types and objects.
Main namespace for all classes / functions of COMMS library.
Check whether TType type is included in the tuple TTuple.
Definition: Tuple.h:94
Calculated "aligned union" storage type for all the types in provided tuple.
Definition: Tuple.h:157
Replacement to some types from standard type_traits.