SuperDex Physics C++ API
Loading...
Searching...
No Matches
error.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
21
22#include <string>
23
24namespace superdex {
25
26/**************************************************************************************************
27 Error Reporting
28
29 The Error class is used to return success or failure when calling a function. It is usually the
30 last function parameter and is passed by reference. Call IsOK() to check for success. Call
31 GetDescription() to find out what went wrong. Example:
32
33 Error error;
34 DoStuff(arg1, arg2, error);
35 if (error.IsOK()) {
36 // celebrate
37 }
38
39 By convention, functions that take an Error parameter always start with a MOCHI_ERROR_RETURN macro
40 so they return quickly and safely if an error has already been set. Thus, the caller does not need
41 to check the error after every call. They can simply pass the same error object to each call and
42 check for errors at the end, or let the error bubble up. Example:
43
44 void DoStuff(int a, int b, Error& error) {
45 MOCHI_ERROR_RETURN(error); // do this first
46
47 DoPartA(a, error);
48 DoPartB(b, error); // does nothing if DoPartA failed
49 Thing* thing = CreateThing(a, b, error); // returns nullptr if DoPartA or DoPartB failed
50 UseThing(thing, error); // does nothing if any of the above failed
51 DestroyThing(thing, error); // does nothing if any of the above failed
52 }
53
54 When you need to report a new error, use MOCHI_ERROR_SET. It captures the file & line number along
55 with your description (must be a string literal). Example:
56
57 MOCHI_ERROR_SET(error, "Here's why it failed");
58
59 There are times when it does not make sense to handle errors because they don't matter, or because
60 you believe they will never happen. There are inline helpers you can use in such cases. Examples:
61
62 DoStuff(ErrorAssert{}); // MOCHI_ASSERT if it fails
63 DoStuff(ErrorLog{}); // MOCHI_LOG if it fails
64*/
65class Error final {
66 public:
67 Error() = default;
68 ~Error() = default;
69 Error(Error&&) = default;
70 Error& operator=(Error&&) = default;
71
72 // Return true if NO error was set, else return false.
73 bool IsOK() const {
74 return _description == nullptr;
75 }
76
77 // Get the error message text (if any)
78 char const* GetDescription() const {
79 return IsOK() ? "" : _description;
80 }
81
82 // Get the name of the source file that set the error (if any)
83 char const* GetFile() const {
84 return IsOK() ? "" : _file;
85 }
86
87 // Get the line of source code that set the error (if any)
88 int GetLine() const {
89 return IsOK() ? 0 : _line;
90 }
91
92 // Format the error for logging.
93 std::string ToString() const {
94 // This format for file & line numer lets Visual Studio users double-click to
95 // go to the source code.
96 return IsOK() ? "OK" : Format("%s(%d): %s", _file, _line, _description);
97 }
98
99 // Errors are normally passed by reference, so we made the class non-copyable to
100 // prevent mistakes. However, it is possible to make an explicit copy if you ever
101 // need to store the Error for later use.
102 Error Copy() const {
103 return Error{*this};
104 }
105
106 // SetFirstError is usually called by the MOCHI_ERROR_SET macro.
107 // It stores the first error and ignores subsequent errors.
108 MOCHI_NO_INLINE void SetFirstError(char const* description, char const* file, int line) {
109 if (IsOK()) {
110 _description = description;
111 _file = file;
112 _line = line;
113 }
114 }
115
116 private:
117 // No implicit copy
118 Error(Error const&) = default; // Used by Error::Copy()
119 Error& operator=(Error const&) = default;
120
121 char const* _description = nullptr; // nullptr means "OK"
122 char const* _file; // NOLINT(cppcoreguidelines-pro-type-member-init) - Lazy init
123 int _line; // NOLINT(cppcoreguidelines-pro-type-member-init) - Lazy init
124};
125
126/**************************************************************************************************
127 Error Macros
128*/
129
130// Use MOCHI_ERROR_RETURN at the top of every function that takes an Error* argument.
131// Returns immediately if an error has already been set. Note: The static_cast supports wrappers
132// like ErrorAssert being used directly.
133#define MOCHI_ERROR_RETURN(error, ...) \
134 if (!static_cast<superdex::Error const&>(error).IsOK()) \
135 MOCHI_UNLIKELY { \
136 return __VA_ARGS__; \
137 }
138
139// Use MOCHI_ERROR_SET to indicate that something went wrong (see notes on the Error class above).
140// Ignored if an error was already set. Example: MOCHI_ERROR_SET(error, "Here's why it failed");
141// Note: The static_cast supports wrappers like ErrorAssert being used directly.
142#define MOCHI_ERROR_SET(error, descriptionStringLiteral) \
143 static_cast<superdex::Error&>(error).SetFirstError("" descriptionStringLiteral, __FILE__, __LINE__);
144
145// Sets an error and returns if the condition is true
146#define MOCHI_ERROR_IF(condition, error, descriptionStringLiteral) \
147 if (condition) \
148 MOCHI_UNLIKELY { \
149 MOCHI_ERROR_SET(error, descriptionStringLiteral); \
150 } \
151 else { \
152 }
153
154// Sets and error and returns if the condition is false
155#define MOCHI_ERROR_IF_NOT(condition, error, descriptionStringLiteral) \
156 MOCHI_ERROR_IF(!(condition), error, descriptionStringLiteral)
157
158// Sets an error to indicate that a particular function still needs to be implemented.
159// MSVC supports compile-time concatenation of __FUNCTION__ with a string literal, but clang/gcc
160// does not.
161#if MOCHI_COMPILER_MSVC
162#define MOCHI_ERROR_NOT_IMPLEMENTED(error) \
163 static_cast<superdex::Error&>(error).SetFirstError( \
164 __FUNCTION__ " not implemented", __FILE__, __LINE__)
165#else
166#define MOCHI_ERROR_NOT_IMPLEMENTED(error) \
167 static_cast<superdex::Error&>(error).SetFirstError("Not implemented", __FILE__, __LINE__)
168#endif
169
170/**************************************************************************************************
171 ErrorAssert:
172 If you are certain that a call will never fail, then you can pass ErrorAssert{} in place of
173 the Error& argument. If you were wrong, your mistake will be reported via a MOCHI_ASSERT. By
174 default, asserts are fatal unless a debugger is connected (then break point). You can
175 customize that behavior (see Debug.h).
176
177 Example:
178 DoStuff(arg1, arg2, ErrorAssert{});
179
180 // Equivalent to:
181 Error error;
182 DoStuff(arg1, arg2, error);
183 MOCHI_ASSERT(error.IsOK(), "[MOCHI ERROR]...");
184
185*/
186class ErrorAssert final {
188
189 public:
190 ErrorAssert() = default;
192#if MOCHI_ASSERT_ENABLED
193 if (!_error.IsOK())
195 MOCHI_ASSERT_ON_FAILURE(
196 _error.GetFile(), _error.GetLine(), "error.IsOK()", "%s", _error.GetDescription());
197 }
198#endif // MOCHI_ASSERT_ENABLED
199 }
200
201 operator Error&() {
202 return _error;
203 }
205};
206
207/**************************************************************************************************
208 ErrorLog:
209 If you do not plan to write custom error handling code, then consider passing ErrorLog{} in
210 place of the Error& argument. It will automatically report any error using the MOCHI_LOG
211 mechanism.
212
213 Example:
214 DoStuff(arg1, arg2, ErrorLog{});
215*/
216class ErrorLog final {
218
219 public:
222 if (!_error.IsOK())
225 _channel,
226 _error.GetFile(),
227 _error.GetLine(),
228 "[MOCHI ERROR] %s",
229 _error.GetDescription());
230 }
231 }
232
233 operator Error&() {
234 return _error;
235 }
236 bool IsOK() const {
237 return _error.IsOK();
238 }
241};
242
243} // namespace superdex
LogChannel _channel
Definition error.h:239
ErrorLog(LogChannel channel=LogChannel::Error)
Definition error.h:220
bool IsOK() const
Definition error.h:236
int GetLine() const
Definition error.h:88
Error & operator=(Error &&)=default
char const * GetFile() const
Definition error.h:83
bool IsOK() const
Definition error.h:73
void SetFirstError(char const *description, char const *file, int line)
Definition error.h:108
~Error()=default
char const * GetDescription() const
Definition error.h:78
Error(Error &&)=default
Error()=default
std::string ToString() const
Definition error.h:93
Error Copy() const
Definition error.h:102
#define MOCHI_LOG_IMPL(channel, file, line,...)
Definition log.h:89
#define MOCHI_UNLIKELY
#define MOCHI_DECLARE_NO_COPY_NO_MOVE(Name)
LogChannel
Definition log.h:37
std::string Format()
Definition log.h:96