error.h 5.85 KB
Newer Older
1
2
#ifndef _SPEL_ERROR_H_
#define _SPEL_ERROR_H_
3
4
5
6
7
8

#include <string>
#include <iostream>
#include <set>
#include <utility>
#include <sstream>
9
#include <thread>
Damien Leroux's avatar
Damien Leroux committed
10
#include <mutex>
11
#include <vector>
12
13
14
extern "C" {
#include <unistd.h>
}
15
16
17
18
19
20
21

#define _WHITE "\x1b[37;1m"
#define _RED "\x1b[31;1m"
#define _YELLOW "\x1b[33;1m"
#define _CYAN "\x1b[36;1m"
#define _NORMAL "\x1b[0;m"

22
23
#define MSG_HANDLER_IS_SYNCED

24
struct msg_handler_t {
25
#ifdef MSG_HANDLER_IS_SYNCED
26
27
    typedef std::recursive_mutex lock_type;
    typedef std::unique_lock<lock_type> scoped_lock_type;
28
29
30
31
32
33
34
35
36
#else
    typedef struct {
        void lock() {}
        void unlock() {}
    } lock_type;
    typedef struct _slt {
        _slt(lock_type&) {}
    } scoped_lock_type;
#endif
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    /* inspired from http://stackoverflow.com/questions/22042414/c-stream-insert-string-after-newline */
    class HeaderInserter : public std::streambuf {
        std::streambuf* dest;
        bool start_of_line;
    protected:
        int overflow(int ch) override
        {
            int retval = 0;
            if (ch != traits_type::eof()) {
                if (start_of_line) {
                    int idt = msg_handler_t::get_indent();
                    for (int i = 0; i < idt; ++i) {
                        dest->sputc(' ');
                    }
                }
                retval = dest->sputc( ch );
                start_of_line = ch == '\n';
            }
            return retval;
        }
    public:
        HeaderInserter(std::streambuf* dest)
            : dest(dest)
            , start_of_line(true)
        {}
    };

65
66
67
68
    struct state_t {
        bool color;
        std::set<std::string> workarounds;
        int count;
69
        int debug_indent;
70
        std::vector<std::function<void()>> hooks;
71
72
        std::streambuf* old_rdbuf;
        HeaderInserter hi;
73
74
75
76
77
78

        const char* error() { ++count; return color ? _RED : ""; }
        const char* warning() { return color ? _YELLOW : ""; }
        const char* info() { return color ? _CYAN : ""; }
        const char* normal() { return color ? _NORMAL : ""; }

79
80
81
82
83
84
85
86
        state_t()
            : color(!!isatty(fileno(stdout))), workarounds(), count(0), debug_indent(0), hooks()
            , old_rdbuf(std::clog.rdbuf())
            , hi(old_rdbuf)
        {
            std::clog.rdbuf(&hi);
        }
		~state_t() { std::clog.rdbuf(old_rdbuf); }
87
88
        void check(bool fatal);
        void reset();
89
        void run_hooks() { for (auto& f: hooks) { f(); } }
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    };

    static state_t& instance() { static state_t _; return _; }

    static void set_color(bool _) { instance().color = _; }
    static bool color() { return instance().color; }

    static const char* e() { return instance().error(); }
    static const char* w() { return instance().warning(); }
    static const char* i() { return instance().info(); }
    static const char* n() { return instance().normal(); }

    static void check(bool fatal) { instance().check(fatal); }
    static void reset() { instance().reset(); }
104

105
106
107
    static void hook(std::function<void()>&& f) { instance().hooks.push_back(f); }
    static void run_hooks() { instance().run_hooks(); }

108
109
110
111
    static void indent() { instance().debug_indent += 3; }
    static void dedent() { instance().debug_indent -= 3; }
    static int get_indent() { return instance().debug_indent; }

112
    static lock_type mutex;
113
114
115
};


116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#define MSG_ERROR(_msg_expr_, _workaround_expr_) \
    do {\
        {msg_handler_t::scoped_lock_type _(msg_handler_t::mutex);\
        std::cerr << msg_handler_t::e() << "[ERR] " << _msg_expr_ << msg_handler_t::n() << std::endl;}\
        std::stringstream s; s << _workaround_expr_;\
        if (s.str().size()) { msg_handler_t::instance().workarounds.insert(s.str()); }\
        msg_handler_t::run_hooks();\
} while (0)

#define MSG_WARNING(_msg_expr_) \
    do {\
        msg_handler_t::scoped_lock_type _(msg_handler_t::mutex);\
        std::cerr << msg_handler_t::w() << "[WRN] " << _msg_expr_ << msg_handler_t::n() << std::endl;\
        msg_handler_t::run_hooks();\
    } while(0)

#define MSG_INFO(_msg_expr_) \
    do {\
        msg_handler_t::scoped_lock_type _(msg_handler_t::mutex);\
        std::cout << msg_handler_t::i() << "[MSG] " << _msg_expr_ << msg_handler_t::n() << std::endl;\
        msg_handler_t::run_hooks();\
    } while(0)

#define MSG_DEBUG(_msg_expr_) \
    do {\
        msg_handler_t::scoped_lock_type _(msg_handler_t::mutex);\
142
        std::clog << _msg_expr_ << std::endl;\
143
144
        msg_handler_t::run_hooks();\
    } while(0)
145

146
147
#define MSG_DEBUG_INDENT msg_handler_t::indent()
#define MSG_DEBUG_DEDENT msg_handler_t::dedent()
148
149
150

inline void msg_handler_t::state_t::check(bool fatal)
{
151
    msg_handler_t::scoped_lock_type _(msg_handler_t::mutex);
152
153
154
155
156
157
158
159
160
161
162
163
    if (count > 0) {
        std::cerr << info() << "[MSG] " << count;
        if (count > 1) {
            std::cout << " errors were";
        } else {
            std::cout << " error was";
        }
        std::cout << " reported. Suggestions to fix this:" << std::endl;
        for (auto& w: workarounds) {
            std::cout << "      - " << w << std::endl;
        }
        if (fatal) {
164
			std::cout << normal() << "At least one fatal error encountered. Aborting process." << std::endl;
165
166
167
168
169
170
171
172
173
174
175
            exit(-count);
        } else {
            reset();
        }
        std::cout << normal();
    }
}

inline void msg_handler_t::state_t::reset()
{
    if (workarounds.size()) {
176
        MSG_WARNING(workarounds.size() << " workarounds silently discarded");
177
178
179
180
181
182
183
184
185
186
187
    }
    count = 0;
    workarounds.clear();
}

#define WHITE (msg_handler_t::instance().color ? _WHITE : "")
#define YELLOW (msg_handler_t::instance().color ? _YELLOW : "")
#define RED (msg_handler_t::instance().color ? _RED : "")
#define CYAN (msg_handler_t::instance().color ? _CYAN : "")
#define NORMAL (msg_handler_t::instance().color ? _NORMAL : "")

188
189
190
191
192
193
#ifdef NDEBUG
#define DUMP_FILE_LINE()
#else
#define DUMP_FILE_LINE() MSG_DEBUG(__FILE__ << ':' << __LINE__)
#endif

194
195
#endif