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