Error.hpp 7.16 KB
Newer Older
1 2 3 4
#ifndef FABULOUS_ERROR_HPP
#define FABULOUS_ERROR_HPP

#include <iostream>
5 6
#include <sstream>
#include <string>
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
7 8 9
#include <exception>
#include <cstdio>
#include <execinfo.h>
10

11 12 13
#include <cstdlib>
#include <cstring>

14
#include "fabulous/utils/Color.hpp"
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
15
#include "fabulous/utils/Utils.hpp"
16

17
namespace fabulous {
18

MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
19 20 21 22 23 24 25 26 27 28 29 30
#ifndef FABULOUS_USE_BACKTRACE
# define FABULOUS_USE_BACKTRACE 1
#endif

#define FABULOUS_MAX_BACKTRACE_SIZE 20

#define FABULOUS_ERROR_LIST(ERROR)                                      \
    ERROR(GENERIC,            Generic,           generic)               \
    ERROR(KERNEL,             Kernel,            kernel)                \
    ERROR(NUMERIC,            Numeric,           numeric)               \
    ERROR(NOT_IMPLEMENTED,    NotImplemented,    not_implemented)       \
    ERROR(INTERNAL,           Internal,          internal)              \
31
    ERROR(ASSERTION,          Assertion,         assertion)             \
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
    ERROR(PARAMETER,          Parameter,         parameter)             \
    ERROR(INPUT,              Input,             input)                 \
    ERROR(UNSUPPORTED,        Unsupported,       unsupported_operation) \
    ERROR(INVALID_ARITHMETIC, InvalidArithmetic, invalid_arithmetic)    \
    ERROR(FILE,               File,              file)                  \


#define FABULOUS_DEFINE_ERROR_ENUM_DECLARATOR_(CAPITAL_, CamelCase_, lower_case_) \
    FABULOUS_ERROR_##CAPITAL_,

enum error_code {
    FABULOUS_SUCCESS = 0,
    FABULOUS_ERROR_LIST(FABULOUS_DEFINE_ERROR_ENUM_DECLARATOR_)
};

#define FABULOUS_DEFINE_ERROR_STRING_CASE_(CAPITAL_, CamelCase_, lower_case_) \
    case FABULOUS_ERROR_##CAPITAL_:                                     \
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
49
    case -FABULOUS_ERROR_##CAPITAL_:                                    \
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
50 51 52
    return #lower_case_;                                                \
    break;

MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
53
inline ::std::string error_type(const int errcode)
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
54 55 56 57
{
    switch ( errcode ) {
        FABULOUS_ERROR_LIST(FABULOUS_DEFINE_ERROR_STRING_CASE_)
    default:
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
58
            return std::string("Invalid error code ") + std::to_string(errcode);
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
        break;
    };
}

class Error : public ::std::exception
{
private:
    std::string _name;
    std::string _what;
    error_code _errcode;
    std::string _what_b;

    #if FABULOUS_USE_BACKTRACE
    int _backtrace_size;
    void *_backtrace[FABULOUS_MAX_BACKTRACE_SIZE];
    #endif


protected:
    Error(const std::string &name, const std::string &what, error_code errcode):
        _name{name},
        _what{what},
        _errcode{errcode},
        _what_b{what}
    {
        #if FABULOUS_USE_BACKTRACE
        _backtrace_size = ::backtrace(_backtrace, array_size(_backtrace));
        #endif
        static ::std::stringstream ss;
        ss << _name << ": " << _what;
        _what_b = ss.str();
    }
public:

    const char *what() const noexcept override final
    {
        return _what_b.c_str();
    }

    int get_error_code()
    {
        return static_cast<int>(_errcode);
    }


    #if FABULOUS_USE_BACKTRACE
    void print_backtrace()
    {
        int f_no = ::fileno(stderr);
        ::backtrace_symbols_fd(_backtrace, _backtrace_size, f_no);
        if (_backtrace_size == FABULOUS_MAX_BACKTRACE_SIZE) {
            std::fprintf(stderr, " ... \n");
        }
    }
    #endif
};

#define FABULOUS_DECLARE_ERROR_(CAPITAL_, CamelCase_, lower_case_)      \
    class CamelCase_##Error : public ::fabulous::Error                  \
    {                                                                   \
      public:                                                           \
        CamelCase_##Error(const ::std::string &what=""):                \
            Error{#CAPITAL_ "_ERROR", what, FABULOUS_ERROR_##CAPITAL_}  \
        {                                                               \
        }                                                               \
    };                                                                  \

FABULOUS_ERROR_LIST(FABULOUS_DECLARE_ERROR_)

128
/** \brief display an error message */
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
129
inline void error(const ::std::string &errstr, const ::std::string &errprefix = "error: ")
130
{
131 132
    std::cerr << Color::error << errprefix
              << Color::reset << errstr << "\n";
133 134
}

135
/** \brief display an warning message */
136
inline void warning(const std::string &errstr)
137
{
138
    std::cerr << Color::warning << "warning: "
139
              << Color::reset << errstr << "\n";
140 141
}

142
/** \brief display an debug message */
143
inline void debug(const std::string &errstr)
144
{
145
    #ifdef FABULOUS_DEBUG_MODE
146 147
    std::cerr << Color::debug << "DEBUG: "
              << Color::reset << errstr << "\n";
148 149 150
    #else
    (void) errstr;
    #endif
151 152
}

153
/** \brief display an note message */
154
inline void note(const std::string &errstr)
155 156
{
    std::cerr << Color::note << "note: "
157
              << Color::reset << errstr << "\n";
158 159
}

160
/** \brief display an error message and exit with an error code */
161
inline void fatal_error(const std::string &errstr, int error_code = EXIT_FAILURE)
162 163
{
    error(errstr, "fatal error: ");
164 165 166
    #ifdef FABULOUS_DEBUG_MODE
    abort();
    #endif
167
    exit(error_code);
168 169
}

170
/** \brief strip directory name */
171
inline const char *basename(const char *str)
172 173 174 175
{
    return strrchr(str, '/')  ? strrchr(str, '/')+1 : str;
}

MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
176
#define FABULOUS_CONCAT_(errstr_, ACTION)                       \
177 178 179 180 181 182
    do {                                                        \
        std::stringstream fabulous_ss_err_impl_;                \
        fabulous_ss_err_impl_                                   \
            << ::fabulous::Color::green                         \
            << ::fabulous::basename(__FILE__) <<":" << __LINE__ \
            <<"["<< __func__ << "]: "                           \
183
            <<  ::fabulous::Color::reset << errstr_;            \
184 185 186
        ACTION(fabulous_ss_err_impl_.str());                    \
    }while(0)                                                   \

MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
187 188
#define FABULOUS_ERROR(errstr_)                         \
    FABULOUS_CONCAT_(errstr_, ::fabulous::error)
189
#define FABULOUS_WARNING(errstr_)                       \
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
190 191 192
    FABULOUS_CONCAT_(errstr_, ::fabulous::warning)
#define FABULOUS_DEBUG(errstr_)                         \
    FABULOUS_CONCAT_(errstr_, ::fabulous::debug)
193
#define FABULOUS_FATAL_ERROR(errstr_)                   \
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
194
    FABULOUS_CONCAT_(errstr_, ::fabulous::fatal_error)
195
#define FABULOUS_NOTE(errstr_)                  \
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
196
    FABULOUS_CONCAT_(errstr_, ::fabulous::note)
197 198 199 200

#ifdef FABULOUS_DEBUG_MODE

#define FABULOUS_THROW(ErrorName_, errstr_)                     \
MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
201
    FABULOUS_FATAL_ERROR( #ErrorName_ "Error: " << errstr_ )
202 203 204

#else // FABULOUS_DEBUG_MODE

MIJIEUX Thomas's avatar
MIJIEUX Thomas committed
205 206
#define FABULOUS_THROW(ErrorName_, errstr_)                             \
    FABULOUS_CONCAT_(errstr_, throw ::fabulous::ErrorName_##Error)
207

208 209 210 211 212 213 214 215 216
#endif // FABULOUS_DEBUG_MODE


#define FABULOUS_ASSERT( cond_ )                                        \
    do{                                                                 \
        if (!(cond_)) {                                                 \
            FABULOUS_THROW(Assertion, "ASSERT( "#cond_" ) FAILED!");    \
        }                                                               \
    }while(0)                                                           \
217

218
} // end namespace fabulous
219 220

#endif // FABULOUS_ERROR_HPP