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