MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
result.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 Matthew Tolman
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17*/
18
19#pragma once
20
21#ifndef MTCORE_RESULT_H
22#define MTCORE_RESULT_H
23
24#include "../core.hpp"
25#include "../traits.hpp"
26#include "optional.hpp"
27#include <memory>
28#include <ostream>
29#include <variant>
30
35
37namespace mtcore {
43 template<typename Underlying>
44 struct Error;
45
52 template<typename T, typename ErrType>
53 struct Result;
54
61 template<typename Underlying>
62 struct Error {
63 using Type = Underlying;
64 Underlying code;
65
71 template<typename T>
72 operator Result<T, Underlying>() const noexcept;
73
79 bool operator==(const Error &o) const noexcept { return code == o.code; }
80 bool operator!=(const Error &o) const noexcept { return code != o.code; }
81 };
82
87 template<typename ErrType>
88 struct Result<void, ErrType> {
89 using Err = ErrType;
91 std::variant<std::monostate, ErrVal> val;
92
98 Result() : val(std::monostate{}) {}
99
105 static Result from_error(ErrVal err) { return Result(err, 0); }
106
111 static Result from_success() { return Result(); }
112
118 bool operator==(const Result &other) const noexcept {
119 if (std::holds_alternative<Err>(val) && std::holds_alternative<Err>(other.val)) {
120 return std::get<Err>(val) == std::get<Err>(other.val);
121 }
122 else if (std::holds_alternative<Err>(val) || std::holds_alternative<Err>(other.val)) {
123 return false;
124 }
125 return true;
126 }
127
128 bool operator!=(const Result &other) const noexcept { return !(*this == other); }
129
133 [[nodiscard]] bool is_error() const noexcept { return std::holds_alternative<ErrVal>(val); }
134
138 [[nodiscard]] bool is_success() const noexcept { return std::holds_alternative<std::monostate>(val); }
139
144 [[nodiscard]] ErrVal error() const noexcept {
145 ensure(is_error(), "CANNOT GET AN ERROR FROM A SUCCESSFUL RESUTL!");
146 return std::get<ErrVal>(val);
147 }
148
149 Result to_void() const { return *this; }
150
151 template<typename R>
153 if (is_error()) {
154 return error();
155 }
156 return s;
157 }
158
159 private:
160 Result(ErrVal err, size_t) : val(err) {}
161 };
162
169 template<typename T, typename ErrType>
170 struct Result {
171 using Err = ErrType;
173 using Value = T;
174
175 private:
176 std::variant<std::monostate, T, ErrVal> val;
177
178 public:
185 Result(const T &val) : val(val) {}
186
193 Result(T &&val) : val(std::forward<T>(val)) {}
194
200 static Result from_error(const ErrVal &err) { return Result(err, 0); }
201
207 static Result from_success(const T &val) { return Result(val); }
208
214 bool operator==(const Result &other) const noexcept {
215 if (is_error()) {
216 if (other.is_error()) {
217 return error() == other.error();
218 }
219 return false;
220 }
221 if (other.is_error()) {
222 return false;
223 }
224
225 if (is_success()) {
226 if (other.is_success()) {
227 return value() == other.value();
228 }
229 return false;
230 }
231 return false;
232 }
233
234 bool operator!=(const Result &other) const noexcept { return !(*this == other); }
235
241 bool operator==(const T &other) const noexcept {
242 if (is_error()) {
243 return false;
244 }
245 return value() == other;
246 }
247
248 bool operator!=(const T &other) const noexcept { return !(*this == other); }
249
253 [[nodiscard]] bool is_success() const noexcept { return std::holds_alternative<T>(val); }
254
258 [[nodiscard]] T value() const noexcept {
259 ensure(is_success(), "CANNOT GET VALUE WHEN ONE IS NOT PRESENT");
260 return std::get<T>(val);
261 }
262
266 [[nodiscard]] bool is_error() const noexcept { return std::holds_alternative<ErrVal>(val); }
267
271 [[nodiscard]] bool copy_if_present(std::remove_const_t<T> &out) const noexcept {
272 if (is_success()) {
273 out = value();
274 }
275 return is_success();
276 }
277
281 [[nodiscard]] bool move_if_present(T &out) noexcept {
282 if (is_success()) {
283 out = std::move(std::get<T>(val));
284 val = std::monostate{};
285 return true;
286 }
287 return is_success();
288 }
289
294 [[nodiscard]] ErrVal error() const noexcept {
295 ensure(is_error(), "CANNOT GET AN ERROR FROM A SUCCESSFUL RESUTL!");
296 return std::get<ErrVal>(val);
297 }
298
302 const T &operator*() const noexcept {
303 ensure(is_success(), "CANNNOT DERERFERENCE ERROR RESULT");
304 return std::get<T>(val);
305 }
306
310 T const *operator->() const noexcept {
311 ensure(is_success(), "CANNNOT DERERFERENCE ERROR RESULT");
312 return &std::get<T>(val);
313 }
314
319 ensure(is_success(), "CANNNOT DERERFERENCE ERROR RESULT");
320 return std::get<T>(val);
321 }
322
327 ensure(is_success(), "CANNNOT DERERFERENCE ERROR RESULT");
328 return &std::get<T>(val);
329 }
330
332 if (is_error()) {
333 return this->error();
334 }
335 return {};
336 }
337
338 template<typename R>
340 if (is_error()) {
341 return error();
342 }
343 return s;
344 }
345
346 private:
347 Result(const ErrVal &err, size_t) : val(err) {}
348 };
349
355 template<typename T>
356 struct Success;
357
362 template<>
363 struct Success<void> {
364 template<typename Err>
365 operator Result<void, Err>() const noexcept;
366 };
367
373 template<typename T>
374 struct Success {
376
377 template<typename Err>
378 operator Result<T, Err>() const noexcept;
379 };
380
388 template<typename T>
389 Success<T> success(const T &v) {
390 return Success<T>{v};
391 }
392
398 inline Success<void> success() { return {}; }
399
400 template<typename Err>
404
405 template<typename Underlying>
406 template<typename Err>
409 }
410
411 template<typename Underlying>
412 template<typename T>
416
424 template<typename Underlying>
425 Error<Underlying> error(Underlying err) {
426 return Error<Underlying>{err};
427 }
428} // namespace mtcore
429
432
450 template<typename Success, typename Acc, typename F, Iterable I,
451 typename Err = typename decltype(std::declval<F>()(
452 std::declval<std::remove_const_t<typename decltype(std::declval<I>().iter())::IterElem>>()))::Err>
453 Result<Success, Err> accumulate(Success initial, const Acc &acc, const F &func, const I &iterable) {
454 auto iter = iterable.iter();
455 using Elem = std::remove_cv_t<typename decltype(iter)::IterElem>;
456 Elem cur;
457 while (iter.next().move_if_present(cur)) {
458 auto res = func(cur);
459 if (res.is_error()) {
460 return res.error();
461 }
462 initial = acc(initial, res.value());
463 }
464 return success(initial);
465 }
466
480 template<typename F, Iterable I,
481 typename Err = typename decltype(std::declval<F>()(
482 std::declval<std::remove_const_t<typename decltype(std::declval<I>().iter())::IterElem>>()))::Err>
483 Result<void, Err> foreach (const F &func, const I &iterable) {
484 auto iter = iterable.iter();
485 using Elem = std::remove_cv_t<typename decltype(iter)::IterElem>;
486 Elem cur;
487 while (iter.next().move_if_present(cur)) {
488 auto res = func(cur);
489 if (res.is_error()) {
490 return res.error();
491 }
492 }
493 return success();
494 }
495
496 namespace impl {
497 template<typename Err, typename F, typename... Args>
498 struct Chain;
499
500 template<typename V, typename T>
501 struct TupleAppend;
502
503 template<typename Out, typename Err, typename F, typename... Args>
504 struct Map;
505
506 template<typename F, typename Acc, typename... Args>
507 struct Reduce;
508
509 template<typename... Args>
510 struct FirstVariadic;
511
512 template<typename A, typename... Args>
513 struct FirstVariadic<A, Args...> {
514 using Type = A;
515 };
516
517 template<>
518 struct FirstVariadic<> {
519 using Type = void;
520 };
521 } // namespace impl
522
536 template<typename F, typename... Args,
537 typename Err =
538 typename decltype(std::declval<F>()(std::declval<typename impl::FirstVariadic<Args...>::Type>()))::Err>
539 Result<void, Err> chain(const F &func, const Args &...args) {
540 return impl::Chain<Err, F, Args...>::run(func, args...);
541 }
542
561 template<typename Success, typename Acc, typename F, typename... Args,
562 typename Err =
563 typename decltype(std::declval<F>()(std::declval<typename impl::FirstVariadic<Args...>::Type>()))::Err>
564 Result<Success, Err> reduce(Success initVal, const Acc &acc, const F &func, const Args &...args) {
565 return impl::Reduce<F, Acc, Args...>::template run<Success, Err>(initVal, func, acc, args...);
566 }
567
580 template<typename F, typename... Args,
581 typename Out =
582 typename decltype(std::declval<F>()(std::declval<typename impl::FirstVariadic<Args...>::Type>()))::Value,
583 typename Err =
584 typename decltype(std::declval<F>()(std::declval<typename impl::FirstVariadic<Args...>::Type>()))::Err>
585 auto map(const F &func, const Args &...args) {
586 return impl::Map<Out, Err, F, Args...>::run(func, args...);
587 }
588
589 namespace impl {
590 template<typename V, typename... TA>
591 struct TupleAppend<V, std::tuple<TA...>> {
592 using Tuple = std::tuple<TA..., V>;
593
594 static Tuple append(const V &v, const std::tuple<TA...> &tuple) {
595 return append_impl(v, tuple, std::index_sequence_for<TA...>{});
596 }
597
598 template<size_t... Indx>
599 static Tuple append_impl(const V &v, const std::tuple<TA...> &tuple, std::index_sequence<Indx...>) {
600 return {std::get<Indx>(tuple)..., v};
601 }
602 };
603
604 template<typename L, typename R>
605 struct TupleConcat;
606
607 template<typename... LA, typename... RA>
608 struct TupleConcat<std::tuple<LA...>, std::tuple<RA...>> {
609 using Tuple = std::tuple<LA..., RA...>;
610 };
611
612 template<typename Out, typename Err, typename F>
613 struct Map<Out, Err, F> {
614 using Tuple = std::tuple<>;
615
616 static Result<std::tuple<>, Err> run(const F &) { return success(std::tuple{}); }
617 template<typename T>
618 static Result<T, Err> run_impl(const T &tuple, const F &) {
619 return success(tuple);
620 }
621 };
622
623 template<typename Out, typename Err, typename F, typename A, typename... Args>
624 struct Map<Out, Err, F, A, Args...> {
625 using Tuple = typename TupleAppend<Out, typename Map<Out, Err, F, Args...>::Tuple>::Tuple;
626
627 template<typename... TA>
628 static Result<typename TupleConcat<std::tuple<TA...>, Tuple>::Tuple, Err>
629 run_impl(const std::tuple<TA...> &curTuple, const F &func, const A &arg, const Args &...args) {
630 auto curRes = func(arg);
631 if (curRes.is_error()) {
632 return curRes.error();
633 }
634 auto newTuple = TupleAppend<Out, std::tuple<TA...>>::append(curRes.value(), curTuple);
635 return Map<Out, Err, F, Args...>::run_impl(newTuple, func, args...);
636 }
637
638 static Result<Tuple, Err> run(const F &func, const A &arg, const Args &...args) {
639 auto curRes = func(arg);
640 if (curRes.is_error()) {
641 return curRes.error();
642 }
643 return Map<Out, Err, F, Args...>::run_impl(std::tuple<Out>{curRes.value()}, func, args...);
644 }
645 };
646
647 template<typename Err, typename F>
648 struct Chain<Err, F> {
649 static Result<void, Err> run(const F &) { return success(); }
650 };
651
652 template<typename Err, typename F, typename A, typename... Args>
653 struct Chain<Err, F, A, Args...> {
654 static Result<void, Err> run(const F &func, const A &arg, const Args &...args) {
655 if (auto curRes = func(arg); curRes.is_error()) {
656 return curRes.error();
657 }
658 return Chain<Err, F, Args...>::run(func, args...).to_void();
659 }
660 };
661
662 template<typename F, typename Acc>
663 struct Reduce<F, Acc> {
664 template<typename Success, typename Err>
665 static Result<Success, Err> run(Success curVal, const F &, const Acc &) {
666 return success(curVal);
667 }
668 };
669
670 template<typename F, typename Acc, typename A, typename... Args>
671 struct Reduce<F, Acc, A, Args...> {
672 template<typename Success, typename Err>
673 static Result<Success, Err> run(Success curVal, const F &func, const Acc &accumulator, const A &arg,
674 const Args &...args) {
675 auto curRes = func(arg);
676 if (curRes.is_error()) {
677 return curRes.error();
678 }
679 curVal = accumulator(curVal, curRes.value());
680 return Reduce<F, Acc, Args...>::template run<Success, Err>(curVal, func, accumulator, args...);
681 }
682 };
683 } // namespace impl
684} // namespace mtcore::results
685
686#endif // MTCORE_RESULT_H
The Iterable trait for types Something that implements this trait can give an iterator to iterate ove...
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
Success< T > success(const T &v)
Creates a successful Result.
Definition result.hpp:389
Result< Success, Err > accumulate(Success initial, const Acc &acc, const F &func, const I &iterable)
Chains together the execution of an errorable function on a list of arguments If an error is encounte...
Definition result.hpp:453
auto map(const F &func, const Args &...args)
Maps values with function that may error.
Definition result.hpp:585
Success< void > success()
Creates a successful void Result object.
Definition result.hpp:398
Result< Success, Err > reduce(Success initVal, const Acc &acc, const F &func, const Args &...args)
Reduces arguments with errorable function If an error is encountered, execution will stop and immedia...
Definition result.hpp:564
Error< Underlying > error(Underlying err)
Creates an error.
Definition result.hpp:425
Result< void, Err > chain(const F &func, const Args &...args)
Chains together the execution of an errorable function on a list of arguments If an error is encounte...
Definition result.hpp:539
constexpr bool is_result_with_value
Checks if a type is result-like with a success value type.
constexpr bool is_result_void
Checks if a type is result-like with no success value.
Generic iterator defaults built on common contracts Does not guarantee performance of iterators Actua...
Definition iter.hpp:91
Additional algorithms and pipeline functionality that can be performed on groups of results.
Definition result.hpp:436
Core library for C++ with Zig-related functionality.
A struct representing an error Auto convertible to a Result of any type.
Definition result.hpp:62
Underlying Type
Definition result.hpp:63
bool operator!=(const Error &o) const noexcept
Definition result.hpp:80
ErrVal error() const noexcept
Returns the associated error Fails if there is no error;.
Definition result.hpp:144
static Result from_success()
Creates a Result object from a success.
Definition result.hpp:111
bool operator==(const Result &other) const noexcept
Compares results.
Definition result.hpp:118
bool operator!=(const Result &other) const noexcept
Definition result.hpp:128
Result()
Creates a Result object from a success Allows for implicit casting by returning success value.
Definition result.hpp:98
static Result from_error(ErrVal err)
Creates a Result object from an error.
Definition result.hpp:105
Result< R, Err > with_success_val(const R &s)
Definition result.hpp:152
bool is_error() const noexcept
Checks if is an error Result.
Definition result.hpp:133
bool is_success() const noexcept
Checks if is a success Result.
Definition result.hpp:138
std::variant< std::monostate, ErrVal > val
Definition result.hpp:91
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
Definition result.hpp:170
Result(const T &val)
Creates a Result object from a success Allows for implicit casting by returning success value.
Definition result.hpp:185
bool operator==(const T &other) const noexcept
Compares Result to a value.
Definition result.hpp:241
bool operator==(const Result &other) const noexcept
Compares results.
Definition result.hpp:214
bool copy_if_present(std::remove_const_t< T > &out) const noexcept
Copies into a reference the successful value if there is one.
Definition result.hpp:271
T value() const noexcept
Checks if is a successful Result (aka.
Definition result.hpp:258
Result< R, Err > with_success_val(const R &s)
Definition result.hpp:339
Error< Err > ErrVal
Definition result.hpp:172
T const * operator->() const noexcept
Dereference the value (fails if an error value)
Definition result.hpp:310
bool is_success() const noexcept
Checks if is a successful Result (aka.
Definition result.hpp:253
T & operator*()
Dereference the value (fails if an error value)
Definition result.hpp:318
Result(T &&val)
Creates a Result object from a success Allows for implicit casting by returning success value.
Definition result.hpp:193
static Result from_success(const T &val)
Creates a Result object from a success.
Definition result.hpp:207
bool operator!=(const T &other) const noexcept
Definition result.hpp:248
const T & operator*() const noexcept
Dereference the value (fails if an error value)
Definition result.hpp:302
static Result from_error(const ErrVal &err)
Creates a Result object from an error.
Definition result.hpp:200
bool move_if_present(T &out) noexcept
Moves into a reference the successful value if there is one.
Definition result.hpp:281
bool operator!=(const Result &other) const noexcept
Definition result.hpp:234
bool is_error() const noexcept
Checks if is an error Result.
Definition result.hpp:266
Result< void, Err > to_void() const
Definition result.hpp:331
ErrVal error() const noexcept
Returns the associated error Fails if there is no error;.
Definition result.hpp:294
T * operator->()
Dereference the value (fails if an error value)
Definition result.hpp:326
ErrType Err
Definition result.hpp:171
Represents a success result (auto castable to Result)
Definition result.hpp:374