aboutsummaryrefslogtreecommitdiffstats
path: root/util/generic/yexception.h
blob: e6507e563f3723d6437c4cbd9cad33008e27a4e5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#pragma once

#include "bt_exception.h"
#include "strbuf.h"
#include "string.h"
#include "utility.h"
#include "va_args.h"
#include <utility>

#include <util/stream/tempbuf.h>
#include <util/system/compat.h>
#include <util/system/compiler.h>
#include <util/system/defaults.h>
#include <util/system/error.h>
#include <util/system/src_location.h>
#include <util/system/platform.h>

#include <exception>

#include <cstdio>

class TBackTrace;

namespace NPrivateException {
    class TTempBufCuttingWrapperOutput: public IOutputStream {
    public:
        TTempBufCuttingWrapperOutput(TTempBuf& tempbuf)
            : TempBuf_(tempbuf)
        {
        }

        void DoWrite(const void* data, size_t len) override {
            TempBuf_.Append(data, Min(len, TempBuf_.Left()));
        }

    private:
        TTempBuf& TempBuf_;
    };

    class yexception: public std::exception {
    public:
        yexception();
        yexception(const yexception&) = default;
        yexception(yexception&&) = default;

        yexception& operator=(const yexception&) = default;
        yexception& operator=(yexception&&) = default;

        const char* what() const noexcept override;
        virtual const TBackTrace* BackTrace() const noexcept;

        template <class T>
        inline void Append(const T& t) {
            TTempBufCuttingWrapperOutput tempBuf(Buf_);
            static_cast<IOutputStream&>(tempBuf) << t;
            ZeroTerminate();
        }

        TStringBuf AsStrBuf() const Y_LIFETIME_BOUND;

    private:
        void ZeroTerminate() noexcept;

    private:
        TTempBuf Buf_;
    };

    template <class E, class T>
    static inline std::enable_if_t<std::is_base_of<yexception, std::decay_t<E>>::value, E&&>
    operator<<(E&& e Y_LIFETIME_BOUND, const T& t) {
        e.Append(t);

        return std::forward<E>(e);
    }

    template <class T>
    static inline T&& operator+(const TSourceLocation& sl, T&& t Y_LIFETIME_BOUND) {
        return std::forward<T>(t << sl << TStringBuf(": "));
    }
}

class yexception: public NPrivateException::yexception {
};

Y_DECLARE_OUT_SPEC(inline, yexception, stream, value) {
    stream << value.AsStrBuf();
}

class TSystemError: public yexception {
public:
    TSystemError(int status)
        : Status_(status)
    {
        Init();
    }

    TSystemError()
        : TSystemError(LastSystemError())
    {
    }

    int Status() const noexcept {
        return Status_;
    }

private:
    void Init();

private:
    int Status_;
};

class TIoException: public TSystemError {
};

class TIoSystemError: public TIoException {
};

class TFileError: public TIoSystemError {
};

/**
 * TBadArgumentException should be thrown when an argument supplied to some function (or constructor)
 * is invalid or incorrect.
 *
 * \note
 * A special case when such argument is given to a function which performs type casting
 * (e.g. integer from string) is covered by the TBadCastException class which is derived from
 * TBadArgumentException.
 */
struct TBadArgumentException: public virtual yexception {
};

/**
 * TBadCastException should be thrown to indicate the failure of some type casting procedure
 * (e.g. reading an integer parameter from string).
 */
struct TBadCastException: public virtual TBadArgumentException {
};

#define ythrow throw __LOCATION__ +

namespace NPrivate {
    /// Encapsulates data for one of the most common case in which
    /// exception message consists of single constant string
    struct TSimpleExceptionMessage {
        TSourceLocation Location;
        TStringBuf Message;
    };

    [[noreturn]] void ThrowYException(const TSimpleExceptionMessage& sm);
    [[noreturn]] void ThrowYExceptionWithBacktrace(const TSimpleExceptionMessage& sm);
}

void fputs(const std::exception& e, FILE* f = stderr);

TString CurrentExceptionMessage();

/**
 * Formats current exception for logging purposes. Includes formatted backtrace if it is stored
 * alongside the exception.
 * The output format is a subject to change, do not depend or canonize it.
 * The speed of this method is not guaranteed either. Do not call it in hot paths of your code.
 *
 * The lack of current exception prior to the invocation indicates logical bug in the client code.
 * Y_ABORT_UNLESS asserts the existence of exception, otherwise panic and abort.
 */
TString FormatCurrentException();
void FormatCurrentExceptionTo(IOutputStream& out);

/*
 * A neat method that detects whether stack unwinding is in progress.
 * As its std counterpart (that is std::uncaught_exception())
 * was removed from the standard, this method uses std::uncaught_exceptions() internally.
 *
 * If you are struggling to use this method, please, consider reading
 *
 * http://www.gotw.ca/gotw/047.htm
 * and
 * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdf
 *
 * DO NOT USE THIS METHOD IN DESTRUCTORS.
 */
bool UncaughtException() noexcept;

std::string CurrentExceptionTypeName();

TString FormatExc(const std::exception& exception);

#define Y_THROW_UNLESS_EX(CONDITION, THROW_EXPRESSION) \
    do {                                               \
        if (Y_UNLIKELY(!(CONDITION))) {                \
            ythrow THROW_EXPRESSION;                   \
        }                                              \
    } while (false)
#define Y_ENSURE_EX Y_THROW_UNLESS_EX

/// @def Y_ENSURE_SIMPLE
/// This macro works like the Y_ENSURE, but requires the second argument to be a constant string view.
/// Should not be used directly.
#define Y_ENSURE_SIMPLE(CONDITION, MESSAGE, THROW_FUNCTION)                                                                 \
    do {                                                                                                                    \
        if (Y_UNLIKELY(!(CONDITION))) {                                                                                     \
            /* use variable to guarantee evaluation at compile time */                                                      \
            static constexpr const ::NPrivate::TSimpleExceptionMessage __SIMPLE_EXCEPTION_MESSAGE{__LOCATION__, (MESSAGE)}; \
            THROW_FUNCTION(__SIMPLE_EXCEPTION_MESSAGE);                                                                     \
        }                                                                                                                   \
    } while (false)

#define Y_ENSURE_IMPL_1(CONDITION) Y_ENSURE_SIMPLE(CONDITION, ::TStringBuf("Condition violated: `" Y_STRINGIZE(CONDITION) "'"), ::NPrivate::ThrowYException)
#define Y_ENSURE_IMPL_2(CONDITION, MESSAGE) Y_ENSURE_EX(CONDITION, yexception() << MESSAGE)

#define Y_ENSURE_BT_IMPL_1(CONDITION) Y_ENSURE_SIMPLE(CONDITION, ::TStringBuf("Condition violated: `" Y_STRINGIZE(CONDITION) "'"), ::NPrivate::ThrowYExceptionWithBacktrace)
#define Y_ENSURE_BT_IMPL_2(CONDITION, MESSAGE) Y_ENSURE_EX(CONDITION, TWithBackTrace<yexception>() << MESSAGE)

/**
 * @def Y_ENSURE
 *
 * This macro is intended to be used as a shortcut for `if () { throw }`.
 *
 * @code
 * void DoSomethingLovely(const int x, const int y) {
 *     Y_ENSURE(x > y, "`x` must be greater than `y`");
 *     Y_ENSURE(x > y); // if you are too lazy
 *     // actually doing something nice here
 * }
 * @endcode
 */
#define Y_THROW_UNLESS(...) Y_PASS_VA_ARGS(Y_MACRO_IMPL_DISPATCHER_2(__VA_ARGS__, Y_ENSURE_IMPL_2, Y_ENSURE_IMPL_1)(__VA_ARGS__))
#define Y_ENSURE Y_THROW_UNLESS

/**
 * @def Y_ENSURE_BT
 *
 * This macro is intended to be used as a shortcut for `if () { throw TWithBackTrace<yexception>() << "message"; }`.
 *
 * @code
 * void DoSomethingLovely(const int x, const int y) {
 *     Y_ENSURE_BT(x > y, "`x` must be greater than `y`");
 *     Y_ENSURE_BT(x > y); // if you are too lazy
 *     // actually doing something nice here
 * }
 * @endcode
 */
#define Y_ENSURE_BT(...) Y_PASS_VA_ARGS(Y_MACRO_IMPL_DISPATCHER_2(__VA_ARGS__, Y_ENSURE_BT_IMPL_2, Y_ENSURE_BT_IMPL_1)(__VA_ARGS__))