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