| 1 | //! @file action.hpp |
| 2 | //! @author ryftchen |
| 3 | //! @brief The declarations (action) in the application module. |
| 4 | //! @version 0.1.0 |
| 5 | //! @copyright Copyright (c) 2022-2025 ryftchen. All rights reserved. |
| 6 | |
| 7 | #pragma once |
| 8 | |
| 9 | #ifndef _PRECOMPILED_HEADER |
| 10 | #include <coroutine> |
| 11 | #else |
| 12 | #include "application/pch/precompiled_header.hpp" |
| 13 | #endif // _PRECOMPILED_HEADER |
| 14 | |
| 15 | #include "application/example/include/register_algorithm.hpp" |
| 16 | #include "application/example/include/register_data_structure.hpp" |
| 17 | #include "application/example/include/register_design_pattern.hpp" |
| 18 | #include "application/example/include/register_numeric.hpp" |
| 19 | |
| 20 | //! @brief The application module. |
| 21 | namespace application // NOLINT(modernize-concat-nested-namespaces) |
| 22 | { |
| 23 | //! @brief Applied-action-related functions in the application module. |
| 24 | namespace action |
| 25 | { |
| 26 | //! @brief Awaitable coroutine. |
| 27 | class Awaitable final |
| 28 | { |
| 29 | public: |
| 30 | // NOLINTBEGIN(readability-identifier-naming) |
| 31 | //! @brief Promise type for use in coroutines. |
| 32 | struct promise_type |
| 33 | { |
| 34 | //! @brief Get the return object for the coroutine. |
| 35 | //! @return awaitable instance |
| 36 | Awaitable get_return_object() { return Awaitable{std::coroutine_handle<promise_type>::from_promise(p&: *this)}; } |
| 37 | //! @brief Initial suspend point of the coroutine. |
| 38 | //! @return std::suspend_never object indicating that the coroutine should not be suspended initially |
| 39 | static std::suspend_never initial_suspend() noexcept { return {}; } |
| 40 | //! @brief Final suspend point of the coroutine. |
| 41 | //! @return std::suspend_always object indicating that the coroutine should be suspended finally |
| 42 | static std::suspend_always final_suspend() noexcept { return {}; } |
| 43 | //! @brief Complete the coroutine without returning a value. |
| 44 | static void return_void() noexcept {} |
| 45 | //! @brief Handle exceptions thrown within the coroutine. |
| 46 | static void unhandled_exception() { std::rethrow_exception(std::current_exception()); } |
| 47 | }; |
| 48 | // NOLINTEND(readability-identifier-naming) |
| 49 | |
| 50 | //! @brief Construct a new Awaitable object. |
| 51 | //! @param handle - coroutine handle |
| 52 | explicit Awaitable(const std::coroutine_handle<promise_type>& handle); |
| 53 | //! @brief Destroy the Awaitable object. |
| 54 | ~Awaitable(); |
| 55 | //! @brief Construct a new Awaitable object. |
| 56 | Awaitable(const Awaitable&) = delete; |
| 57 | //! @brief Construct a new Awaitable object. |
| 58 | Awaitable(Awaitable&&) = delete; |
| 59 | //! @brief The operator (=) overloading of Awaitable class. |
| 60 | //! @return reference of the Awaitable object |
| 61 | Awaitable& operator=(const Awaitable&) = delete; |
| 62 | //! @brief The operator (=) overloading of Awaitable class. |
| 63 | //! @return reference of the Awaitable object |
| 64 | Awaitable& operator=(Awaitable&&) = delete; |
| 65 | |
| 66 | //! @brief Resume the execution of the coroutine if it is suspended. |
| 67 | void resume() const; |
| 68 | //! @brief Check whether the coroutine has been completed. |
| 69 | //! @return be done or not |
| 70 | [[nodiscard]] bool done() const; |
| 71 | |
| 72 | private: |
| 73 | //! @brief Coroutine handle. |
| 74 | std::coroutine_handle<promise_type> handle; |
| 75 | //! @brief Flag to ensure only one instance is active. |
| 76 | static std::atomic_bool active; |
| 77 | }; |
| 78 | |
| 79 | //! @brief Alias for the type information. |
| 80 | //! @tparam UDT - type of user defined data |
| 81 | template <typename UDT> |
| 82 | using TypeInfo = utility::reflection::TypeInfo<UDT>; |
| 83 | //! @brief Get the name field directly for sub-cli related registrations. |
| 84 | //! @tparam MappedCLI - type of sub-cli or sub-cli's category |
| 85 | //! @return name field |
| 86 | template <typename MappedCLI> |
| 87 | consteval std::string_view name() |
| 88 | { |
| 89 | return TypeInfo<MappedCLI>::name; |
| 90 | } |
| 91 | //! @brief Get the alias attribute directly for sub-cli related registrations. |
| 92 | //! @tparam Meth - type of sub-cli's category |
| 93 | //! @return alias attribute |
| 94 | template <typename Meth> |
| 95 | requires std::is_same_v<Meth, reg_algo::MatchMethod> || std::is_same_v<Meth, reg_algo::NotationMethod> |
| 96 | || std::is_same_v<Meth, reg_algo::OptimalMethod> || std::is_same_v<Meth, reg_algo::SearchMethod> |
| 97 | || std::is_same_v<Meth, reg_algo::SortMethod> |
| 98 | consteval std::string_view alias() |
| 99 | { |
| 100 | return TypeInfo<reg_algo::ApplyAlgorithm>::fields.find(REFLECTION_STR(TypeInfo<Meth>::name)) |
| 101 | .attrs.find(REFLECTION_STR("alias" )) |
| 102 | .value; |
| 103 | } |
| 104 | //! @brief Get the alias attribute directly for sub-cli related registrations. |
| 105 | //! @tparam Inst - type of sub-cli's category |
| 106 | //! @return alias attribute |
| 107 | template <typename Inst> |
| 108 | requires std::is_same_v<Inst, reg_dp::BehavioralInstance> || std::is_same_v<Inst, reg_dp::CreationalInstance> |
| 109 | || std::is_same_v<Inst, reg_dp::StructuralInstance> |
| 110 | consteval std::string_view alias() |
| 111 | { |
| 112 | return TypeInfo<reg_dp::ApplyDesignPattern>::fields.find(REFLECTION_STR(TypeInfo<Inst>::name)) |
| 113 | .attrs.find(REFLECTION_STR("alias" )) |
| 114 | .value; |
| 115 | } |
| 116 | //! @brief Get the alias attribute directly for sub-cli related registrations. |
| 117 | //! @tparam Inst - type of sub-cli's category |
| 118 | //! @return alias attribute |
| 119 | template <typename Inst> |
| 120 | requires std::is_same_v<Inst, reg_ds::CacheInstance> || std::is_same_v<Inst, reg_ds::FilterInstance> |
| 121 | || std::is_same_v<Inst, reg_ds::GraphInstance> || std::is_same_v<Inst, reg_ds::HeapInstance> |
| 122 | || std::is_same_v<Inst, reg_ds::LinearInstance> || std::is_same_v<Inst, reg_ds::TreeInstance> |
| 123 | consteval std::string_view alias() |
| 124 | { |
| 125 | return TypeInfo<reg_ds::ApplyDataStructure>::fields.find(REFLECTION_STR(TypeInfo<Inst>::name)) |
| 126 | .attrs.find(REFLECTION_STR("alias" )) |
| 127 | .value; |
| 128 | } |
| 129 | //! @brief Get the alias attribute directly for sub-cli related registrations. |
| 130 | //! @tparam Meth - type of sub-cli's category |
| 131 | //! @return alias attribute |
| 132 | template <typename Meth> |
| 133 | requires std::is_same_v<Meth, reg_num::ArithmeticMethod> || std::is_same_v<Meth, reg_num::DivisorMethod> |
| 134 | || std::is_same_v<Meth, reg_num::IntegralMethod> || std::is_same_v<Meth, reg_num::PrimeMethod> |
| 135 | consteval std::string_view alias() |
| 136 | { |
| 137 | return TypeInfo<reg_num::ApplyNumeric>::fields.find(REFLECTION_STR(TypeInfo<Meth>::name)) |
| 138 | .attrs.find(REFLECTION_STR("alias" )) |
| 139 | .value; |
| 140 | } |
| 141 | //! @brief Get the description attribute directly for sub-cli related registrations. |
| 142 | //! @tparam MappedCLI - type of sub-cli or sub-cli's category |
| 143 | //! @return description attribute |
| 144 | template <typename MappedCLI> |
| 145 | consteval std::string_view descr() |
| 146 | { |
| 147 | return TypeInfo<MappedCLI>::attrs.find(REFLECTION_STR("descr" )).value; |
| 148 | } |
| 149 | |
| 150 | //! @brief The "Set Choice" message in the applied action. |
| 151 | //! @tparam Evt - type of applied action event |
| 152 | template <typename Evt> |
| 153 | struct SetChoice |
| 154 | { |
| 155 | //! @brief Target choice. |
| 156 | const std::string choice; |
| 157 | }; |
| 158 | //! @brief The "Run Candidates" message in the applied action. |
| 159 | //! @tparam Evt - type of applied action event |
| 160 | template <typename Evt> |
| 161 | struct RunCandidates |
| 162 | { |
| 163 | //! @brief Collection of candidates for choice. |
| 164 | const std::vector<std::string> candidates; |
| 165 | }; |
| 166 | //! @brief Indication type of setting in the applied action. |
| 167 | //! @tparam Msg - type of message |
| 168 | template <typename Msg> |
| 169 | struct SettingIndication; |
| 170 | //! @brief Indication type of running in the applied action. |
| 171 | //! @tparam Msg - type of message |
| 172 | template <typename Msg> |
| 173 | struct RunningIndication; |
| 174 | //! @brief Message type list. |
| 175 | //! @tparam Types - type of indication types |
| 176 | template <typename... Types> |
| 177 | struct MessageTypeList |
| 178 | { |
| 179 | //! @brief Alias for the parameter pack. |
| 180 | //! @tparam InnerTypes - type of inner types |
| 181 | //! @tparam TemplatedType - type of templated type |
| 182 | template <template <typename... InnerTypes> class TemplatedType> |
| 183 | using AsParameterPackFor = TemplatedType<Types...>; |
| 184 | //! @brief Alias for the providing interface. |
| 185 | //! @tparam Intf - type of interface |
| 186 | template <typename Intf> |
| 187 | using WithInterface = MessageTypeList<Types..., Intf>; |
| 188 | }; |
| 189 | //! @brief Alias for the message type list. |
| 190 | using MessageTypes = MessageTypeList< |
| 191 | SettingIndication<SetChoice<reg_algo::MatchMethod>>, |
| 192 | RunningIndication<RunCandidates<reg_algo::MatchMethod>>, |
| 193 | SettingIndication<SetChoice<reg_algo::NotationMethod>>, |
| 194 | RunningIndication<RunCandidates<reg_algo::NotationMethod>>, |
| 195 | SettingIndication<SetChoice<reg_algo::OptimalMethod>>, |
| 196 | RunningIndication<RunCandidates<reg_algo::OptimalMethod>>, |
| 197 | SettingIndication<SetChoice<reg_algo::SearchMethod>>, |
| 198 | RunningIndication<RunCandidates<reg_algo::SearchMethod>>, |
| 199 | SettingIndication<SetChoice<reg_algo::SortMethod>>, |
| 200 | RunningIndication<RunCandidates<reg_algo::SortMethod>>, |
| 201 | SettingIndication<SetChoice<reg_dp::BehavioralInstance>>, |
| 202 | RunningIndication<RunCandidates<reg_dp::BehavioralInstance>>, |
| 203 | SettingIndication<SetChoice<reg_dp::CreationalInstance>>, |
| 204 | RunningIndication<RunCandidates<reg_dp::CreationalInstance>>, |
| 205 | SettingIndication<SetChoice<reg_dp::StructuralInstance>>, |
| 206 | RunningIndication<RunCandidates<reg_dp::StructuralInstance>>, |
| 207 | SettingIndication<SetChoice<reg_ds::CacheInstance>>, |
| 208 | RunningIndication<RunCandidates<reg_ds::CacheInstance>>, |
| 209 | SettingIndication<SetChoice<reg_ds::FilterInstance>>, |
| 210 | RunningIndication<RunCandidates<reg_ds::FilterInstance>>, |
| 211 | SettingIndication<SetChoice<reg_ds::GraphInstance>>, |
| 212 | RunningIndication<RunCandidates<reg_ds::GraphInstance>>, |
| 213 | SettingIndication<SetChoice<reg_ds::HeapInstance>>, |
| 214 | RunningIndication<RunCandidates<reg_ds::HeapInstance>>, |
| 215 | SettingIndication<SetChoice<reg_ds::LinearInstance>>, |
| 216 | RunningIndication<RunCandidates<reg_ds::LinearInstance>>, |
| 217 | SettingIndication<SetChoice<reg_ds::TreeInstance>>, |
| 218 | RunningIndication<RunCandidates<reg_ds::TreeInstance>>, |
| 219 | SettingIndication<SetChoice<reg_num::ArithmeticMethod>>, |
| 220 | RunningIndication<RunCandidates<reg_num::ArithmeticMethod>>, |
| 221 | SettingIndication<SetChoice<reg_num::DivisorMethod>>, |
| 222 | RunningIndication<RunCandidates<reg_num::DivisorMethod>>, |
| 223 | SettingIndication<SetChoice<reg_num::IntegralMethod>>, |
| 224 | RunningIndication<RunCandidates<reg_num::IntegralMethod>>, |
| 225 | SettingIndication<SetChoice<reg_num::PrimeMethod>>, |
| 226 | RunningIndication<RunCandidates<reg_num::PrimeMethod>>>; |
| 227 | |
| 228 | //! @brief Alias for the message handler. |
| 229 | //! @tparam Msg - type of message |
| 230 | template <typename Msg> |
| 231 | using Handler = std::function<void(const Msg&)>; |
| 232 | //! @brief Message dispatcher. |
| 233 | //! @tparam Is - type of indications |
| 234 | template <typename... Is> |
| 235 | class Dispatcher; |
| 236 | //! @brief Message dispatcher of the setting indication. |
| 237 | //! @tparam Msg - type of message |
| 238 | //! @tparam Is - type of indications |
| 239 | template <typename Msg, typename... Is> |
| 240 | class Dispatcher<SettingIndication<Msg>, Is...> : public Dispatcher<Is...> |
| 241 | { |
| 242 | public: |
| 243 | using Dispatcher<Is...>::registerHandler; |
| 244 | //! @brief Register the handler. |
| 245 | //! @param handling - handling for message |
| 246 | virtual void registerHandler(Handler<Msg> handling) = 0; |
| 247 | }; |
| 248 | //! @brief Message dispatcher of the running indication. |
| 249 | //! @tparam Msg - type of messages |
| 250 | //! @tparam Is - type of indications |
| 251 | template <typename Msg, typename... Is> |
| 252 | class Dispatcher<RunningIndication<Msg>, Is...> : public Dispatcher<Is...> |
| 253 | { |
| 254 | public: |
| 255 | using Dispatcher<Is...>::registerHandler; |
| 256 | //! @brief Register the handler. |
| 257 | //! @param handling - handling for message |
| 258 | virtual void registerHandler(Handler<Msg> handling) = 0; |
| 259 | }; |
| 260 | //! @brief Default message dispatcher. |
| 261 | template <> |
| 262 | class Dispatcher<> |
| 263 | { |
| 264 | public: |
| 265 | //! @brief Construct a new Dispatcher object. |
| 266 | Dispatcher() = default; |
| 267 | //! @brief Destroy the Dispatcher object. |
| 268 | virtual ~Dispatcher() = default; |
| 269 | //! @brief Construct a new Dispatcher object. |
| 270 | Dispatcher(const Dispatcher&) = default; |
| 271 | //! @brief Construct a new Dispatcher object. |
| 272 | Dispatcher(Dispatcher&&) noexcept = default; |
| 273 | //! @brief The operator (=) overloading of Dispatcher class. |
| 274 | //! @return reference of the Dispatcher object |
| 275 | Dispatcher& operator=(const Dispatcher&) = default; |
| 276 | //! @brief The operator (=) overloading of Dispatcher class. |
| 277 | //! @return reference of the Dispatcher object |
| 278 | Dispatcher& operator=(Dispatcher&&) noexcept = default; |
| 279 | |
| 280 | //! @brief Register the handler. |
| 281 | void registerHandler(); |
| 282 | }; |
| 283 | //! @brief Message receiver. |
| 284 | //! @tparam Is - type of indications |
| 285 | template <typename... Is> |
| 286 | class Receiver; |
| 287 | //! @brief Message receiver of the setting indication. |
| 288 | //! @tparam Msg - type of message |
| 289 | //! @tparam Is - type of indications |
| 290 | template <typename Msg, typename... Is> |
| 291 | class Receiver<SettingIndication<Msg>, Is...> : public Receiver<Is...> |
| 292 | { |
| 293 | public: |
| 294 | using Receiver<Is...>::onMessage; |
| 295 | //! @brief Action on message. |
| 296 | //! @param message - message body |
| 297 | virtual void onMessage(const Msg& message) = 0; |
| 298 | }; |
| 299 | //! @brief Message receiver of the running indication. |
| 300 | //! @tparam Msg - type of message |
| 301 | //! @tparam Is - type of indications |
| 302 | template <typename Msg, typename... Is> |
| 303 | class Receiver<RunningIndication<Msg>, Is...> : public Receiver<Is...> |
| 304 | { |
| 305 | public: |
| 306 | using Receiver<Is...>::onMessage; |
| 307 | //! @brief Action on message. |
| 308 | //! @param message - message body |
| 309 | virtual void onMessage(const Msg& message) = 0; |
| 310 | }; |
| 311 | //! @brief Default message receiver. |
| 312 | template <> |
| 313 | class Receiver<> |
| 314 | { |
| 315 | public: |
| 316 | //! @brief Construct a new Receiver object. |
| 317 | Receiver() = default; |
| 318 | //! @brief Destroy the Receiver object. |
| 319 | virtual ~Receiver() = default; |
| 320 | //! @brief Construct a new Receiver object. |
| 321 | Receiver(const Receiver&) = default; |
| 322 | //! @brief Construct a new Receiver object. |
| 323 | Receiver(Receiver&&) noexcept = default; |
| 324 | //! @brief The operator (=) overloading of Receiver class. |
| 325 | //! @return reference of the Receiver object |
| 326 | Receiver& operator=(const Receiver&) = default; |
| 327 | //! @brief The operator (=) overloading of Receiver class. |
| 328 | //! @return reference of the Receiver object |
| 329 | Receiver& operator=(Receiver&&) noexcept = default; |
| 330 | |
| 331 | //! @brief Action on message. |
| 332 | void onMessage(); |
| 333 | }; |
| 334 | //! @brief Forwarding basis for all message types. |
| 335 | struct ForwardBase : public MessageTypes::AsParameterPackFor<Dispatcher>, |
| 336 | public MessageTypes::AsParameterPackFor<Receiver> |
| 337 | { |
| 338 | }; |
| 339 | //! @brief Forwarding action. |
| 340 | //! @tparam Is - type of indications |
| 341 | template <typename... Is> |
| 342 | class Forward; |
| 343 | //! @brief Forward message of the setting indication. |
| 344 | //! @tparam Msg - type of message |
| 345 | //! @tparam Is - type of indications |
| 346 | template <typename Msg, typename... Is> |
| 347 | class Forward<SettingIndication<Msg>, Is...> : public Forward<Is...> |
| 348 | { |
| 349 | public: |
| 350 | //! @brief Alias for the base class. |
| 351 | using Base = Forward<Is...>; |
| 352 | using Base::registerHandler, Base::onMessage; |
| 353 | //! @brief Register the handler. |
| 354 | //! @param handling - handling for message |
| 355 | void registerHandler(Handler<Msg> handling) override; |
| 356 | //! @brief Action on message. |
| 357 | //! @param message - message body |
| 358 | void onMessage(const Msg& message) override; |
| 359 | |
| 360 | private: |
| 361 | //! @brief Message handler. |
| 362 | Handler<Msg> handler{}; |
| 363 | }; |
| 364 | template <typename Msg, typename... Is> |
| 365 | void Forward<SettingIndication<Msg>, Is...>::registerHandler(Handler<Msg> handling) |
| 366 | { |
| 367 | handler = std::move(handling); |
| 368 | } |
| 369 | template <typename Msg, typename... Is> |
| 370 | void Forward<SettingIndication<Msg>, Is...>::onMessage(const Msg& message) |
| 371 | { |
| 372 | if (handler) |
| 373 | { |
| 374 | handler(message); |
| 375 | } |
| 376 | } |
| 377 | //! @brief Forward message of the running indication. |
| 378 | //! @tparam Msg - type of message |
| 379 | //! @tparam Is - type of indications |
| 380 | template <typename Msg, typename... Is> |
| 381 | class Forward<RunningIndication<Msg>, Is...> : public Forward<Is...> |
| 382 | { |
| 383 | public: |
| 384 | //! @brief Alias for the base class. |
| 385 | using Base = Forward<Is...>; |
| 386 | using Base::registerHandler, Base::onMessage; |
| 387 | //! @brief Register the handler. |
| 388 | //! @param handling - handling for message |
| 389 | void registerHandler(Handler<Msg> handling) override; |
| 390 | //! @brief Action on message. |
| 391 | //! @param message - message body |
| 392 | void onMessage(const Msg& message) override; |
| 393 | |
| 394 | private: |
| 395 | //! @brief Message handler. |
| 396 | Handler<Msg> handler{}; |
| 397 | }; |
| 398 | template <typename Msg, typename... Is> |
| 399 | void Forward<RunningIndication<Msg>, Is...>::registerHandler(Handler<Msg> handling) |
| 400 | { |
| 401 | handler = std::move(handling); |
| 402 | } |
| 403 | template <typename Msg, typename... Is> |
| 404 | void Forward<RunningIndication<Msg>, Is...>::onMessage(const Msg& message) |
| 405 | { |
| 406 | if (handler) |
| 407 | { |
| 408 | handler(message); |
| 409 | } |
| 410 | } |
| 411 | //! @brief Forwarding action interface. |
| 412 | //! @tparam Intf - type of interface |
| 413 | template <typename Intf> |
| 414 | class Forward<Intf> : public Intf |
| 415 | { |
| 416 | }; |
| 417 | |
| 418 | //! @brief Message forwarder. |
| 419 | struct MessageForwarder : public MessageTypes::WithInterface<ForwardBase>::AsParameterPackFor<Forward> |
| 420 | { |
| 421 | }; |
| 422 | |
| 423 | //! @brief Alias for the applied action event type. |
| 424 | using EventType = std::variant< |
| 425 | reg_algo::MatchMethod, |
| 426 | reg_algo::NotationMethod, |
| 427 | reg_algo::OptimalMethod, |
| 428 | reg_algo::SearchMethod, |
| 429 | reg_algo::SortMethod, |
| 430 | reg_dp::BehavioralInstance, |
| 431 | reg_dp::CreationalInstance, |
| 432 | reg_dp::StructuralInstance, |
| 433 | reg_ds::CacheInstance, |
| 434 | reg_ds::FilterInstance, |
| 435 | reg_ds::GraphInstance, |
| 436 | reg_ds::HeapInstance, |
| 437 | reg_ds::LinearInstance, |
| 438 | reg_ds::TreeInstance, |
| 439 | reg_num::ArithmeticMethod, |
| 440 | reg_num::DivisorMethod, |
| 441 | reg_num::IntegralMethod, |
| 442 | reg_num::PrimeMethod>; |
| 443 | } // namespace action |
| 444 | } // namespace application |
| 445 | |