COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
MsgFactoryBase.h
1//
2// Copyright 2017 - 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
8#pragma once
9
10#include <type_traits>
11#include <memory>
12
13#include "comms/Assert.h"
14#include "comms/util/Tuple.h"
15#include "comms/util/alloc.h"
17#include "comms/MessageBase.h"
19#include "comms/traits.h"
20#include "comms/dispatch.h"
22#include "comms/details/tag.h"
23
24namespace comms
25{
26
27namespace details
28{
29
30template <typename TAllMessages>
31constexpr bool msgFactoryAllHaveStaticNumId()
32{
33 return allMessagesHaveStaticNumId<TAllMessages>();
34}
35
36template <typename TMessage>
37constexpr bool msgFactoryMessageHasStaticNumId()
38{
39 return messageHasStaticNumId<TMessage>();
40}
41
42template <typename TMsgBase, typename TAllMessages, typename... TOptions>
43class MsgFactoryBase
44{
45 static_assert(TMsgBase::hasMsgIdType(),
46 "Usage of MsgFactoryBase requires Message interface to provide ID type. "
47 "Use comms::option::def::MsgIdType option in message interface type definition.");
48 using ParsedOptionsInternal = details::MsgFactoryOptionsParser<TOptions...>;
49
50 static const bool InterfaceHasVirtualDestructor =
51 std::has_virtual_destructor<TMsgBase>::value;
52
53 using AllMessagesInternal =
54 typename ParsedOptionsInternal::template AllMessages<TAllMessages>;
55 using GenericMessageInternal = typename ParsedOptionsInternal::GenericMessage;
56
57 template <typename...>
58 struct InPlaceAllocDeepCondWrap
59 {
60 template <
61 typename TInterface,
62 typename TAllocMessages,
63 typename TOrigMessages,
64 typename TId,
65 typename TDefaultType,
66 typename...>
67 using Type =
68 typename comms::util::LazyDeepConditional<
69 InterfaceHasVirtualDestructor
70 >::template Type<
71 comms::util::alloc::details::InPlaceSingleDeepCondWrap,
72 comms::util::alloc::details::InPlaceSingleNoVirtualDestructorDeepCondWrap,
73 TInterface,
74 TAllocMessages,
75 TOrigMessages,
76 TId,
77 TDefaultType
78 >;
79 };
80
81 template <typename...>
82 struct DynMemoryAllocDeepCondWrap
83 {
84 template <
85 typename TInterface,
86 typename TAllocMessages,
87 typename TOrigMessages,
88 typename TId,
89 typename TDefaultType,
90 typename...>
91 using Type =
92 typename comms::util::LazyDeepConditional<
93 InterfaceHasVirtualDestructor
94 >::template Type<
95 comms::util::alloc::details::DynMemoryDeepCondWrap,
96 comms::util::alloc::details::DynMemoryNoVirtualDestructorDeepCondWrap,
97 TInterface,
98 TOrigMessages,
99 TId,
100 TDefaultType
101 >;
102 };
103
104 using Alloc =
105 typename comms::util::LazyDeepConditional<
106 ParsedOptionsInternal::HasInPlaceAllocation
107 >::template Type<
108 InPlaceAllocDeepCondWrap,
109 DynMemoryAllocDeepCondWrap,
110 TMsgBase,
111 AllMessagesInternal,
112 TAllMessages,
113 typename TMsgBase::MsgIdType,
114 GenericMessageInternal
115 >;
116public:
117 using ParsedOptions = ParsedOptionsInternal;
118 using Message = TMsgBase;
119 using MsgIdParamType = typename Message::MsgIdParamType;
120 using MsgIdType = typename Message::MsgIdType;
121 using MsgPtr = typename Alloc::Ptr;
122 using AllMessages = TAllMessages;
123
124 using CreateFailureReason = MsgFactoryCreateFailureReason;
125
126 MsgPtr createMsg(MsgIdParamType id, unsigned idx, CreateFailureReason* reason) const
127 {
128 CreateFailureReason reasonTmp = CreateFailureReason::None;
129 bool result = false;
130 MsgPtr msg = createMsgInternal(id, idx, result, DestructorTag<>());
131 do {
132 if (msg) {
133 COMMS_ASSERT(result);
134 break;
135 }
136
137 if (!result) {
138 reasonTmp = CreateFailureReason::InvalidId;
139 break;
140 }
141
142 reasonTmp = CreateFailureReason::AllocFailure;
143 } while (false);
144
145 if (reason != nullptr) {
146 *reason = reasonTmp;
147 }
148
149 return msg;
150 }
151
152 MsgPtr createGenericMsg(MsgIdParamType id, unsigned idx) const
153 {
154 static_cast<void>(this);
155 using Tag =
156 typename comms::util::LazyShallowConditional<
157 ParsedOptions::HasSupportGenericMessage
158 >::template Type<
159 AllocGenericTag,
160 NoAllocTag
161 >;
162
163 return createGenericMsgInternal(id, idx, Tag(), DestructorTag<>());
164 }
165
166 bool canAllocate() const
167 {
168 return alloc_.canAllocate();
169 }
170
171 std::size_t msgCount(MsgIdParamType id) const
172 {
173 return comms::dispatchMsgTypeCountStaticBinSearch<AllMessages>(id);
174 }
175
176 static constexpr bool hasUniqueIds()
177 {
178 return comms::details::allMessagesAreStrongSorted<AllMessages>();
179 }
180
181 static constexpr bool isDispatchPolymorphic()
182 {
183 return isDispatchPolymorphicInternal(DispatchTag<>());
184 }
185
186 static constexpr bool isDispatchStaticBinSearch()
187 {
188 return isDispatchStaticBinSearchInternal(DispatchTag<>());
189 }
190
191 static constexpr bool isDispatchLinearSwitch()
192 {
193 return isDispatchLinearSwitchInternal(DispatchTag<>());
194 }
195
196protected:
197 MsgFactoryBase() = default;
198 MsgFactoryBase(const MsgFactoryBase&) = default;
199 MsgFactoryBase(MsgFactoryBase&&) = default;
200 MsgFactoryBase& operator=(const MsgFactoryBase&) = default;
201 MsgFactoryBase& operator=(MsgFactoryBase&&) = default;
202
203 template <typename TObj, typename... TArgs>
204 MsgPtr allocMsg(TArgs&&... args) const
205 {
206 static_assert(std::is_base_of<Message, TObj>::value,
207 "TObj is not a proper message type");
208
209 static_assert(std::has_virtual_destructor<TObj>::value,
210 "This function is expected to be called for message objects with virtual destructor");
211 static_assert(
212 (!ParsedOptionsInternal::HasInPlaceAllocation) ||
214 "TObj must be in provided tuple of supported messages");
215
216 return alloc_.template alloc<TObj>(std::forward<TArgs>(args)...);
217 }
218
219 template <typename TObj, typename... TArgs>
220 MsgPtr allocMsg(MsgIdParamType id, unsigned idx, TArgs&&... args) const
221 {
222 static_assert(std::is_base_of<Message, TObj>::value,
223 "TObj is not a proper message type");
224
225 static_assert(!std::has_virtual_destructor<TObj>::value,
226 "This function is expected to be called for message objects without virtual destructor");
227
228 static_assert(
229 (!ParsedOptionsInternal::HasInPlaceAllocation) ||
231 "TObj must be in provided tuple of supported messages");
232
233 return alloc_.template alloc<TObj>(id, idx, std::forward<TArgs>(args)...);
234 }
235
236private:
237 template <typename... TParams>
238 using AllocGenericTag = comms::details::tag::Tag1<>;
239
240 template <typename... TParams>
241 using NoAllocTag = comms::details::tag::Tag2<>;
242
243 template <typename... TParams>
244 using ForcedTag = comms::details::tag::Tag3<>;
245
246 template <typename... TParams>
247 using StandardTag = comms::details::tag::Tag4<>;
248
249 template <typename... TParams>
250 using VirtualDestructorTag = comms::details::tag::Tag5<>;
251
252 template <typename... TParams>
253 using NonVirtualDestructorTag = comms::details::tag::Tag6<>;
254
255 template <typename...>
256 using DispatchTag =
257 typename comms::util::LazyShallowConditional<
258 ParsedOptions::HasForcedDispatch
259 >::template Type<
260 ForcedTag,
261 StandardTag
262 >;
263
264 template <typename...>
265 using DestructorTag =
266 typename comms::util::LazyShallowConditional<
267 InterfaceHasVirtualDestructor
268 >::template Type<
269 VirtualDestructorTag,
270 NonVirtualDestructorTag
271 >;
272
273 class CreateHandler
274 {
275 public:
276 explicit CreateHandler(Alloc& a) : a_(a) {}
277
278 MsgPtr getMsg()
279 {
280 return std::move(msg_);
281 }
282
283 template <typename T>
284 void handle()
285 {
286 msg_ = a_.template alloc<T>();
287 }
288
289 private:
290 Alloc& a_;
291 MsgPtr msg_;
292 };
293
294 class NonVirtualDestructorCreateHandler
295 {
296 public:
297 explicit NonVirtualDestructorCreateHandler(MsgIdParamType id, unsigned idx, Alloc& a) :
298 id_(id),
299 idx_(idx),
300 a_(a)
301 {
302 }
303
304 MsgPtr getMsg()
305 {
306 return std::move(msg_);
307 }
308
309 template <typename T>
310 void handle()
311 {
312 msg_ = a_.template alloc<T>(id_, idx_);
313 }
314
315 private:
316 MsgIdType id_;
317 unsigned idx_ = 0U;
318 Alloc& a_;
319 MsgPtr msg_;
320 };
321
322 template <typename... TParams>
323 MsgPtr createGenericMsgInternal(MsgIdParamType id, unsigned idx, AllocGenericTag<TParams...>, VirtualDestructorTag<TParams...>) const
324 {
325 static_cast<void>(idx);
326 static_assert(std::is_base_of<Message, typename ParsedOptions::GenericMessage>::value,
327 "The requested GenericMessage class must have the same interface class as all other messages");
328 return allocMsg<typename ParsedOptions::GenericMessage>(id);
329 }
330
331 template <typename... TParams>
332 MsgPtr createGenericMsgInternal(MsgIdParamType id, unsigned idx, AllocGenericTag<TParams...>, NonVirtualDestructorTag<TParams...>) const
333 {
334 static_assert(std::is_base_of<Message, typename ParsedOptions::GenericMessage>::value,
335 "The requested GenericMessage class must have the same interface class as all other messages");
336 return allocMsg<typename ParsedOptions::GenericMessage>(id, idx, id);
337 }
338
339 template <typename TDestructorTag, typename... TParams>
340 static MsgPtr createGenericMsgInternal(MsgIdParamType, NoAllocTag<TParams...>, TDestructorTag)
341 {
342 return MsgPtr();
343 }
344
345 template <typename THandler, typename... TParams>
346 static bool dispatchMsgTypeInternal(MsgIdParamType id, unsigned idx, THandler& handler, StandardTag<TParams...>)
347 {
348 return comms::dispatchMsgType<AllMessages>(id, idx, handler);
349 }
350
351 template <typename THandler, typename... TParams>
352 static bool dispatchMsgTypeInternal(MsgIdParamType id, unsigned idx, THandler& handler, ForcedTag<TParams...>)
353 {
354 using Tag = typename ParsedOptions::ForcedDispatch;
355 return dispatchMsgTypeInternal(id, idx, handler, Tag());
356 }
357
358 template <typename THandler>
359 static bool dispatchMsgTypeInternal(MsgIdParamType id, unsigned idx, THandler& handler, comms::traits::dispatch::Polymorphic)
360 {
361 return comms::dispatchMsgTypePolymorphic<AllMessages>(id, idx, handler);
362 }
363
364 template <typename THandler>
365 static bool dispatchMsgTypeInternal(MsgIdParamType id, unsigned idx, THandler& handler, comms::traits::dispatch::StaticBinSearch)
366 {
367 return comms::dispatchMsgTypeStaticBinSearch<AllMessages>(id, idx, handler);
368 }
369
370 template <typename THandler>
371 static bool dispatchMsgTypeInternal(MsgIdParamType id, unsigned idx, THandler& handler, comms::traits::dispatch::LinearSwitch)
372 {
373 return comms::dispatchMsgTypeStaticBinSearch<AllMessages>(id, idx, handler);
374 }
375
376 template <typename... TParams>
377 static constexpr bool isDispatchPolymorphicInternal(ForcedTag<TParams...>)
378 {
379 return std::is_same<comms::traits::dispatch::Polymorphic, typename ParsedOptions::ForcedDispatch>::value;
380 }
381
382 template <typename... TParams>
383 static constexpr bool isDispatchPolymorphicInternal(StandardTag<TParams...>)
384 {
385 return dispatchMsgTypeIsPolymorphic<AllMessages>();
386 }
387
388 template <typename... TParams>
389 static constexpr bool isDispatchStaticBinSearchInternal(ForcedTag<TParams...>)
390 {
391 return std::is_same<comms::traits::dispatch::StaticBinSearch, typename ParsedOptions::ForcedDispatch>::value;
392 }
393
394 template <typename... TParams>
395 static constexpr bool isDispatchStaticBinSearchInternal(StandardTag<TParams...>)
396 {
397 return dispatchMsgTypeIsStaticBinSearch<AllMessages>();
398 }
399
400 template <typename... TParams>
401 static constexpr bool isDispatchLinearSwitchInternal(ForcedTag<TParams...>)
402 {
403 return std::is_same<comms::traits::dispatch::LinearSwitch, typename ParsedOptions::ForcedDispatch>::value;
404 }
405
406 template <typename... TParams>
407 static constexpr bool isDispatchLinearSwitchInternal(StandardTag<TParams...>)
408 {
409 return false;
410 }
411
412 template <typename... TParams>
413 MsgPtr createMsgInternal(MsgIdParamType id, unsigned idx, bool& success, VirtualDestructorTag<TParams...>) const
414 {
415 CreateHandler handler(alloc_);
416 success = dispatchMsgTypeInternal(id, idx, handler, DispatchTag<>());
417 return handler.getMsg();
418 }
419
420 template <typename... TParams>
421 MsgPtr createMsgInternal(MsgIdParamType id, unsigned idx, bool& success, NonVirtualDestructorTag<TParams...>) const
422 {
423 NonVirtualDestructorCreateHandler handler(id, idx, alloc_);
424 success = dispatchMsgTypeInternal(id, idx, handler, DispatchTag<>());
425 return handler.getMsg();
426 }
427
428 mutable Alloc alloc_;
429};
430
431
432} // namespace details
433
434} // namespace comms
435
436
This file contains classes required for generic custom assertion functionality.
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:170
Provides common base class for the custom messages with default implementation.
Contains various tuple type manipulation classes and functions.
This file contains various generic allocator classes that may be used to allocate objects using dynam...
typename BaseImpl::MsgIdType MsgIdType
Type used for message ID.
Definition Message.h:196
typename BaseImpl::MsgIdParamType MsgIdParamType
Type used for message ID passed as parameter or returned from function.
Definition Message.h:203
Contains extra logic to help with dispatching message types and objects.
comms::option::def::MsgIdType< T > MsgIdType
Same as comms::option::def::MsgIdType.
Definition options.h:1448
Main namespace for all classes / functions of COMMS library.
MsgFactoryCreateFailureReason
Definition MsgFactoryCreateFailureReason.h:18
Tag class used to indicate linear switch dispatch.
Definition traits.h:207
Tag class used to indicate polymorphic dispatch.
Definition traits.h:201
Tag class used to indicate static binary search dispatch.
Definition traits.h:204
Check whether TType type is included in the tuple TTuple.
Definition Tuple.h:95
This file contains all the classes necessary to properly define message traits.
Replacement to some types from standard type_traits.