CoriEngine
Loading...
Searching...
No Matches
Error.hpp
Go to the documentation of this file.
1#ifndef ERROR_H
2#define ERROR_H
3#include "Logger.hpp"
5
6namespace Cori {
7 namespace Core {
11 template <typename... Types>
12 concept AllAreExceptions = (std::derived_from<Types, std::exception> && ...);
13
26 template <typename... DeclaredTypes>
27 class CoriError final : public std::exception {
28 public:
35 template <typename... Args>
36 explicit CoriError(const std::string& message, Args&&... args) {
37 static_assert(sizeof...(Args) == 2 * sizeof...(DeclaredTypes), "Incorrect number of arguments provided. Expected a description and a value for each declared type.");
38
39 m_Payloads.reserve(sizeof...(DeclaredTypes));
40
41 std::stringstream ss;
42 ss << message;
43
44 if constexpr (sizeof...(Args) > 0) {
45 ss << " | Additional data: (";
46 ProcessArgs<DeclaredTypes...>(ss, std::forward<Args>(args)...);
47 ss << ") |";
48 }
49
50 m_Message = ss.str();
51 }
52
53 ~CoriError() override {
54 if (!m_Seen) {
55 CORI_CORE_ERROR_TAGGED({ Logger::Tags::UnusedError }, "A CoriError was never logged, logging it now. Error: '{}'", what());
56 }
57 }
58
59 CoriError(const CoriError& other) noexcept : m_Payloads(other.m_Payloads), m_Message(other.m_Message), m_Seen(other.m_Seen) {
60 other.m_Seen = true;
61 }
62
63 CoriError(CoriError&& other) noexcept : m_Payloads(std::move(other.m_Payloads)), m_Message(std::move(other.m_Message)), m_Seen(other.m_Seen) {
64 other.m_Seen = true;
65 }
66
67 CoriError& operator=(const CoriError& other) = delete;
68 CoriError& operator=(CoriError&& other) noexcept = delete;
69
74 const char* what() const noexcept override {
75 if (!m_Seen) {
76 m_Seen = true;
77 }
78 return m_Message.c_str();
79 }
80
84 void Ignore() const {
85 m_Seen = true;
86 }
87
93 template <typename T>
94 T Get() const {
95 static_assert(Utility::IsInPack<T, DeclaredTypes...>, "Error: Attempting to Get<T> a type T that was not declared in the CoriError<TypeIsAnalogInPacks...> specialization.");
96 for (const auto& payload : m_Payloads) {
97 if (payload.type() == typeid(T)) {
98 return std::any_cast<T>(payload);
99 }
100 }
101
102 throw std::runtime_error("Type was declared for CoriError but not provided in this specific error instance."); // this should be unreachable, but i will still leave it here just in case
103 }
104
110 template <typename... Types> requires (sizeof...(Types) > 1)
111 std::tuple<Types...> Get() const {
112 return std::make_tuple(Get<Types>()...);
113 }
114
115 private:
116 static_assert(!Utility::HasDuplicates<DeclaredTypes...>, "CoriError cannot be instantiated with duplicate types in its template parameter list. Each retrievable type must be unique.");
117
118 mutable std::vector<std::any> m_Payloads;
119 mutable std::string m_Message;
120 mutable bool m_Seen{ false };
121
122 // ReSharper disable once CppMemberFunctionMayBeStatic
123 void ProcessArgs([[maybe_unused]] std::stringstream& ss) {}
124
125 template <typename...>
126 // ReSharper disable once CppMemberFunctionMayBeStatic
127 void ProcessArgs([[maybe_unused]] std::stringstream& ss) {}
128
129 template <typename DeclaredT, typename... RestT, typename ValT, typename... RestArgs>
130 void ProcessArgs(std::stringstream& ss, const std::string& description, ValT&& value, RestArgs&&... restArgs) {
131 static_assert(std::is_constructible_v<DeclaredT, ValT>, "A declared error type cannot be constructed from its provided value.");
132
133 if constexpr (Utility::IsStreamable<DeclaredT>) {
134 ss << description << " (" << CORI_CLEAN_TYPE_NAME(DeclaredT) << "): '" << value << "'";
135 } else {
136 ss << description << " (" << CORI_CLEAN_TYPE_NAME(DeclaredT) << "): 'Type is not streamable, you can retrieve it with Get<T>()'";
137 }
138
139 if constexpr (sizeof...(RestArgs) > 0) {
140 ss << ", ";
141 }
142
143 m_Payloads.emplace_back(DeclaredT(std::forward<ValT>(value)));
144
145 if constexpr (sizeof...(RestT) > 0) {
146 ProcessArgs<RestT...>(ss, std::forward<RestArgs>(restArgs)...);
147 }
148 }
149 };
150
155 template <typename... Exceptions> requires AllAreExceptions<Exceptions...>
157 public:
158 template <typename E> requires(std::is_same_v<std::decay_t<E>, Exceptions> || ...)
159 PossibleErrors(E&& e) : m_Variant(std::forward<E>(e)) {} // NOLINT (to make clang-tidy happy :) ) constructor should be implicid for std::unexpected to work properly
160
162 if (!m_ErrorHandled) {
163 std::visit([](const auto& e) {
164 CORI_CORE_ERROR_TAGGED({ Logger::Tags::UnusedError }, "An error inside PossibleErrors was never used, logging it now. Error: '{}'", e.what());
165 }, m_Variant);
166 }
167 }
168
169 PossibleErrors(const PossibleErrors& other) noexcept : m_Variant(other.m_Variant), m_ErrorHandled(other.m_ErrorHandled) {
170 other.m_ErrorHandled = true;
171 }
172
173 PossibleErrors(PossibleErrors&& other) noexcept : m_Variant(std::move(other.m_Variant)), m_ErrorHandled(other.m_ErrorHandled) {
174 other.m_ErrorHandled = true;
175 }
176
177 PossibleErrors& operator=(const PossibleErrors& other) = delete;
178 PossibleErrors& operator=(PossibleErrors&& other) noexcept = delete;
179
184 [[nodiscard]] const std::exception* GetRaw() const {
185 m_ErrorHandled = true;
186 return std::visit([](const auto& e) -> const std::exception* {
187 return &e;
188 }, m_Variant);
189 }
190
191
196 template <typename Visitor>
197 void Visit(Visitor&& visitor) const & {
198 m_ErrorHandled = true;
199 std::visit(std::forward<Visitor>(visitor), m_Variant);
200 }
201
206 template <typename Visitor>
207 void Visit(Visitor&& visitor) const && {
208 m_ErrorHandled = true;
209 std::visit(std::forward<Visitor>(visitor), std::move(m_Variant));
210 }
211
218 template <typename SpecificException, typename Func>
219 PossibleErrors& On(Func&& handler) & {
220 auto* e = std::get_if<SpecificException>(&m_Variant);
221 if (e) {
222 handler(*e);
223 m_ErrorHandled = true;
224 }
225 return *this;
226 }
227
234 template <typename SpecificException, typename Func>
235 PossibleErrors&& On(Func&& handler) && {
236 auto* e = std::get_if<SpecificException>(&m_Variant);
237 if (e) {
238 handler(*e);
239 m_ErrorHandled = true;
240 }
241 return std::move(*this);
242 }
243
244
249 const char* GetWhat() const {
250 return GetRaw()->what();
251 }
252
253 private:
254 std::variant<Exceptions...> m_Variant;
255 mutable bool m_ErrorHandled{ false };
256 };
257 }
258}
259
260#endif
#define CORI_CLEAN_TYPE_NAME(tn)
#define CORI_CORE_ERROR_TAGGED(...)
Definition Logger.hpp:1039
CoriError & operator=(CoriError &&other) noexcept=delete
CoriError(const std::string &message, Args &&... args)
Constructs CoriError object and formats the final error message.
Definition Error.hpp:36
CoriError(CoriError &&other) noexcept
Definition Error.hpp:63
void Ignore() const
Explicitly sets the CoriError object into 'seen' state.
Definition Error.hpp:84
~CoriError() override
Definition Error.hpp:53
CoriError & operator=(const CoriError &other)=delete
std::tuple< Types... > Get() const
The overload of Get method for use with structured bindings.
Definition Error.hpp:111
CoriError(const CoriError &other) noexcept
Definition Error.hpp:59
const char * what() const noexcept override
Returns the formated message and if called the CoriError is considered 'seen'.
Definition Error.hpp:74
T Get() const
Retrieves the value of the type requested from the CoriError object.
Definition Error.hpp:94
PossibleErrors & On(Func &&handler) &
Handles the exception with a callable if it is a specific exception type.
Definition Error.hpp:219
PossibleErrors(PossibleErrors &&other) noexcept
Definition Error.hpp:173
PossibleErrors && On(Func &&handler) &&
Handles the exception with a callable if it is a specific exception type.
Definition Error.hpp:235
const char * GetWhat() const
Simply returns the formated error message held inside the execution object.
Definition Error.hpp:249
PossibleErrors(const PossibleErrors &other) noexcept
Definition Error.hpp:169
void Visit(Visitor &&visitor) const &&
Visits the variant inside of the object with a callable.
Definition Error.hpp:207
const std::exception * GetRaw() const
Returns a pointer to the exception held inside of the object.
Definition Error.hpp:184
void Visit(Visitor &&visitor) const &
Visits the variant inside of the object with a callable.
Definition Error.hpp:197
PossibleErrors & operator=(PossibleErrors &&other) noexcept=delete
PossibleErrors & operator=(const PossibleErrors &other)=delete
All types from Types pack are std::exception or derived from it.
Definition Error.hpp:12
Checks if Types pack has duplicated types.
Checks if T is present in Types pack.
Core systems of the engine are here.
Global engine namespace.
static constexpr char UnusedError[]
Definition Logger.hpp:156