message.h 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #pragma once
  2. //
  3. // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com)
  4. //
  5. // Distributed under the MIT Software License
  6. //
  7. #include "arc.hpp"
  8. #include "address.hpp"
  9. #include <typeindex>
  10. #include <deque>
  11. #if defined(_MSC_VER)
  12. #pragma warning(push)
  13. #pragma warning(disable : 4251)
  14. #endif
  15. namespace rotor {
  16. struct message_base_t;
  17. /** \struct message_visitor_t
  18. * \brief Abstract message visitor interface
  19. *
  20. * As the message type is dynamic and might not be known at compile type,
  21. * the `try_visit` method returns `true` or `false` to indicate whether
  22. * a message has been successfully processed. If it returns `false`,
  23. * that might indicate, that some other message visitor should try a luck.
  24. *
  25. * It should be noted, that it is potentially slow (not very performant)
  26. * as it involves multiple visitors and multiple message try attemps
  27. * (the double dispatch visitor pattern is not available).
  28. *
  29. */
  30. struct ROTOR_API message_visitor_t {
  31. virtual ~message_visitor_t() = default;
  32. /** \brief returns `true` if a message has been successfully processed */
  33. virtual bool try_visit(const message_base_t &message) const = 0;
  34. };
  35. /** \struct message_base_t
  36. * \brief Base class for `rotor` message.
  37. *
  38. * The base class contains destination address (in the form of intrusive
  39. * pointer to `address_t`) and possibility to detect final message type.
  40. *
  41. * The actual message payload meant to be provided by derived classes
  42. *
  43. */
  44. struct message_base_t : public arc_base_t<message_base_t> {
  45. virtual ~message_base_t() = default;
  46. /**
  47. * \brief unique message type pointer.
  48. *
  49. * The unique message type pointer is used to runtime check message type
  50. * match when the message is delivered to subscribers.
  51. *
  52. */
  53. const void *type_index;
  54. /** \brief message destination address */
  55. address_ptr_t address;
  56. /** \brief constructor which takes destination address */
  57. inline message_base_t(const void *type_index_, const address_ptr_t &addr)
  58. : type_index(type_index_), address{addr} {}
  59. };
  60. namespace message_support {
  61. ROTOR_API const void *register_type(const std::type_index &type_index) noexcept;
  62. }
  63. /** \struct message_t
  64. * \brief the generic message meant to hold user-specific payload
  65. * \tparam T payload type
  66. */
  67. template <typename T> struct message_t : public message_base_t {
  68. /** \brief alias for payload type */
  69. using payload_t = T;
  70. /** \brief struct visitor_t concrete message type visitor
  71. *
  72. * The `visitor_t` class is intentionally not related to
  73. * `message_visitor_t`, as there is no `try_visit`,
  74. * because concrete message visitor always "successfully"
  75. * visits concrete message type.
  76. *
  77. */
  78. struct ROTOR_API visitor_t {
  79. virtual ~visitor_t() = default;
  80. /** \brief visit concrete message */
  81. virtual void on(const message_t &) {}
  82. };
  83. /** \brief forwards `args` for payload construction */
  84. template <typename... Args>
  85. message_t(const address_ptr_t &addr, Args &&...args)
  86. : message_base_t{message_type, addr}, payload{std::forward<Args>(args)...} {}
  87. /** \brief user-defined payload */
  88. T payload;
  89. /** \brief unique per-message-type pointer used for routing */
  90. static const void *message_type;
  91. };
  92. template <typename T> const void *message_t<T>::message_type = message_support::register_type(typeid(message_t<T>));
  93. /** \brief intrusive pointer for message */
  94. using message_ptr_t = intrusive_ptr_t<message_base_t>;
  95. /** \brief structure to hold messages (intrusive pointers) */
  96. using messages_queue_t = std::deque<message_ptr_t>;
  97. /** \brief constructs message by constructing it's payload; intrusive pointer for the message is returned */
  98. template <typename M, typename... Args> auto make_message(const address_ptr_t &addr, Args &&...args) -> message_ptr_t {
  99. return message_ptr_t{new message_t<M>(addr, std::forward<Args>(args)...)};
  100. }
  101. } // namespace rotor
  102. #if defined(_MSC_VER)
  103. #pragma warning(pop)
  104. #endif