Skip to content

Instantly share code, notes, and snippets.

@dk949
Last active June 9, 2025 12:29
Show Gist options
  • Save dk949/7ab26f6405813e246528994e0f560fba to your computer and use it in GitHub Desktop.
Save dk949/7ab26f6405813e246528994e0f560fba to your computer and use it in GitHub Desktop.
#ifndef UT_ERR
#define UT_ERR
#include <concepts>
#include <type_traits>
#if __cplusplus < 202'302L
# error this file has to be compiled with at least C++23
#endif
#include <expected>
#include <format>
#include <source_location>
namespace ut {
namespace detail {
template<typename... Args>
struct ErrorStringT {
std::format_string<Args...> fmt;
std::source_location loc;
template<std::convertible_to<std::string_view> Str>
explicit(false) consteval ErrorStringT(Str const &f, std::source_location l = std::source_location::current())
: fmt(std::format_string<Args...> {f})
, loc(l) { }
};
struct EmptyBase { };
template<typename T>
struct ErrorPayload {
T payload;
};
template<typename T>
concept Formattable = requires(std::formatter<T> f) {
{f};
};
} // namespace detail
// This alias is needed to prevent template argument deduction
template<typename... Args>
using ErrorString = detail::ErrorStringT<std::type_identity_t<Args>...>;
template<typename Payload = void, typename Message = std::string>
struct BaseError : std::conditional_t<std::same_as<Payload, void>, detail::EmptyBase, detail::ErrorPayload<Payload>> {
public:
Message msg;
private:
std::source_location m_loc;
public:
BaseError(Message m, std::source_location l) requires(std::constructible_from<Message, std::string>)
: msg(std::move(m))
, m_loc(l) { }
explicit BaseError(Message m, std::source_location l = std::source_location::current())
requires(!std::constructible_from<Message, std::string>)
: msg(std::move(m))
, m_loc(l) { }
template<typename... Args>
explicit BaseError(ErrorString<Args...> fmt, Args &&...args) requires(std::constructible_from<Message, std::string>)
: msg(std::format(fmt.fmt, std::forward<Args>(args)...))
, m_loc(fmt.loc) { }
[[nodiscard]]
std::string format() const requires(detail::Formattable<Message>) {
return std::format("({}) {}:{}:{}:\n{}", m_loc.function_name(), m_loc.file_name(), m_loc.line(), m_loc.column(), msg);
}
[[nodiscard]]
std::source_location const &loc() const {
return m_loc;
}
};
using Error = BaseError<>;
template<typename T>
using ErrorOr = std::expected<T, Error>;
template<typename... Args>
[[nodiscard]]
std::unexpected<BaseError<>> error(ErrorString<Args...> fmt, Args &&...args) {
return std::unexpected(Error {fmt, std::forward<Args>(args)...});
}
} // namespace ut
#endif // UT_ERR
convenient error type for use with std::expected
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment