1#pragma once
2#include <cstdint>
3#include <climits>
4#include <type_traits>
5
6namespace nn {
7
8 namespace result::detail {
9
10 class ResultTraits {
11 public:
12 using BaseType = std::uint32_t;
13 static const BaseType SuccessValue = BaseType();
14 static const int ModuleBits = 9;
15 static const int DescriptionBits = 13;
16 static const int ReservedBits = 10;
17 static_assert(ModuleBits + DescriptionBits + ReservedBits == sizeof(BaseType) * CHAR_BIT, "ModuleBits + DescriptionBits + ReservedBits == sizeof(BaseType) * CHAR_BIT");
18 private:
19 static BaseType GetBitsValue(BaseType v, int ofs, int num) noexcept {
20 return (v >> ofs) & ~(~BaseType() << num);
21 }
22 public:
23 template<int M, int D>
24 struct MakeInnerValueStatic : public std::integral_constant<BaseType, (M) | (D << ModuleBits)> {
25 static_assert(M < (1 << ModuleBits), "Invalid Module");
26 static_assert(D < (1 << DescriptionBits), "Invalid Description");
27 };
28
29 static BaseType MakeInnerValue(int m, int d) noexcept {
30 return (static_cast<BaseType>(m) << 0) | (static_cast<BaseType>(d) << ModuleBits);
31 }
32
33 static BaseType GetModuleFromValue(BaseType value) noexcept {
34 return GetBitsValue(v: value, ofs: 0, num: ModuleBits);
35 }
36
37 static BaseType GetDescriptionFromValue(BaseType value) noexcept {
38 return GetBitsValue(v: value, ofs: ModuleBits, num: DescriptionBits);
39 }
40 };
41
42 /* Use CRTP for Results. */
43 template<typename Self>
44 class ResultBase {
45 public:
46 using BaseType = typename ResultTraits::BaseType;
47 static const BaseType SuccessValue = ResultTraits::SuccessValue;
48
49 int GetModule() const noexcept {
50 return static_cast<int>(ResultTraits::GetModuleFromValue(value: static_cast<const Self &>(*this).GetInnerValueForDebug()));
51 }
52
53 int GetDescription() const noexcept {
54 return static_cast<int>(ResultTraits::GetDescriptionFromValue(value: static_cast<const Self &>(*this).GetInnerValueForDebug()));
55 }
56 };
57
58 class ResultInternalAccessor;
59
60 }
61
62 class ResultSuccess;
63
64 class Result : public result::detail::ResultBase<Result> {
65 friend class result::detail::ResultInternalAccessor;
66 private:
67 using Base = typename result::detail::ResultBase<Result>;
68 private:
69 BaseType m_value;
70 private:
71 explicit Result(BaseType v) noexcept : m_value(v) { /* ... */ }
72 public:
73 Result() noexcept { /* ... */ }
74
75 BaseType GetInnerValueForDebug() const noexcept { return m_value; }
76
77 bool IsSuccess() const noexcept { return m_value == SuccessValue; }
78 bool IsFailure() const noexcept { return !IsSuccess(); }
79
80 operator ResultSuccess() const noexcept;
81 static bool CanAccept(Result result) noexcept;
82
83 int GetModule() const noexcept { return Base::GetModule(); }
84 int GetDescription() const noexcept { return Base::GetDescription(); }
85 };
86 static_assert(sizeof(Result) == sizeof(result::detail::ResultTraits::BaseType), "sizeof(Result) == sizeof(detail::ResultTraits::BaseType)");
87 static_assert(std::is_trivially_destructible<Result>::value, "std::is_trivially_destructible<Result>::value");
88
89 inline bool Result::CanAccept(Result result) noexcept {
90 (void)(result);
91 return true;
92 }
93
94 namespace result::detail {
95
96 class ResultInternalAccessor {
97 public:
98 static Result ConstructResult(ResultTraits::BaseType v) noexcept { return Result(v); }
99 };
100
101 inline Result ConstructResult(ResultTraits::BaseType v) noexcept { return ResultInternalAccessor::ConstructResult(v); }
102
103 }
104
105 class ResultSuccess : public result::detail::ResultBase<ResultSuccess> {
106 public:
107 using Base = typename result::detail::ResultBase<ResultSuccess>;
108 public:
109 operator Result() const noexcept { return result::detail::ConstructResult(v: SuccessValue); }
110
111 BaseType GetInnerValueForDebug() const noexcept { return SuccessValue; }
112
113 bool IsSuccess() const noexcept { return true; }
114
115 static bool CanAccept(Result result) noexcept { return result.IsSuccess(); }
116
117 int GetModule() const noexcept { return Base::GetModule(); }
118 int GetDescription() const noexcept { return Base::GetDescription(); }
119 };
120
121 namespace result::detail {
122
123 __attribute__((noreturn)) void OnUnhandledResult(Result result) noexcept;
124
125 }
126
127 inline Result::operator ResultSuccess() const noexcept {
128 if (!ResultSuccess::CanAccept(result: *this)) {
129 result::detail::OnUnhandledResult(result: *this);
130 }
131 return ResultSuccess();
132 }
133
134 namespace result::detail {
135
136 template<int Module_, int Description_>
137 class ErrorResultBase : public ResultBase<ErrorResultBase<Module_, Description_>> {
138 private:
139 using Base = ResultBase<ErrorResultBase<Module_, Description_>>;
140 public:
141 static constexpr int Module = Module_;
142 static constexpr int Description = Description_;
143 static constexpr typename Base::BaseType InnerValue = ResultTraits::MakeInnerValueStatic<Module, Description>::value;
144 static_assert(InnerValue != Base::SuccessValue, "InnerValue != Base::SuccessValue");
145 public:
146 operator Result() const noexcept { return ConstructResult(InnerValue); }
147 operator ResultSuccess() const noexcept { OnUnhandledResult(*this); }
148
149 bool IsSuccess() const noexcept { return false; }
150
151 typename Base::BaseType GetInnerValueForDebug() const noexcept { return InnerValue; }
152 };
153
154 template<int Module_, int DescStart, int DescEnd>
155 class ErrorRange {
156 public:
157 static const int Module = Module_;
158 static const int DescriptionStart = DescStart;
159 static const int DescriptionEnd = DescEnd;
160 static_assert(DescriptionStart < DescriptionEnd, "DescriptionStart < DescriptionEnd");
161 public:
162 static bool Includes(Result result) noexcept {
163 return result.GetModule() == Module && DescriptionStart <= result.GetDescription() && result.GetDescription() < DescriptionEnd;
164 }
165
166 friend bool operator <=(Result result, ErrorRange) noexcept {
167 return Includes(result);
168 }
169 };
170
171 }
172
173}
174
175/* Macros for defining new results. */
176#define R_DEFINE_NAMESPACE_RESULT_MODULE(value) namespace detail::result { using ResultModuleId = std::integral_constant<int, value>; }
177#define R_CURRENT_NAMESPACE_RESULT_MODULE detail::result::ResultModuleId::value
178#define R_NAMESPACE_MODULE_ID(nmspc) nmspc::R_CURRENT_NAMESPACE_RESULT_MODULE
179
180#define R_DEFINE_ERROR_RESULT_IMPL(name, desc_start, desc_end) \
181 class Result##name : \
182 public ::nn::result::detail::ErrorResultBase<R_CURRENT_NAMESPACE_RESULT_MODULE, desc_start>, \
183 public ::nn::result::detail::ErrorRange<R_CURRENT_NAMESPACE_RESULT_MODULE, desc_start, desc_end + 1> \
184 {}
185
186#define R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, desc_start, desc_end) \
187 class Result##name : \
188 public ::nn::result::detail::ErrorRange<R_CURRENT_NAMESPACE_RESULT_MODULE, desc_start, desc_end + 1> \
189 {}
190
191
192#define R_DEFINE_ERROR_RESULT(name, desc) R_DEFINE_ERROR_RESULT_IMPL(name, desc, desc)
193#define R_DEFINE_ERROR_RANGE(name, start, end) R_DEFINE_ERROR_RESULT_IMPL(name, start, end)
194
195#define R_DEFINE_ABSTRACT_ERROR_RESULT(name, desc) R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, desc, desc)
196#define R_DEFINE_ABSTRACT_ERROR_RANGE(name, start, end) R_DEFINE_ABSTRACT_ERROR_RESULT_IMPL(name, start, end)
197