IMP logo
IMP Reference Guide  develop.98ef8da184,2024/04/23
The Integrative Modeling Platform
check_macros.h
Go to the documentation of this file.
1 /**
2  * \file IMP/check_macros.h
3  * \brief Helper macros for throwing and handling exceptions
4  *
5  * Copyright 2007-2022 IMP Inventors. All rights reserved.
6  *
7  */
8 
9 #ifndef IMPKERNEL_CHECK_MACROS_H
10 #define IMPKERNEL_CHECK_MACROS_H
11 
12 #include <IMP/kernel_config.h>
13 #include "exception.h"
14 #include "compiler_macros.h"
15 #include <iostream>
16 #include <cmath>
17 
18 #if !defined(IMP_HAS_CHECKS)
19 #error "IMP_HAS_CHECKS is not defined, compilation is broken"
20 #endif
21 
22 #if !defined(IMP_NONE)
23 #error "IMP_NONE is not defined, compilation is broken"
24 #endif
25 
26 /** Catch any IMP exception thrown by expr and terminate with an
27  error message. Use this for basic error handling in main functions
28  in C++. Do not use within the \imp library.
29 */
30 #define IMP_CATCH_AND_TERMINATE(expr) \
31  try { \
32  expr; \
33  } \
34  catch (const IMP::Exception &e) { \
35  std::cerr << "Application terminated with error :" << e.what() \
36  << std::endl; \
37  exit(1); \
38  }
39 
40 //! Throw an exception with a message
41 /** The exception thrown must inherit from Exception and not be
42  UsageException or InternalException as those are reserved for
43  disableable checks (the IMP_INTERNAL_CHECK() and IMP_USAGE_CHECK()
44  macros).
45  \code
46  IMP_THROW("Could not open file " << file_name,
47  IOException);
48  \endcode
49  */
50 #define IMP_THROW(message, exception_name) \
51  do { \
52  std::ostringstream imp_throw_oss; \
53  imp_throw_oss << message << std::endl; \
54  throw exception_name(imp_throw_oss.str().c_str()); \
55  } while (true)
56 
57 //! Throw an exception if a check fails
58 /** Do IMP_THROW() if the check as the first argument fails. Unlike
59  IMP_USAGE_CHECK() and IMP_INTERNAL_CHECK() these checks are
60  always present.*/
61 #define IMP_ALWAYS_CHECK(condition, message, exception_name) \
62  if (!(condition)) { \
63  IMP_THROW(message, exception_name); \
64  }
65 
66 //! A runtime failure for IMP.
67 /** \param[in] message Failure message to write.
68  This macro is used to provide nice error messages when there is
69  an internal error in \imp. It causes an IMP::InternalException to be
70  thrown.
71 */
72 #define IMP_FAILURE(message) \
73  do { \
74  std::ostringstream imp_failure_oss; \
75  imp_failure_oss << message << std::endl; \
76  IMP::handle_error(imp_failure_oss.str().c_str()); \
77  throw IMP::InternalException(imp_failure_oss.str().c_str()); \
78  } while (true)
79 
80 //! Use this to mark that the method is not implemented yet
81 #define IMP_NOT_IMPLEMENTED \
82  do { \
83  IMP::handle_error("This method is not implemented."); \
84  throw IMP::InternalException("Not implemented"); \
85  } while (true)
86 
87 #ifdef IMP_DOXYGEN
88 
89 //! Execute the code block if a certain level checks are on
90 /**
91  The next code block (delimited by { }) is executed if
92  get_check_level() <= level.
93 
94  For example:
95  \code
96  IMP_IF_CHECK(USAGE) {
97  Vector<Particle*> testp(input.begin(), input.end());
98  std::sort(testp.begin(), testp.end());
99  IMP_USAGE_CHECK(std::unique(testp.begin(), testp.end()) == testp.end(),
100  "Duplicate particles found in the input list.");
101  }
102  \endcode
103 */
104 #define IMP_IF_CHECK(level)
105 
106 //! Only compile the code if checks are enabled
107 /** For example
108  \code
109  IMP_CHECK_CODE({
110  Vector<Particle*> testp(input.begin(), input.end());
111  std::sort(testp.begin(), testp.end());
112  IMP_USAGE_CHECK(std::unique(testp.begin(), testp.end()) == testp.end(),
113  "Duplicate particles found in the input list.");
114  });
115  \endcode
116 **/
117 #define IMP_CHECK_CODE(expr)
118 
119 /** \brief An assertion to check for internal errors in \imp. An
120  IMP::ErrorException will be thrown.
121 
122  Since it is a debug-only check and no attempt should be made to
123  recover from it, the exception type cannot be specified.
124 
125  For example:
126  \code
127  IMP_INTERNAL_CHECK((3.14-PI) < .01,
128  "PI is not close to 3.14. It is instead " << PI);
129  \endcode
130 
131  \note if the code is compiled with 'fast', or the check level is
132  less than IMP::USAGE_AND_INTERNAL, the check is not performed. Do
133  not use asserts as a shorthand to throw exceptions (throw the
134  exception yourself); use them only to check for logic errors.
135 
136  \param[in] expr The assertion expression.
137  \param[in] message Write this message if the assertion fails.
138 */
139 #define IMP_INTERNAL_CHECK(expr, message)
140 
141 /** This is like IMP_INTERNAL_CHECK, however designed to check if
142  two floating point numbers are almost equal. The check looks something
143  like
144  \code
145  std::abs(a-b) < .1*(a+b)+.1
146  \endcode
147  Using this makes such tests a bit easier to spot and not mess up.
148 */
149 #define IMP_INTERNAL_CHECK_FLOAT_EQUAL(expra, exprb, message)
150 
151 //! A runtime test for incorrect usage of a class or method.
152 /** \param[in] expr The assertion expression.
153  \param[in] message Write this message if the assertion fails.
154 
155  It should be used to check arguments to function. For example
156  \code
157  IMP_USAGE_CHECK(positive_argument >0,
158  "Argument positive_argument to function my_function "
159  << " must be positive. Instead got " << positive_argument);
160  \endcode
161 
162  \note if the build is 'fast', or the check level
163  is less than IMP::USAGE, the check is not performed. Do not use these
164  checks as a shorthand to throw necessary exceptions (throw the
165  exception yourself); use them only to check for errors, such as
166  inappropriate input.
167  */
168 #define IMP_USAGE_CHECK(expr, message)
169 
170 /** This is like IMP_USAGE_CHECK, however designed to check if
171  two floating point numbers are almost equal. The check looks something
172  like
173  \code
174  std::abs(a-b) < .1*(a+b)+.1
175  \endcode
176  Using this makes such tests a bit easier to spot and not mess up.
177 */
178 #define IMP_USAGE_CHECK_FLOAT_EQUAL(expra, exprb, message)
179 
180 #ifndef IMP_DOXYGEN
181 /** Mark a variable as one that is only used in checks. This disables
182  unused variable warnings on it in fast mode.
183 */
184 #define IMP_CHECK_VARIABLE(variable)
185 #endif
186 
187 /** Mark a variable as one that is only used in checks. This disables
188  unused variable warnings on it in fast mode.
189 */
190 #define IMP_USAGE_CHECK_VARIABLE(variable)
191 
192 /** Mark a variable as one that is only used in checks. This disables
193  unused variable warnings on it in fast mode.
194 */
195 #define IMP_INTERNAL_CHECK_VARIABLE(variable)
196 
197 #else // IMP_DOXYGEN
198 
199 #if IMP_HAS_CHECKS == IMP_INTERNAL
200 #define IMP_CHECK_VARIABLE(variable)
201 #define IMP_USAGE_CHECK_VARIABLE(variable)
202 #define IMP_INTERNAL_CHECK_VARIABLE(variable)
203 #elif IMP_HAS_CHECKS == IMP_USAGE
204 #define IMP_CHECK_VARIABLE(variable)
205 #define IMP_USAGE_CHECK_VARIABLE(variable)
206 #define IMP_INTERNAL_CHECK_VARIABLE(variable) IMP_UNUSED(variable)
207 #else
208 #define IMP_CHECK_VARIABLE(variable) IMP_UNUSED(variable)
209 #define IMP_USAGE_CHECK_VARIABLE(variable) IMP_UNUSED(variable)
210 #define IMP_INTERNAL_CHECK_VARIABLE(variable) IMP_UNUSED(variable)
211 #endif
212 
213 #if IMP_HAS_CHECKS > IMP_NONE
214 #define IMP_IF_CHECK(level) \
215  if (level <= ::IMP::get_check_level())
216 
217 #define IMP_CHECK_CODE(expr) expr
218 
219 #if IMP_KERNEL_HAS_LOG4CXX
220 #define IMP_KERNEL_CONTEXT
221 #else
222 #define IMP_KERNEL_CONTEXT << IMP::get_context_message()
223 #endif
224 
225 #else // IMP_HAS_CHECKS == IMP_NONE
226 #define IMP_IF_CHECK(level) if (0)
227 #define IMP_CHECK_CODE(expr)
228 #endif // IMP_HAS_CHECKS
229 
230 #if IMP_HAS_CHECKS >= IMP_INTERNAL
231 #define IMP_INTERNAL_CHECK(expr, message) \
232  do { \
233  if (IMP::get_check_level() >= IMP::USAGE_AND_INTERNAL && \
234  !(expr)) { \
235  std::ostringstream imp_check_oss; \
236  imp_check_oss << "Internal check failure: " << message << std::endl \
237  << " File \"" << __FILE__ << "\", line " \
238  << __LINE__ IMP_KERNEL_CONTEXT << std::endl; \
239  IMP::handle_error(imp_check_oss.str().c_str()); \
240  throw IMP::InternalException(imp_check_oss.str().c_str()); \
241  } \
242  } while (false)
243 
244 #define IMP_INTERNAL_CHECK_FLOAT_EQUAL(expra, exprb, message) \
245  IMP_INTERNAL_CHECK( \
246  std::abs((expra) - (exprb)) < .1 * std::abs((expra) + (exprb)) + .1, \
247  (expra) << " != " << (exprb) << " - " << message)
248 #else
249 #define IMP_INTERNAL_CHECK(expr, message)
250 #define IMP_INTERNAL_CHECK_FLOAT_EQUAL(expra, exprb, message)
251 #endif
252 
253 #if IMP_HAS_CHECKS >= IMP_USAGE
254 #define IMP_USAGE_CHECK(expr, message) \
255  do { \
256  if (IMP::get_check_level() >= IMP::USAGE && !(expr)) { \
257  std::ostringstream imp_check_oss; \
258  imp_check_oss << "Usage check failure: " << message IMP_KERNEL_CONTEXT \
259  << std::endl; \
260  IMP::handle_error(imp_check_oss.str().c_str()); \
261  throw IMP::UsageException(imp_check_oss.str().c_str()); \
262  } \
263  } while (false)
264 #define IMP_USAGE_CHECK_FLOAT_EQUAL(expra, exprb, message) \
265  IMP_USAGE_CHECK( \
266  std::abs((expra) - (exprb)) < .1 * std::abs((expra) + (exprb)) + .1, \
267  expra << " != " << exprb << " - " << message)
268 #else
269 #define IMP_USAGE_CHECK(expr, message)
270 #define IMP_USAGE_CHECK_FLOAT_EQUAL(expra, exprb, message)
271 #endif
272 
273 #endif // IMP_DOXYGEN
274 
275 #if defined(IMP_DOXYGEN) || IMP_HAS_CHECKS == IMP_NONE
276 //! Perform some basic validity checks on the object for memory debugging
277 #define IMP_CHECK_OBJECT(obj) IMP_UNUSED(obj)
278 #define IMP_CHECK_OBJECT_IF_NOT_nullptr(obj) IMP_UNUSED(obj)
279 #else
280 
281 #define IMP_CHECK_OBJECT(obj) \
282  do { \
283  IMP_UNUSED(obj); \
284  IMP_INTERNAL_CHECK((obj!=nullptr), "nullptr object"); \
285  IMP_INTERNAL_CHECK((obj)->get_is_valid(), \
286  "Check object " << static_cast<const void *>(obj) \
287  << " was previously freed"); \
288  } while (false)
289 
290 #define IMP_CHECK_OBJECT_IF_NOT_nullptr(obj) \
291  do { \
292  if (obj) { \
293  IMP_INTERNAL_CHECK((obj)->get_is_valid(), \
294  "Check object " << static_cast<const void *>(obj) \
295  << " was previously freed"); \
296  } \
297  } while (false)
298 #endif
299 
300 #endif /* IMPKERNEL_CHECK_MACROS_H */
Exception definitions and assertions.
Various compiler workarounds.