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 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 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
102
111 static std::vector<std::string> exceptionStackToString(Exception const & e, std::size_t initialIndentLevel = 0, std::size_t indentSize = 2, bool addSourceLocation = true);
112
124 static void printToLogger(Exception const & e, spdlog::level::level_enum lvl = spdlog::level::err,
125 std::string const & loggerName = "", std::size_t initialIndentLevel = 0,
126 std::string const & logPattern = noFileLogPattern,
127 std::string const & currentLogPattern = defaultLogPattern);
128
137 static void printToLogger(Exception const & e, std::shared_ptr<spdlog::logger> logger,
138 spdlog::level::level_enum lvl = spdlog::level::err,
139 std::size_t initialIndentLevel = 0,
140 std::string const & logPattern = noFileLogPattern,
141 std::string const & currentLogPattern = defaultLogPattern);
142
143private:
144 static constexpr char const * _filename(char const * const path, std::size_t const length) noexcept
145 {
146 if (path == nullptr || length == 0 || path[0] == '\0')
147 return nullptr;
148
149 auto file = path + length;
150 while (*file != '/' && file != path)
151 --file;
152
153 if (*file == '/')
154 ++file;
155
156 return file;
157 }
158
159private:
160 std::source_location const _location;
161};
162
170template<typename T>
171constexpr bool __b_assert_expr(T && expr)
172{
173 static_assert(!(std::is_same_v<std::decay_t<T>, char*> ||
174 std::is_array_v<std::remove_reference_t<T>>),
175 "B_ASSERT: First argument appears to be a string literal or array");
176
177 static_assert(std::is_convertible_v<T, bool>,
178 "B_ASSERT: First argument must be convertible to bool.");
179
180 return static_cast<bool>(std::forward<T>(expr));
181}
182
201template<typename F, typename ... Args>
202requires std::invocable<F, Args...>
203auto safeInvoke(std::shared_ptr<spdlog::logger> logger, F && f, Args && ... args) noexcept
204{
205 using Result = std::invoke_result_t<F, Args...>;
206
207 try
208 {
209 if constexpr (std::is_void_v<Result>)
210 std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
211 else
212 return std::optional<Result>{ std::invoke(std::forward<F>(f), std::forward<Args>(args)...) };
213 }
214 catch (Exception const & e)
215 { if (logger != nullptr) Exception::printToLogger(e, logger); }
216 catch (std::exception const & e)
217 { if (logger != nullptr) SPDLOG_LOGGER_ERROR(logger, "An error occured: {}", e.what()); }
218 catch (...)
219 { if (logger != nullptr) SPDLOG_LOGGER_ERROR(logger, "An unknown error occured"); }
220
221 if constexpr (!std::is_void_v<Result>)
222 return std::optional<Result>{};
223}
224
227template<typename F, typename ... Args>
228requires std::invocable<F, Args...>
229auto safeInvoke(std::string const & loggerName, F && f, Args && ... args) noexcept
230{
231 return safeInvoke(spdlog::get(loggerName), std::forward<F>(f), std::forward<Args>(args)...);
232}
233} // !namespace dfx::Utils
234
237#define CURRENT_SOURCE_LOCATION std::source_location::current()
238
243#define THROW_NESTED(msg, ...) ::dfx::Utils::Exception::throwNested(CURRENT_SOURCE_LOCATION, msg __VA_OPT__(,) __VA_ARGS__)
246#define THROW(msg, ...) throw ::dfx::Utils::Exception(CURRENT_SOURCE_LOCATION, msg __VA_OPT__(,) __VA_ARGS__)
251#define B_ASSERT(expr, msg, ...) do { if (!::dfx::Utils::__b_assert_expr(expr)) THROW(msg __VA_OPT__(,) __VA_ARGS__); } 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
Definition SystemConfigCommandHandler.hpp:15
constexpr bool __b_assert_expr(T &&expr)
Helper used by B_ASSERT to validate and evaluate the asserted expression.
Definition Exception.hpp:171
auto safeInvoke(std::shared_ptr< spdlog::logger > logger, F &&f, Args &&... args) noexcept
Invoke a callable and catch/log all exceptions.
Definition Exception.hpp:203
std::vector< Exception > ExceptionStack
Convenience alias for a linearized nested exception chain.
Definition Exception.hpp:34
STL namespace.