1//! @file apply_design_pattern.cpp
2//! @author ryftchen
3//! @brief The definitions (apply_design_pattern) in the application module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2026 ryftchen. All rights reserved.
6
7#include "apply_design_pattern.hpp"
8#include "register_design_pattern.hpp"
9
10#ifndef _PRECOMPILED_HEADER
11#include <iomanip>
12#include <ranges>
13#else
14#include "application/pch/precompiled_header.hpp"
15#endif
16
17#include "application/core/include/log.hpp"
18#include "utility/include/currying.hpp"
19
20//! @brief Title of printing when design pattern tasks are beginning.
21#define APP_DP_PRINT_TASK_TITLE_SCOPE_BEGIN(title) \
22 std::osyncstream(std::cout) << "\nAPPLY DESIGN PATTERN: " << std::setiosflags(std::ios_base::left) \
23 << std::setfill('.') << std::setw(50) << (title) << "BEGIN" \
24 << std::resetiosflags(std::ios_base::left) << std::setfill(' ') << std::endl; \
25 {
26//! @brief Title of printing when design pattern tasks are ending.
27#define APP_DP_PRINT_TASK_TITLE_SCOPE_END(title) \
28 } \
29 std::osyncstream(std::cout) << "\nAPPLY DESIGN PATTERN: " << std::setiosflags(std::ios_base::left) \
30 << std::setfill('.') << std::setw(50) << (title) << "END" \
31 << std::resetiosflags(std::ios_base::left) << std::setfill(' ') << '\n' \
32 << std::endl;
33
34namespace application::app_dp
35{
36using namespace reg_dp; // NOLINT(google-build-using-namespace)
37
38//! @brief Make the title of a particular instance in design pattern choices.
39//! @tparam Inst - type of target instance
40//! @param instance - target instance
41//! @return initial capitalized title
42template <typename Inst>
43static std::string customTitle(const Inst instance)
44{
45 std::string title(TypeInfo<Inst>::fields.nameOfValue(instance));
46 title.at(n: 0) = static_cast<char>(std::toupper(c: title.at(n: 0)));
47 return title;
48}
49
50//! @brief Get the curried task name.
51//! @return curried task name
52static const auto& curriedTaskName()
53{
54 static const auto curried =
55 utility::currying::curry(func: configure::task::presetName, args: TypeInfo<ApplyDesignPattern>::name);
56 return curried;
57}
58
59//! @brief Get the alias of the category in design pattern choices.
60//! @tparam Cat - target category
61//! @return alias of the category name
62template <Category Cat>
63static consteval std::string_view categoryAlias()
64{
65 constexpr auto attr =
66 TypeInfo<ApplyDesignPattern>::fields.find(REFLECTION_STR(toString(Cat))).attrs.find(REFLECTION_STR("alias"));
67 static_assert(attr.hasValue);
68 return attr.value;
69}
70
71namespace behavioral
72{
73//! @brief Show the contents of the behavioral result.
74//! @param instance - used behavioral instance
75//! @param result - behavioral result
76static void display(const BehavioralInstance instance, const std::string& result)
77{
78 std::printf(format: "\n==> %-21s Instance <==\n%s", customTitle(instance).c_str(), result.c_str());
79}
80
81//! @brief Pattern of behavioral.
82//! @param instance - used behavioral instance
83static void pattern(const BehavioralInstance instance)
84try
85{
86 std::ostringstream result{};
87 switch (instance)
88 {
89 static_assert(utility::common::isStatelessClass<Showcase>());
90 case BehavioralInstance::chainOfResponsibility:
91 result = Showcase::chainOfResponsibility();
92 break;
93 case BehavioralInstance::command:
94 result = Showcase::command();
95 break;
96 case BehavioralInstance::interpreter:
97 result = Showcase::interpreter();
98 break;
99 case BehavioralInstance::iterator:
100 result = Showcase::iterator();
101 break;
102 case BehavioralInstance::mediator:
103 result = Showcase::mediator();
104 break;
105 case BehavioralInstance::memento:
106 result = Showcase::memento();
107 break;
108 case BehavioralInstance::observer:
109 result = Showcase::observer();
110 break;
111 case BehavioralInstance::state:
112 result = Showcase::state();
113 break;
114 case BehavioralInstance::strategy:
115 result = Showcase::strategy();
116 break;
117 case BehavioralInstance::templateMethod:
118 result = Showcase::templateMethod();
119 break;
120 case BehavioralInstance::visitor:
121 result = Showcase::visitor();
122 break;
123 default:
124 return;
125 }
126 display(instance, result: result.str());
127}
128catch (const std::exception& err)
129{
130 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(instance).c_str(), err.what());
131}
132} // namespace behavioral
133//! @brief To apply behavioral-related instances that are mapped to choices.
134//! @param candidates - container for the candidate target choices
135void applyingBehavioral(const std::vector<std::string>& candidates)
136{
137 constexpr auto category = Category::behavioral;
138 const auto& spec = categoryOpts<category>();
139 if (MACRO_IMPLIES(spec.any(), spec.size() != candidates.size()))
140 {
141 return;
142 }
143
144 const std::string_view title = design_pattern::behavioral::description();
145 APP_DP_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
146
147 auto& pooling = configure::task::resourcePool();
148 auto* const allocatedJob = pooling.newEntry(args: spec.count());
149 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
150 const auto addTask = [allocatedJob, &taskNamer](const std::string_view subtask, const BehavioralInstance instance)
151 { allocatedJob->enqueue(name: taskNamer(subtask), func&: behavioral::pattern, args: instance); };
152 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
153
154 std::cout << "\nInstances of the " << toString(cat: category) << " pattern:" << std::endl;
155 for (const auto index :
156 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
157 {
158 const auto& choice = candidates.at(n: index);
159 switch (utility::common::bkdrHash(str: choice.c_str()))
160 {
161 case abbrLitHash(instance: BehavioralInstance::chainOfResponsibility):
162 addTask(choice, BehavioralInstance::chainOfResponsibility);
163 break;
164 case abbrLitHash(instance: BehavioralInstance::command):
165 addTask(choice, BehavioralInstance::command);
166 break;
167 case abbrLitHash(instance: BehavioralInstance::interpreter):
168 addTask(choice, BehavioralInstance::interpreter);
169 break;
170 case abbrLitHash(instance: BehavioralInstance::iterator):
171 addTask(choice, BehavioralInstance::iterator);
172 break;
173 case abbrLitHash(instance: BehavioralInstance::mediator):
174 addTask(choice, BehavioralInstance::mediator);
175 break;
176 case abbrLitHash(instance: BehavioralInstance::memento):
177 addTask(choice, BehavioralInstance::memento);
178 break;
179 case abbrLitHash(instance: BehavioralInstance::observer):
180 addTask(choice, BehavioralInstance::observer);
181 break;
182 case abbrLitHash(instance: BehavioralInstance::state):
183 addTask(choice, BehavioralInstance::state);
184 break;
185 case abbrLitHash(instance: BehavioralInstance::strategy):
186 addTask(choice, BehavioralInstance::strategy);
187 break;
188 case abbrLitHash(instance: BehavioralInstance::templateMethod):
189 addTask(choice, BehavioralInstance::templateMethod);
190 break;
191 case abbrLitHash(instance: BehavioralInstance::visitor):
192 addTask(choice, BehavioralInstance::visitor);
193 break;
194 default:
195 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
196 }
197 }
198
199 APP_DP_PRINT_TASK_TITLE_SCOPE_END(title);
200}
201
202namespace creational
203{
204//! @brief Show the contents of the creational result.
205//! @param instance - used creational instance
206//! @param result - creational result
207static void display(const CreationalInstance instance, const std::string& result)
208{
209 std::printf(format: "\n==> %-15s Instance <==\n%s", customTitle(instance).c_str(), result.c_str());
210}
211
212//! @brief Pattern of creational.
213//! @param instance - used creational instance
214static void pattern(const CreationalInstance instance)
215try
216{
217 std::ostringstream result{};
218 switch (instance)
219 {
220 static_assert(utility::common::isStatelessClass<Showcase>());
221 case CreationalInstance::abstractFactory:
222 result = Showcase::abstractFactory();
223 break;
224 case CreationalInstance::builder:
225 result = Showcase::builder();
226 break;
227 case CreationalInstance::factoryMethod:
228 result = Showcase::factoryMethod();
229 break;
230 case CreationalInstance::prototype:
231 result = Showcase::prototype();
232 break;
233 case CreationalInstance::singleton:
234 result = Showcase::singleton();
235 break;
236 default:
237 return;
238 }
239 display(instance, result: result.str());
240}
241catch (const std::exception& err)
242{
243 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(instance).c_str(), err.what());
244}
245} // namespace creational
246//! @brief To apply creational-related instances that are mapped to choices.
247//! @param candidates - container for the candidate target choices
248void applyingCreational(const std::vector<std::string>& candidates)
249{
250 constexpr auto category = Category::creational;
251 const auto& spec = categoryOpts<category>();
252 if (MACRO_IMPLIES(spec.any(), spec.size() != candidates.size()))
253 {
254 return;
255 }
256
257 const std::string_view title = design_pattern::creational::description();
258 APP_DP_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
259
260 auto& pooling = configure::task::resourcePool();
261 auto* const allocatedJob = pooling.newEntry(args: spec.count());
262 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
263 const auto addTask = [allocatedJob, &taskNamer](const std::string_view subtask, const CreationalInstance instance)
264 { allocatedJob->enqueue(name: taskNamer(subtask), func&: creational::pattern, args: instance); };
265 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
266
267 std::cout << "\nInstances of the " << toString(cat: category) << " pattern:" << std::endl;
268 for (const auto index :
269 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
270 {
271 const auto& choice = candidates.at(n: index);
272 switch (utility::common::bkdrHash(str: choice.c_str()))
273 {
274 case abbrLitHash(instance: CreationalInstance::abstractFactory):
275 addTask(choice, CreationalInstance::abstractFactory);
276 break;
277 case abbrLitHash(instance: CreationalInstance::builder):
278 addTask(choice, CreationalInstance::builder);
279 break;
280 case abbrLitHash(instance: CreationalInstance::factoryMethod):
281 addTask(choice, CreationalInstance::factoryMethod);
282 break;
283 case abbrLitHash(instance: CreationalInstance::prototype):
284 addTask(choice, CreationalInstance::prototype);
285 break;
286 case abbrLitHash(instance: CreationalInstance::singleton):
287 addTask(choice, CreationalInstance::singleton);
288 break;
289 default:
290 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
291 }
292 }
293
294 APP_DP_PRINT_TASK_TITLE_SCOPE_END(title);
295}
296
297namespace structural
298{
299//! @brief Show the contents of the structural result.
300//! @param instance - used structural instance
301//! @param result - structural result
302static void display(const StructuralInstance instance, const std::string& result)
303{
304 std::printf(format: "\n==> %-9s Instance <==\n%s", customTitle(instance).c_str(), result.c_str());
305}
306
307//! @brief Pattern of structural.
308//! @param instance - used structural instance
309static void pattern(const StructuralInstance instance)
310try
311{
312 std::ostringstream result{};
313 switch (instance)
314 {
315 static_assert(utility::common::isStatelessClass<Showcase>());
316 case StructuralInstance::adapter:
317 result = Showcase::adapter();
318 break;
319 case StructuralInstance::bridge:
320 result = Showcase::bridge();
321 break;
322 case StructuralInstance::composite:
323 result = Showcase::composite();
324 break;
325 case StructuralInstance::decorator:
326 result = Showcase::decorator();
327 break;
328 case StructuralInstance::facade:
329 result = Showcase::facade();
330 break;
331 case StructuralInstance::flyweight:
332 result = Showcase::flyweight();
333 break;
334 case StructuralInstance::proxy:
335 result = Showcase::proxy();
336 break;
337 default:
338 return;
339 }
340 display(instance, result: result.str());
341}
342catch (const std::exception& err)
343{
344 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(instance).c_str(), err.what());
345}
346} // namespace structural
347//! @brief To apply structural-related instances that are mapped to choices.
348//! @param candidates - container for the candidate target choices
349void applyingStructural(const std::vector<std::string>& candidates)
350{
351 constexpr auto category = Category::structural;
352 const auto& spec = categoryOpts<category>();
353 if (MACRO_IMPLIES(spec.any(), spec.size() != candidates.size()))
354 {
355 return;
356 }
357
358 const std::string_view title = design_pattern::structural::description();
359 APP_DP_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
360
361 auto& pooling = configure::task::resourcePool();
362 auto* const allocatedJob = pooling.newEntry(args: spec.count());
363 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
364 const auto addTask = [allocatedJob, &taskNamer](const std::string_view subtask, const StructuralInstance instance)
365 { allocatedJob->enqueue(name: taskNamer(subtask), func&: structural::pattern, args: instance); };
366 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
367
368 std::cout << "\nInstances of the " << toString(cat: category) << " pattern:" << std::endl;
369 for (const auto index :
370 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
371 {
372 const auto& choice = candidates.at(n: index);
373 switch (utility::common::bkdrHash(str: choice.c_str()))
374 {
375 case abbrLitHash(instance: StructuralInstance::adapter):
376 addTask(choice, StructuralInstance::adapter);
377 break;
378 case abbrLitHash(instance: StructuralInstance::bridge):
379 addTask(choice, StructuralInstance::bridge);
380 break;
381 case abbrLitHash(instance: StructuralInstance::composite):
382 addTask(choice, StructuralInstance::composite);
383 break;
384 case abbrLitHash(instance: StructuralInstance::decorator):
385 addTask(choice, StructuralInstance::decorator);
386 break;
387 case abbrLitHash(instance: StructuralInstance::facade):
388 addTask(choice, StructuralInstance::facade);
389 break;
390 case abbrLitHash(instance: StructuralInstance::flyweight):
391 addTask(choice, StructuralInstance::flyweight);
392 break;
393 case abbrLitHash(instance: StructuralInstance::proxy):
394 addTask(choice, StructuralInstance::proxy);
395 break;
396 default:
397 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
398 }
399 }
400
401 APP_DP_PRINT_TASK_TITLE_SCOPE_END(title);
402}
403} // namespace application::app_dp
404
405#undef APP_DP_PRINT_TASK_TITLE_SCOPE_BEGIN
406#undef APP_DP_PRINT_TASK_TITLE_SCOPE_END
407