1
0
mirror of https://github.com/Zygo/bees.git synced 2025-05-17 21:35:45 +02:00
Zygo Blaxell 2e36dd2d58 error: introduce THROW_CHECK4, the long-awaited sequel to THROW_CHECK3
Sometimes we need to check constraints on 4 variables at once.

It would be nice if variadic macros in C++ were also polymorphic.

Signed-off-by: Zygo Blaxell <bees@furryterror.org>
2021-10-31 19:42:01 -04:00

163 lines
5.3 KiB
C++

#ifndef CRUCIBLE_ERROR_H
#define CRUCIBLE_ERROR_H
// Common error-handling idioms for C library calls
#include <cerrno>
#include <cstring>
#include <functional>
#include <sstream>
#include <stdexcept>
#include <system_error>
#include <unistd.h>
namespace crucible {
using namespace std;
// Common error-handling idioms for C library calls
template <class T> T die_if_minus_errno(const char *expr, T rv)
{
if (rv < 0) {
throw system_error(error_code(-rv, system_category()), expr);
}
return rv;
}
template <class T> T die_if_minus_one(const char *expr, T rv)
{
if (rv == -1) {
throw system_error(error_code(errno, system_category()), expr);
}
return rv;
}
template <class T> T die_if_zero(const char *expr, T rv)
{
if (rv == 0) {
throw system_error(error_code(errno, system_category()), expr);
}
return rv;
}
template <class T> T die_if_non_zero(const char *expr, T rv)
{
if (rv != 0) {
throw system_error(error_code(errno, system_category()), expr);
}
return rv;
}
// Usage: catch_all([&]() { /* insert body here */ } );
// Executes body with exceptions caught and reported to cerr.
// Returns:
// 0 if f() returns
// non-zero if f() throws an exception
// -1 for unknown exception
// 1 for std::exception or class derived thereof
void set_catch_explainer(function<void(string s)> f);
void default_catch_explainer(string s);
int catch_all(const function<void()> &f, const function<void(string)> &explainer = default_catch_explainer);
// catch_and_explain traps the exception, calls the explainer, then rethrows the original exception
void catch_and_explain(const function<void()> &f, const function<void(string)> &explainer = default_catch_explainer);
};
// 0 on success, -errno on error.
// Covers most pthread functions.
#define DIE_IF_MINUS_ERRNO(expr) crucible::die_if_minus_errno(#expr, expr)
// -1 on error, all other values mean success.
#define DIE_IF_MINUS_ONE(expr) crucible::die_if_minus_one(#expr, expr)
// 0 (or NULL) on error, all other values mean success.
#define DIE_IF_ZERO(expr) crucible::die_if_zero(#expr, expr)
// 0 (or NULL) on success, all other values mean error.
#define DIE_IF_NON_ZERO(expr) crucible::die_if_non_zero(#expr, expr)
// macro for throwing an error
#define THROW_ERROR(type, expr) do { \
std::ostringstream _te_oss; \
_te_oss << expr << " at " << __FILE__ << ":" << __LINE__; \
throw type(_te_oss.str()); \
} while (0)
// macro for throwing a system_error with errno
#define THROW_ERRNO(expr) do { \
std::ostringstream _te_oss; \
_te_oss << expr << " at " << __FILE__ << ":" << __LINE__; \
throw std::system_error(std::error_code(errno, std::system_category()), _te_oss.str()); \
} while (0)
// macro for throwing a system_error with some other variable
#define THROW_ERRNO_VALUE(value, expr) do { \
std::ostringstream _te_oss; \
_te_oss << expr << " at " << __FILE__ << ":" << __LINE__; \
throw std::system_error(std::error_code((value), std::system_category()), _te_oss.str()); \
} while (0)
// macros for checking a constraint
#define THROW_CHECK0(type, expr) do { \
if (!(expr)) { \
THROW_ERROR(type, "failed constraint check (" << #expr << ")"); \
} \
} while(0)
#define THROW_CHECK1(type, value, expr) do { \
if (!(expr)) { \
THROW_ERROR(type, #value << " = " << (value) << " failed constraint check (" << #expr << ")"); \
} \
} while(0)
#define THROW_CHECK2(type, value1, value2, expr) do { \
if (!(expr)) { \
THROW_ERROR(type, #value1 << " = " << (value1) << ", " #value2 << " = " << (value2) \
<< " failed constraint check (" << #expr << ")"); \
} \
} while(0)
#define THROW_CHECK3(type, value1, value2, value3, expr) do { \
if (!(expr)) { \
THROW_ERROR(type, #value1 << " = " << (value1) << ", " #value2 << " = " << (value2) << ", " #value3 << " = " << (value3) \
<< " failed constraint check (" << #expr << ")"); \
} \
} while(0)
#define THROW_CHECK4(type, value1, value2, value3, value4, expr) do { \
if (!(expr)) { \
THROW_ERROR(type, #value1 << " = " << (value1) << ", " #value2 << " = " << (value2) << ", " #value3 << " = " << (value3) << ", " #value4 << " = " << (value4) \
<< " failed constraint check (" << #expr << ")"); \
} \
} while(0)
#define THROW_CHECK_BIN_OP(type, value1, op, value2) do { \
if (!((value1) op (value2))) { \
THROW_ERROR(type, "failed constraint check " << #value1 << " (" << (value1) << ") " << #op << " " << #value2 << " (" << (value2) << ")"); \
} \
} while(0)
#define THROW_CHECK_PREFIX_OP(type, op, value1) do { \
if (!(op (value1))) { \
THROW_ERROR(type, "failed constraint check " << #op << " " << #value1 << " (" << (value1) << ")"); \
} \
} while(0)
#define THROW_CHECK_RANGE(type, value_min, value_test, value_max) do { \
if ((value_test) < (value_min) || (value_max) < (value_test)) { \
THROW_ERROR(type, "failed constraint check " << #value_min << " (" << (value_min) << ") <= " #value_test << " (" << (value_test) \
<< ") <= " << #value_max << " (" << (value_max) << ")"); \
} \
} while(0)
#define THROW_CHECK_ARRAY_RANGE(type, value_min, value_test, value_max) do { \
if ((value_test) < (value_min) || !((value_test) < (value_max))) { \
THROW_ERROR(type, "failed constraint check " << #value_min << " (" << (value_min) << ") <= " #value_test << " (" << (value_test) \
<< ") < " << #value_max << " (" << (value_max) << ")"); \
} \
} while(0)
#endif // CRUCIBLE_ERROR_H