-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathincerr.hpp
More file actions
191 lines (160 loc) · 8.11 KB
/
Copy pathincerr.hpp
File metadata and controls
191 lines (160 loc) · 8.11 KB
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
#ifndef INCOM_INCERR_HPP
#define INCOM_INCERR_HPP
#include <string>
#include <string_view>
#include <system_error>
#include <type_traits>
#include <utility>
#include <magic_enum/magic_enum.hpp>
namespace incom {
namespace error {
namespace detail {
// The user MUST 'register' the enum types used in the library by running INCERR_REGISTER(TYPE_FULLY_QUALIFIED,
// NAMESPACE_FULLY_QUALIFIED) macro (defined at the of incerr.hpp)
template <typename T>
concept enum_isRegistered = requires(T t) {
{ make_error_code(t) } -> std::same_as<std::error_code>;
{ make_error_condition(t) } -> std::same_as<std::error_condition>;
};
template <typename T>
concept enum_hasMsgDispatch = requires(T t) {
{ incerr_msg_dispatch(std::move(t)) } -> std::same_as<std::string_view>;
};
template <typename T>
concept enum_hasNameDispatch = requires(T t) {
{ incerr_name_dispatch(std::move(t)) } -> std::same_as<std::string_view>;
};
template <typename ENUM_T, int VAL>
struct enum_hasNoValX {
static const bool value = (not magic_enum::enum_cast<ENUM_T>(VAL).has_value());
};
// The scoped enums used as template arguments in incerr library MUST NOT use the constant '0' as one of the
// explicitly named contants (so called 'enumerators')
// The fix would typically be to define the first enumerator as having constant value '1'
// This small limitation ensures better compatibility with the standard library
template <typename ENUM_T>
concept enum_hasNoZeroValue_v = enum_hasNoValX<ENUM_T, 0>::value;
template <typename E>
requires std::is_scoped_enum_v<E>
class incerr_cat : public std::error_category {
private:
incerr_cat() = default;
virtual const char *name() const noexcept override {
static const std::string s{__internal_name_dispatch()};
return s.c_str();
}
virtual std::string message(int ev) const override { return __internal_msg_dispatch(ev); }
// template <typename EE = E>
// requires enum_hasNameDispatch<EE>
// std::string __internal_name_dispatch() const {
// static constexpr const EE instance{};
// return std::string{incerr_name_dispatch(instance)};
// }
template <typename EE = E>
std::string __internal_name_dispatch() const {
return std::string{magic_enum::enum_type_name<EE>()};
}
template <typename EE = E>
requires enum_hasMsgDispatch<EE>
std::string __internal_msg_dispatch(const int ev) const {
return std::string(magic_enum::enum_name<EE>(magic_enum::enum_cast<EE>(ev).value()))
.append("\n")
.append(incerr_msg_dispatch(EE{ev}));
}
template <typename EE = E>
std::string __internal_msg_dispatch(const int ev) const {
return std::string(magic_enum::enum_name<EE>(magic_enum::enum_cast<EE>(ev).value()));
}
public:
// Meyers' Singleton technique to guarantee only 1 instance is ever created
static const incerr_cat<E> &getSingleton() {
static const incerr_cat<E> instance;
return instance;
}
};
} // namespace detail
class incerr_code : public std::error_code {
private:
std::string customMessage;
public:
// MAIN INTERFACE METHODS
template <typename E>
requires std::is_scoped_enum_v<E> && std::is_error_code_enum<E>::value && detail::enum_isRegistered<E>
static incerr_code make(E e) {
return incerr_code(std::to_underlying(e), error::detail::incerr_cat<E>::getSingleton());
}
template <typename E, typename S>
requires std::is_scoped_enum_v<E> && std::is_error_code_enum<E>::value && detail::enum_isRegistered<E> &&
std::is_convertible_v<S, std::string_view>
static incerr_code make(E e, S const &&customMessage) {
return incerr_code(std::to_underlying(e), error::detail::incerr_cat<E>::getSingleton(),
std::forward<decltype(customMessage)>(customMessage));
}
// If one prefers to use the 'std::error_code' type directly and not the extended type 'incerr_code'
// By doing so one gets all the benefits of this library except the possibility to create
// 'customMessage' at the call site (and later show it)
template <typename E>
requires std::is_scoped_enum_v<E> && std::is_error_code_enum<E>::value && detail::enum_isRegistered<E>
static inline const std::error_code make_std_ec(E e) {
return std::error_code(std::to_underlying(e), error::detail::incerr_cat<E>::getSingleton());
}
const std::string_view get_customMessage() const { return std::string_view{customMessage}; }
// CONSTRUCTION
incerr_code() = delete;
incerr_code(incerr_code &&src) = default; // move constructor
// copy constructor
incerr_code(const incerr_code &src) = default;
// copy assignment
incerr_code &operator=(const incerr_code &src) { return *this = incerr_code(src); }
// move assignment
incerr_code &operator=(incerr_code &&src) noexcept {
std::swap(customMessage, src.customMessage);
return *this;
}
// DESTRUCTION
~incerr_code() = default;
private:
template <typename E>
requires std::is_scoped_enum_v<E> && std::is_error_code_enum<E>::value && detail::enum_isRegistered<E>
incerr_code(E __e) {
*this = make(__e);
}
incerr_code(int ec, const std::error_category &cat) noexcept : std::error_code(ec, cat), customMessage{""} {}
template <typename SV>
requires std::is_convertible_v<SV, std::string_view>
incerr_code(int ec, const std::error_category &cat, SV const &&sv) noexcept
: std::error_code(ec, cat), customMessage{sv} {}
};
} // namespace error
} // namespace incom
#ifndef INCOM_INCERR_NAMESPACE_ALIAS
#define INCOM_INCERR_NAMESPACE_ALIAS
namespace incerr = incom::error;
#endif
#endif // INCOM_INCERR_HPP
// Macro section intentionally outside include guard.
// This allows re-including incerr.hpp to restore macro after #undef.
// The user MUST 'register' the enum types used in the library. Constraints violation on incerr_code static methods will
// ensue during compilation otherwise.
// This MACRO needs to be called from global namespace, but it only inserts specialization of is_error_code_enum into
// std namespace and two free function definitions into the same namespace as the ENUM type.
// Either do this by using this macro (which can be 'undefed' once not needed ... typically at the end of the file with
// the enums definitions). Or just do the thing the macro does manually
// Note: The author is not aware of a good way to (easily) do this without a MACRO
#ifndef INCERR_REGISTER
#define INCERR_REGISTER(TYPE_FULLY_QUALIFIED, NAMESPACE_FULLY_QUALIFIED) \
template <> \
struct std::is_error_code_enum<TYPE_FULLY_QUALIFIED> : public true_type {}; \
\
namespace NAMESPACE_FULLY_QUALIFIED { \
inline std::error_code make_error_code(TYPE_FULLY_QUALIFIED e) { \
return std::error_code(static_cast<int>(e), \
incom::error::detail::incerr_cat<TYPE_FULLY_QUALIFIED>::getSingleton()); \
} \
\
inline std::error_condition make_error_condition(TYPE_FULLY_QUALIFIED e) { \
return std::error_condition(static_cast<int>(e), \
incom::error::detail::incerr_cat<TYPE_FULLY_QUALIFIED>::getSingleton()); \
} \
}
#endif