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
|