when.hpp
Go to the documentation of this file.
1
3#pragma once
4
5#include <utility>
6#include <cstdlib>
7#include "indep/def/_nout.hpp"
12#include "indep/helper/tmay.hpp"
13
14namespace by {
15
16 // `WHEN` macro:
17 // in `byeol`, we actively apply the early-return pattern.
18 // this helps to reduce the depth, make the code clean, and handle exceptional situations
19 // immediately.
20 // in the case of general C++ code that applies the early pattern, you will use `if` as
21 // follows.
22 // ```cpp
23 // SomeAdapter* adapter = bridge.requestAdapter("token");
24 // if (!adapter) { // 1) ealry return
25 // printLogs("null adapter!");
26 // return;
27 // }
28 //
29 // if (adapter->getState() == State.ACTIVATE) { // 2)
30 // adapter->onActivate();
31 // } else {
32 // const vector<Device>& devices = adapter->getDevices();
33 // if (devices.size() <= 0) { // 3) ealry return
34 // printLogs("there is no device in " + adapter->getName());
35 // return;
36 // }
37 //
38 // for (const auto& device : devices) {
39 // DeviceResult res = device.attach();
40 // if (res == FAILURE) {
41 // printLogs("device can't attach to system"); // 4) ealry early-return
42 // return;
43 // }
44 // if (res == Pending) {
45 // device.retryQueue();
46 // }
47 // }
48 // }
49 //
50 //
51 // ```
52 //
53 // the problem is that, as can be seen in the code above, if is used so commonly that it is
54 // difficult to immediately know whether a normal branch is made for logic or an ealry return
55 // is made to prune in advance with exception handling. that's why in byeol, `WHEN` is used
56 // for almost all ealry return exception handling.
57 //
58 // the output is very intuitive.
59 //
60 // ```cpp
61 // SomeAdapter* adapter = bridge.requestAdapter("token");
62 // WHEN_NUL(adapter).err("null adapter!");
63 //
64 // if (adapter->getState() == State.ACTIVATE) {
65 // adapter->onActivate();
66 // } else {
67 // const vector<Device>& devices = adapter->getDevices();
68 // WHEN(devices.size() <= 0).err("there is no device in %s", adapter->getName());
69 //
70 // for (const auto& device : devices) {
71 // DeviceResult res = device.attach();
72 // WHEN(res == FAILURE).err("device can't attach to system");
73 //
74 // if (res == Pending) {
75 // device.retryQueue();
76 // }
77 // }
78 // }
79 // ```
80 //
81 // as in the example, it is clear which part handles exceptions and sends them out, and which
82 // part branches for logic. also, since `WHEN` is class-based, handling when the exception
83 // handling condition is satisfied can be handled based on the class.
84
85 // __WHEN_OBJECT__?:
86 // since byeol is structured as a multi-layered architecture, if the layer is low-level, it
87 // simply outputs the log to the screen, but in high-level layers, it requires more complex
88 // processing, such as creating an exception as an object and including stacktrace information
89 // to record it.
90 //
91 // in this way, since different classes should be displayed when the `WHEN` macro is expanded
92 // depending on the layer, this is solved by redefining `__WHEN_OBJECT__`.
93
94#define __WHEN_OBJECT__ __indep_when__
95
96 class _nout __WHEN_OBJECT__ {
97 BY(ME(__WHEN_OBJECT__))
98
99 public:
100 static const me& get();
101
102 template <typename R> R& ret([[maybe_unused]] R& r) const { return r; }
103
104 template <typename R> R* ret([[maybe_unused]] R* r) const { return r; }
105
106 template <typename R> R&& ret([[maybe_unused]] R&& r) const { return std::move(r); }
107
108 void ret() const;
109
110 template <typename T> tmay<T> retMay() const { return tmay<T>(); }
111
112 template <typename T, typename... Ts> tmay<T> retMay(Ts... args) const { return tmay<T>(args...); }
113
114 void crash() const;
115
116 template <typename R> R& crash([[maybe_unused]] R& r) const {
117 abort();
118 return r;
119 }
120
121 template <typename R> R&& crash([[maybe_unused]] R&& r) const {
122 abort();
123 return std::move(r);
124 }
125 };
126
127#define BY_WHEN ::by::__WHEN_OBJECT__::get()
128
129#define __WHEN_POSTFIX__ return BY_WHEN
130#define WHEN(condition) \
131 if(condition) __WHEN_POSTFIX__
132#define WHEN_NUL_1(v1) \
133 if(nul(v1)) __WHEN_POSTFIX__
134#define WHEN_NUL_2(v1, v2) \
135 if(nul(v1) || nul(v2)) __WHEN_POSTFIX__
136#define WHEN_NUL_3(v1, v2, v3) \
137 if(nul(v1) || nul(v2) || nul(v3)) __WHEN_POSTFIX__
138#define WHEN_NUL_4(v1, v2, v3, v4) \
139 if(nul(v1) || nul(v2) || nul(v3) || nul(v4)) __WHEN_POSTFIX__
140#define WHEN_NUL_5(v1, v2, v3, v4, v5) \
141 if(nul(v1) || nul(v2) || nul(v3) || nul(v4) || nul(v5)) __WHEN_POSTFIX__
142#define WHEN_NUL_6(v1, v2, v3, v4, v5, v6) \
143 if(nul(v1) || nul(v2) || nul(v3) || nul(v4) || nul(v5) || nul(v6)) __WHEN_POSTFIX__
144#define WHEN_NUL(...) BY_OVERLOAD(WHEN_NUL, __VA_ARGS__)
145
146} // namespace by