1 #pragma once
2
3 #include "Util2.h"
4 #include <string>
5 #include <type_traits>
6
7 namespace pymol
8 {
9 struct Void {
10 };
11
12 /**
13 * Expressive error handling alternative
14 */
15 class Error
16 {
17 public:
18 enum Code {
19 DEFAULT,
20 QUIET,
21 MEMORY,
22 };
23
Error()24 Error() {}
25
26 /**
27 * Construct from string
28 */
Error(std::string s)29 explicit Error(std::string s)
30 : m_errMsg(std::move(s))
31 {
32 }
33
34 /**
35 * Construct from error code.
36 */
Error(Code code)37 explicit Error(Code code)
38 : m_code(code)
39 {
40 }
41
42 /**
43 * Retrieves error message
44 * @return error message
45 */
46
what()47 const std::string& what() const noexcept { return m_errMsg; }
48
49 /**
50 * Error code
51 */
code()52 Code code() const noexcept { return m_code; }
53
54 /**
55 * Make an error instance with error code.
56 */
57 template <Code C, typename... PrintableTs>
make(PrintableTs &&...ts)58 static Error make(PrintableTs&&... ts)
59 {
60 auto error = Error(join_to_string(std::forward<PrintableTs>(ts)...));
61 error.m_code = C;
62 return error;
63 }
64
65 private:
66 std::string m_errMsg;
67 Code m_code = DEFAULT;
68 };
69
70 /**
71 * Creates Error message
72 * @param ts collection of printable values to be joined
73 * into an error message
74 */
75 template<typename... PrintableTs>
make_error(PrintableTs &&...ts)76 Error make_error(PrintableTs&&... ts)
77 {
78 return Error(join_to_string(std::forward<PrintableTs>(ts)...));
79 }
80
81 /**
82 * Class that expresses the expected result of a function
83 */
84
85 template <typename ResultT=Void> class Result
86 {
87
88 public:
89 using type = ResultT;
90
91 Result() = default;
92
93 /**
94 * Constructor alternative that allows for convertible types
95 * param r result returned from function with a type convertible to ResultT
96 */
97
Result(type r)98 Result(type r) : m_result(std::move(r)) {}
99
100 /**
101 * Constructor alternative that takes in pymol::Error. Value of expected type
102 * should not be taken at this point.
103 * @param e error object to express why value should not be used
104 */
105
Result(Error e)106 Result(Error e) : m_error{std::move(e)}, m_valid{false} {}
107
108 /**
109 * Construct from error code.
110 */
Result(Error::Code code)111 Result(Error::Code code)
112 : m_error(code)
113 , m_valid{false}
114 {
115 }
116
117 /**
118 * Determines whether the value of the expected type can be used.
119 */
120
121 explicit operator bool() const noexcept { return m_valid; }
122
123 /**
124 * Retrieves the underlying error object
125 */
126
error()127 const Error& error() const noexcept { return m_error; }
128
129 /**
130 * Rvalue reference to the underlying error object
131 */
error_move()132 Error&& error_move() noexcept
133 {
134 assert(!m_valid);
135 return std::move(m_error);
136 }
137
138 /**
139 * Retrieves the value of the expected object
140 */
141
result()142 ResultT& result() { return m_result; }
143
144 /**
145 * Retrieves the value of the expected object
146 */
147
result()148 const ResultT& result() const { return m_result; }
149
150 /**
151 * Pointer to the expected object. Never NULL. Call is invalid if this
152 * instance is in error state.
153 */
154 ResultT* operator->()
155 {
156 assert(m_valid);
157 return &m_result;
158 }
159
160 private:
161 ResultT m_result;
162 Error m_error;
163 bool m_valid{true};
164 };
165
166 } // namespace pymol
167
168 /**
169 * If `res` is in error state, return from the calling scope with `res.error()`.
170 * @param res Expression of type pymol::Result
171 * @note Inspired by `g_return_val_if_fail` from glib, except that the check
172 * will always be performed, there is nothing like `G_DISABLE_CHECKS`.
173 */
174 #define p_return_if_error(res) \
175 { \
176 auto&& _res_evaluated_ = res; \
177 if (!_res_evaluated_) \
178 return _res_evaluated_.error_move(); \
179 }
180
181 /**
182 * Like p_return_if_error but add a prefix to the error message.
183 */
184 #define p_return_if_error_prefixed(res, prefix) \
185 { \
186 auto&& _res_evaluated_ = res; \
187 if (!_res_evaluated_) \
188 return pymol::make_error(prefix, _res_evaluated_.error().what()); \
189 }
190
191 /**
192 * If `expr` evaluates to false, return from the calling scope with `val`.
193 * @note Inspired by `g_return_val_if_fail` from glib, except that the check
194 * will always be performed, there is nothing like `G_DISABLE_CHECKS`.
195 */
196 #define p_return_val_if_fail(expr, val) \
197 { \
198 if (!(expr)) \
199 return val; \
200 }
201