dfx 0.1.0
Linux-based dynamic dataflow executor
Loading...
Searching...
No Matches
Exception.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2025-2026 Vincent Leroy
2// SPDX-License-Identifier: MIT
3//
4// This file is part of dfx.
5//
6// Licensed under the MIT License. See the LICENSE file in the project root
7// for full license information.
8
9#pragma once
10
11// Standard includes
12#include <concepts>
13#include <cstring>
14#include <format>
15#include <functional>
16#include <source_location>
17#include <stdexcept>
18#include <vector>
19
20// Project includes
21#include "Log.hpp"
22
25
26namespace dfx::Utils
27{
28class Exception;
29
34using ExceptionStack = std::vector<Exception>;
35
51class Exception : public std::runtime_error
52{
53public:
59 template<typename ... Args>
60 Exception(std::source_location location, std::format_string<Args ...> message, Args && ... args)
61 : std::runtime_error(std::format(message, std::forward<Args>(args)...))
62 , _location { std::move(location) }
63 {}
64
70 explicit Exception(std::string const & message) noexcept
71 : std::runtime_error(message)
72 {}
73
74public:
76 uint32_t line() const noexcept { return _location.line(); }
78 char const * filename() const noexcept { return _filename(_location.file_name(), _location.file_name() == nullptr ? 0 : std::strlen(_location.file_name())); }
80 char const * function() const noexcept { return _location.function_name(); }
81
82public:
92 template<typename ... Args>
93 [[noreturn]] static void throwNested(std::source_location location, std::format_string<Args ...> message, Args && ... args)
94 { std::throw_with_nested(Exception(std::move(location), message, std::forward<Args>(args)...)); }
95
99 [[noreturn]] static void throwNestedVector(std::vector<std::string> const & errors);
100
107
116 static std::vector<std::string> exceptionStackToString(Exception const & e, std::size_t initialIndentLevel = 0, std::size_t indentSize = 2, bool addSourceLocation = true);
117
129 static void printToLogger(Exception const & e, spdlog::level::level_enum lvl = spdlog::level::err,
130 std::string const & loggerName = "", std::size_t initialIndentLevel = 0,
131 std::string const & logPattern = noFileLogPattern,
132 std::string const & currentLogPattern = defaultLogPattern);
133
142 static void printToLogger(Exception const & e, std::shared_ptr<spdlog::logger> logger,
143 spdlog::level::level_enum lvl = spdlog::level::err,
144 std::size_t initialIndentLevel = 0,
145 std::string const & logPattern = noFileLogPattern,
146 std::string const & currentLogPattern = defaultLogPattern);
147
148private:
149 static constexpr char const * _filename(char const * const path, std::size_t const length) noexcept
150 {
151 if (path == nullptr || length == 0 || path[0] == '\0')
152 return nullptr;
153
154 auto file = path + length;
155 while (*file != '/' && file != path)
156 --file;
157
158 if (*file == '/')
159 ++file;
160
161 return file;
162 }
163
164private:
165 std::source_location const _location;
166};
167
175template<typename T>
176constexpr bool __b_assert_expr(T && expr)
177{
178 static_assert(!(std::is_same_v<std::decay_t<T>, char*> ||
179 std::is_array_v<std::remove_reference_t<T>>),
180 "DFX_ASSERT: First argument appears to be a string literal or array");
181
182 static_assert(std::is_convertible_v<T, bool> || std::is_constructible_v<bool, T>,
183 "DFX_ASSERT: First argument must be convertible to bool.");
184
185 return static_cast<bool>(std::forward<T>(expr));
186}
187
206template<typename F, typename ... Args>
207requires std::invocable<F, Args...>
208auto safeInvoke(std::shared_ptr<spdlog::logger> logger, F && f, Args && ... args) noexcept
209{
210 using Result = std::invoke_result_t<F, Args...>;
211
212 try
213 {
214 if constexpr (std::is_void_v<Result>)
215 std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
216 else
217 return std::optional<Result>{ std::invoke(std::forward<F>(f), std::forward<Args>(args)...) };
218 }
219 catch (Exception const & e)
220 { if (logger != nullptr) Exception::printToLogger(e, logger); }
221 catch (std::exception const & e)
222 { if (logger != nullptr) SPDLOG_LOGGER_ERROR(logger, "An error occured: {}", e.what()); }
223 catch (...)
224 { if (logger != nullptr) SPDLOG_LOGGER_ERROR(logger, "An unknown error occured"); }
225
226 if constexpr (!std::is_void_v<Result>)
227 return std::optional<Result>{};
228}
229
232template<typename F, typename ... Args>
233requires std::invocable<F, Args...>
234auto safeInvoke(std::string const & loggerName, F && f, Args && ... args) noexcept
235{
236 return safeInvoke(spdlog::get(loggerName), std::forward<F>(f), std::forward<Args>(args)...);
237}
238} // !namespace dfx::Utils
239
242#define DFX_CURRENT_SOURCE_LOCATION std::source_location::current()
243
246#define DFX_THROW(msg, ...) \
247 throw ::dfx::Utils::Exception(DFX_CURRENT_SOURCE_LOCATION, msg __VA_OPT__(,) __VA_ARGS__)
248
253#define DFX_THROW_NESTED(msg, ...) \
254 ::dfx::Utils::Exception::throwNested(DFX_CURRENT_SOURCE_LOCATION, msg __VA_OPT__(,) __VA_ARGS__)
255
258#define DFX_THROW_VECTOR_NESTED(errors, msg, ...) \
259 do { \
260 try { ::dfx::Utils::Exception::throwNestedVector(errors); } \
261 catch (...) { DFX_THROW_NESTED(msg __VA_OPT__(,) __VA_ARGS__); } \
262 } while (false)
263
268#define DFX_ASSERT(expr, msg, ...) \
269 do { \
270 if (!::dfx::Utils::__b_assert_expr(expr)) \
271 DFX_THROW(msg __VA_OPT__(,) __VA_ARGS__); \
272 } while (false)
Runtime error that captures a std::source_location.
Definition Exception.hpp:52
char const * filename() const noexcept
Return the base filename from the recorded source location.
Definition Exception.hpp:78
static std::vector< std::string > exceptionStackToString(Exception const &e, std::size_t initialIndentLevel=0, std::size_t indentSize=2, bool addSourceLocation=true)
Convert a nested exception chain to a list of printable lines.
static void printToLogger(Exception const &e, std::shared_ptr< spdlog::logger > logger, spdlog::level::level_enum lvl=spdlog::level::err, std::size_t initialIndentLevel=0, std::string const &logPattern=noFileLogPattern, std::string const &currentLogPattern=defaultLogPattern)
Log an exception (including nested exceptions) using an explicit logger instance.
uint32_t line() const noexcept
Return the line number recorded in the associated source location.
Definition Exception.hpp:76
static ExceptionStack getExceptionStack(Exception const &e)
Extract a nested exception chain into an ExceptionStack.
Exception(std::source_location location, std::format_string< Args ... > message, Args &&... args)
Construct an exception with a formatted message and a specific source location.
Definition Exception.hpp:60
char const * function() const noexcept
Return the function name recorded in the associated source location.
Definition Exception.hpp:80
static void printToLogger(Exception const &e, spdlog::level::level_enum lvl=spdlog::level::err, std::string const &loggerName="", std::size_t initialIndentLevel=0, std::string const &logPattern=noFileLogPattern, std::string const &currentLogPattern=defaultLogPattern)
Log an exception (including nested exceptions) using a logger name.
Exception(std::string const &message) noexcept
Construct an exception from an already-formatted message.
Definition Exception.hpp:70
static void throwNested(std::source_location location, std::format_string< Args ... > message, Args &&... args)
Throw an Exception nested within the currently handled exception.
Definition Exception.hpp:93
static void throwNestedVector(std::vector< std::string > const &errors)
Converts a vector of strings into a nested exception chain.
Definition SystemConfigCommandHandler.hpp:15
constexpr bool __b_assert_expr(T &&expr)
Helper used by DFX_ASSERT to validate and evaluate the asserted expression.
Definition Exception.hpp:176
auto safeInvoke(std::shared_ptr< spdlog::logger > logger, F &&f, Args &&... args) noexcept
Invoke a callable and catch/log all exceptions.
Definition Exception.hpp:208
std::vector< Exception > ExceptionStack
Convenience alias for a linearized nested exception chain.
Definition Exception.hpp:34
STL namespace.