File: error.hpp

package info (click to toggle)
foonathan-memory 0.7.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,644 kB
  • sloc: cpp: 12,425; xml: 139; sh: 48; makefile: 25
file content (287 lines) | stat: -rw-r--r-- 13,673 bytes parent folder | download
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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
// Copyright (C) 2015-2025 Jonathan Müller and foonathan/memory contributors
// SPDX-License-Identifier: Zlib

/// \file
/// The exception classes.

#ifndef FOONATHAN_MEMORY_ERROR_HPP_INCLUDED
#define FOONATHAN_MEMORY_ERROR_HPP_INCLUDED

#include <cstddef>
#include <new>

#include "config.hpp"

namespace foonathan
{
    namespace memory
    {
        /// Contains information about an allocator.
        /// It can be used for logging in the various handler functions.
        /// \ingroup core
        struct allocator_info
        {
            /// The name of the allocator.
            /// It is a NTBS whose lifetime is not managed by this object,
            /// it must be stored elsewhere or be a string literal.
            const char* name;

            /// A pointer representing an allocator.
            /// It does not necessarily point to the beginning of the allocator object,
            /// the only guarantee is that different allocator objects result in a different pointer value.
            /// For stateless allocators it is sometimes \c nullptr.
            /// \note The pointer must not be cast back to any allocator type.
            const void* allocator;

            /// \effects Creates it by giving it the name of the allocator and a pointer.
            constexpr allocator_info(const char* n, const void* alloc) noexcept
            : name(n), allocator(alloc)
            {
            }

            /// @{
            /// \effects Compares two \ref allocator_info objects, they are equal, if the \ref allocator is the same.
            /// \returns The result of the comparision.
            friend constexpr bool operator==(const allocator_info& a,
                                             const allocator_info& b) noexcept
            {
                return a.allocator == b.allocator;
            }

            friend constexpr bool operator!=(const allocator_info& a,
                                             const allocator_info& b) noexcept
            {
                return a.allocator != b.allocator;
            }
            /// @}
        };

        /// The exception class thrown when a low level allocator runs out of memory.
        /// It is derived from \c std::bad_alloc.
        /// This can happen if a low level allocation function like \c std::malloc() runs out of memory.
        /// Throwing can be prohibited by the handler function.
        /// \ingroup core
        class out_of_memory : public std::bad_alloc
        {
        public:
            /// The type of the handler called in the constructor of \ref out_of_memory.
            /// When an out of memory situation is encountered and the exception class created,
            /// this handler gets called.
            /// It is especially useful if exception support is disabled.
            /// It gets the \ref allocator_info and the amount of memory that was tried to be allocated.
            /// \requiredbe It can log the error, throw a different exception derived from \c std::bad_alloc or abort the program.
            /// If it returns, this exception object will be created and thrown.
            /// \defaultbe On a hosted implementation it logs the error on \c stderr and continues execution,
            /// leading to this exception being thrown.
            /// On a freestanding implementation it does nothing.
            /// \note It is different from \c std::new_handler; it will not be called in a loop trying to allocate memory
            /// or something like that. Its only job is to report the error.
            using handler = void (*)(const allocator_info& info, std::size_t amount);

            /// \effects Sets \c h as the new \ref handler in an atomic operation.
            /// A \c nullptr sets the default \ref handler.
            /// \returns The previous \ref handler. This is never \c nullptr.
            static handler set_handler(handler h);

            /// \returns The current \ref handler. This is never \c nullptr.
            static handler get_handler();

            /// \effects Creates it by passing it the \ref allocator_info and the amount of memory failed to be allocated.
            /// It also calls the \ref handler to control whether or not it will be thrown.
            out_of_memory(const allocator_info& info, std::size_t amount);

            /// \returns A static NTBS that describes the error.
            /// It does not contain any specific information since there is no memory for formatting.
            const char* what() const noexcept override;

            /// \returns The \ref allocator_info passed to it in the constructor.
            const allocator_info& allocator() const noexcept
            {
                return info_;
            }

            /// \returns The amount of memory that was tried to be allocated.
            /// This is the value passed in the constructor.
            std::size_t failed_allocation_size() const noexcept
            {
                return amount_;
            }

        private:
            allocator_info info_;
            std::size_t    amount_;
        };

        /// A special case of \ref out_of_memory errors
        /// thrown when a low-level allocator with a fixed size runs out of memory.
        /// For example, thrown by \ref fixed_block_allocator or \ref static_allocator.<br>
        /// It is derived from \ref out_of_memory but does not provide its own handler.
        /// \ingroup core
        class out_of_fixed_memory : public out_of_memory
        {
        public:
            /// \effects Just forwards to \ref out_of_memory.
            out_of_fixed_memory(const allocator_info& info, std::size_t amount)
            : out_of_memory(info, amount)
            {
            }

            /// \returns A static NTBS that describes the error.
            /// It does not contain any specific information since there is no memory for formatting.
            const char* what() const noexcept override;
        };

        /// The exception class thrown when an allocation size is bigger than the supported maximum.
        /// This size is either the node, array or alignment parameter in a call to an allocation function.
        /// If those exceed the supported maximum returned by \c max_node_size(), \c max_array_size() or \c max_alignment(),
        /// one of its derived classes will be thrown or this class if in a situation where the type is unknown.
        /// It is derived from \c std::bad_alloc.
        /// Throwing can be prohibited by the handler function.
        /// \note Even if all parameters are less than the maximum, \ref out_of_memory or a similar exception can be thrown,
        /// because the maximum functions return an upper bound and not the actual supported maximum size,
        /// since it always depends on fence memory, alignment buffer and the like.
        /// \note A user should only \c catch for \c bad_allocation_size, not the derived classes.
        /// \note Most checks will only be done if \ref FOONATHAN_MEMORY_CHECK_ALLOCATION_SIZE is \c true.
        /// \ingroup core
        class bad_allocation_size : public std::bad_alloc
        {
        public:
            /// The type of the handler called in the constructor of \ref bad_allocation_size.
            /// When a bad allocation size is detected and the exception object created,
            /// this handler gets called.
            /// It is especially useful if exception support is disabled.
            /// It gets the \ref allocator_info, the size passed to the function and the supported size
            /// (the latter is still an upper bound).
            /// \requiredbe It can log the error, throw a different exception derived from \c std::bad_alloc or abort the program.
            /// If it returns, this exception object will be created and thrown.
            /// \defaultbe On a hosted implementation it logs the error on \c stderr and continues execution,
            /// leading to this exception being thrown.
            /// On a freestanding implementation it does nothing.
            using handler = void (*)(const allocator_info& info, std::size_t passed,
                                     std::size_t supported);

            /// \effects Sets \c h as the new \ref handler in an atomic operation.
            /// A \c nullptr sets the default \ref handler.
            /// \returns The previous \ref handler. This is never \c nullptr.
            static handler set_handler(handler h);

            /// \returns The current \ref handler. This is never \c nullptr.
            static handler get_handler();

            /// \effects Creates it by passing it the \ref allocator_info, the size passed to the allocation function
            /// and an upper bound on the supported size.
            /// It also calls the \ref handler to control whether or not it will be thrown.
            bad_allocation_size(const allocator_info& info, std::size_t passed,
                                std::size_t supported);

            /// \returns A static NTBS that describes the error.
            /// It does not contain any specific information since there is no memory for formatting.
            const char* what() const noexcept override;

            /// \returns The \ref allocator_info passed to it in the constructor.
            const allocator_info& allocator() const noexcept
            {
                return info_;
            }

            /// \returns The size or alignment value that was passed to the allocation function
            /// which was too big. This is the same value passed to the constructor.
            std::size_t passed_value() const noexcept
            {
                return passed_;
            }

            /// \returns An upper bound on the maximum supported size/alignment.
            /// It is only an upper bound, values below can fail, but values above will always fail.
            std::size_t supported_value() const noexcept
            {
                return supported_;
            }

        private:
            allocator_info info_;
            std::size_t    passed_, supported_;
        };

        /// The exception class thrown when the node size exceeds the supported maximum,
        /// i.e. it is bigger than \c max_node_size().
        /// It is derived from \ref bad_allocation_size but does not override the handler.
        /// \ingroup core
        class bad_node_size : public bad_allocation_size
        {
        public:
            /// \effects Just forwards to \ref bad_allocation_size.
            bad_node_size(const allocator_info& info, std::size_t passed, std::size_t supported)
            : bad_allocation_size(info, passed, supported)
            {
            }

            /// \returns A static NTBS that describes the error.
            /// It does not contain any specific information since there is no memory for formatting.
            const char* what() const noexcept override;
        };

        /// The exception class thrown when the array size exceeds the supported maximum,
        /// i.e. it is bigger than \c max_array_size().
        /// It is derived from \ref bad_allocation_size but does not override the handler.
        /// \ingroup core
        class bad_array_size : public bad_allocation_size
        {
        public:
            /// \effects Just forwards to \ref bad_allocation_size.
            bad_array_size(const allocator_info& info, std::size_t passed, std::size_t supported)
            : bad_allocation_size(info, passed, supported)
            {
            }

            /// \returns A static NTBS that describes the error.
            /// It does not contain any specific information since there is no memory for formatting.
            const char* what() const noexcept override;
        };

        /// The exception class thrown when the alignment exceeds the supported maximum,
        /// i.e. it is bigger than \c max_alignment().
        /// It is derived from \ref bad_allocation_size but does not override the handler.
        /// \ingroup core
        class bad_alignment : public bad_allocation_size
        {
        public:
            /// \effects Just forwards to \ref bad_allocation_size.
            /// \c passed is <tt>count * size</tt>, \c supported the size in bytes.
            bad_alignment(const allocator_info& info, std::size_t passed, std::size_t supported)
            : bad_allocation_size(info, passed, supported)
            {
            }

            /// \returns A static NTBS that describes the error.
            /// It does not contain any specific information since there is no memory for formatting.
            const char* what() const noexcept override;
        };

        namespace detail
        {
            template <class Ex, typename Func>
            void check_allocation_size(std::size_t passed, Func f, const allocator_info& info)
            {
#if FOONATHAN_MEMORY_CHECK_ALLOCATION_SIZE
                auto supported = f();
                if (passed > supported)
                    FOONATHAN_THROW(Ex(info, passed, supported));
#else
                (void)passed;
                (void)f;
                (void)info;
#endif
            }

            template <class Ex>
            void check_allocation_size(std::size_t passed, std::size_t supported,
                                       const allocator_info& info)
            {
                check_allocation_size<Ex>(passed, [&] { return supported; }, info);
            }
        } // namespace detail
    } // namespace memory
} // namespace foonathan

#endif // FOONATHAN_MEMORY_ERROR_HPP_INCLUDED