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