fp
Functional Programming extensions to C++ for ROS projects.
result.hpp
1 // Copyright (c) 2022, Tyler Weaver
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 //
9 // * Redistributions in binary form must reproduce the above copyright
10 // notice, this list of conditions and the following disclaimer in the
11 // documentation and/or other materials provided with the distribution.
12 //
13 // * Neither the name of the copyright holder nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 
29 #pragma once
30 
31 #include <cxxabi.h>
32 #include <fmt/format.h>
33 
34 #include <iostream>
35 #include <map>
36 #include <optional>
37 #include <string>
38 #include <string_view>
39 
40 #include "fp/_external/expected.hpp"
41 #include "fp/no_discard.hpp"
42 
43 namespace fp {
44 
48 enum class ErrorCode : int {
49  UNKNOWN,
50  CANCELLED,
52  TIMEOUT,
53  NOT_FOUND,
58  ABORTED,
61  INTERNAL,
63  DATA_LOSS,
65  EXCEPTION,
66 };
67 
71 struct [[nodiscard]] Error {
73  std::string what = "";
74 
75  inline bool operator==(const Error& other) const noexcept {
76  return code == other.code && what == other.what;
77  }
78  inline bool operator!=(const Error& other) const noexcept {
79  return code != other.code || what != other.what;
80  }
81 };
82 
83 constexpr auto Unknown = [](const std::string& what = "") {
84  return Error{ErrorCode::UNKNOWN, what};
85 };
86 constexpr auto Cancelled = [](const std::string& what = "") {
87  return Error{ErrorCode::CANCELLED, what};
88 };
89 constexpr auto InvalidArgument = [](const std::string& what = "") {
90  return Error{ErrorCode::INVALID_ARGUMENT, what};
91 };
92 constexpr auto Timeout = [](const std::string& what = "") {
93  return Error{ErrorCode::TIMEOUT, what};
94 };
95 constexpr auto NotFound = [](const std::string& what = "") {
96  return Error{ErrorCode::NOT_FOUND, what};
97 };
98 constexpr auto AlreadyExists = [](const std::string& what = "") {
99  return Error{ErrorCode::ALREADY_EXISTS, what};
100 };
101 constexpr auto PermissionDenied = [](const std::string& what = "") {
102  return Error{ErrorCode::PERMISSION_DENIED, what};
103 };
104 constexpr auto ResourceExhausted = [](const std::string& what = "") {
105  return Error{ErrorCode::RESOURCE_EXHAUSTED, what};
106 };
107 constexpr auto FailedPrecondition = [](const std::string& what = "") {
109 };
110 constexpr auto Aborted = [](const std::string& what = "") {
111  return Error{ErrorCode::ABORTED, what};
112 };
113 constexpr auto OutOfRange = [](const std::string& what = "") {
114  return Error{ErrorCode::OUT_OF_RANGE, what};
115 };
116 constexpr auto Unimplemented = [](const std::string& what = "") {
117  return Error{ErrorCode::UNIMPLEMENTED, what};
118 };
119 constexpr auto Internal = [](const std::string& what = "") {
120  return Error{ErrorCode::INTERNAL, what};
121 };
122 constexpr auto Unavailable = [](const std::string& what = "") {
123  return Error{ErrorCode::UNAVAILABLE, what};
124 };
125 constexpr auto DataLoss = [](const std::string& what = "") {
126  return Error{ErrorCode::DATA_LOSS, what};
127 };
128 constexpr auto Unauthenticated = [](const std::string& what = "") {
129  return Error{ErrorCode::UNAUTHENTICATED, what};
130 };
131 constexpr auto Exception = [](const std::string& what = "") {
132  return Error{ErrorCode::EXCEPTION, what};
133 };
134 
140 [[nodiscard]] constexpr std::string_view toStringView(const ErrorCode& code) {
141  switch (code) {
143  return "Cancelled";
144  case ErrorCode::UNKNOWN:
145  return "Unknown";
147  return "InvalidArgument";
148  case ErrorCode::TIMEOUT:
149  return "Timeout";
151  return "NotFound";
153  return "AlreadyExists";
155  return "PermissionDenied";
157  return "ResourceExhausted";
159  return "FailedPrecondition";
160  case ErrorCode::ABORTED:
161  return "Aborted";
163  return "OutOfRange";
165  return "Unimplemented";
166  case ErrorCode::INTERNAL:
167  return "Internal";
169  return "Unavailable";
171  return "DataLoss";
173  return "Unauthenticated";
175  return "Exception";
176  default:
177  __builtin_unreachable();
178  }
179 }
180 
186 template <typename T, typename E = Error>
187 using Result = tl::expected<T, E>;
188 
198 template <typename T, typename E = Error>
199 constexpr Result<T, E> make_result(T value) {
200  return Result<T, E>{value};
201 }
202 
211 template <typename T, typename E>
212 constexpr bool has_error(const tl::expected<T, E>& exp) {
213  return !exp;
214 }
215 
226 template <typename E, typename... Args>
227 constexpr std::optional<E> maybe_error(tl::expected<Args, E>... args) {
228  auto maybe = std::optional<E>{std::nullopt};
229  (
230  [&](auto& exp) {
231  if (maybe.has_value()) return;
232  if (has_error(exp)) maybe = exp.error();
233  }(args),
234  ...);
235  return maybe;
236 }
237 
250 template <typename F, typename Ret = typename std::result_of<F()>::type,
251  typename Exp = Result<Ret>>
252 Exp try_to_result(F f) {
253  try {
254  return make_result(f());
255  } catch (const std::exception& ex) {
256  return tl::make_unexpected(Exception(fmt::format(
257  "[{}: {}]", abi::__cxa_current_exception_type()->name(), ex.what())));
258  }
259 }
260 
261 } // namespace fp
262 
266 template <>
267 struct fmt::formatter<fp::Error> {
268  template <typename ParseContext>
269  constexpr auto parse(ParseContext& ctx) {
270  return ctx.begin();
271  }
272 
273  template <typename FormatContext>
274  auto format(const fp::Error& error, FormatContext& ctx) {
275  return format_to(ctx.out(), "[Error: [{}] {}]", toStringView(error.code),
276  error.what);
277  }
278 };
279 
283 template <typename T>
284 struct fmt::formatter<fp::Result<T>> {
285  template <typename ParseContext>
286  constexpr auto parse(ParseContext& ctx) {
287  return ctx.begin();
288  }
289 
290  template <typename FormatContext>
291  auto format(const fp::Result<T>& result, FormatContext& ctx) {
292  if (result.has_value()) {
293  return format_to(ctx.out(), "[Result<T>: value={}]", result.value());
294  } else {
295  return format_to(ctx.out(), "[Result<T>: {}]", result.error());
296  }
297  }
298 };
fp::Timeout
constexpr auto Timeout
Definition: result.hpp:92
fp::make_result
constexpr Result< T, E > make_result(T value)
Makes a Result<T> from a T value.
Definition: result.hpp:199
fp::ErrorCode::UNIMPLEMENTED
@ UNIMPLEMENTED
fp::Exception
constexpr auto Exception
Definition: result.hpp:131
fp::NotFound
constexpr auto NotFound
Definition: result.hpp:95
fp::Error::operator!=
bool operator!=(const Error &other) const noexcept
Definition: result.hpp:78
fp::maybe_error
constexpr std::optional< E > maybe_error(tl::expected< Args, E >... args)
Definition: result.hpp:227
fp::ErrorCode
ErrorCode
Enum for ErrorCodes inspired by absl::StatusCode.
Definition: result.hpp:48
fp::Error::operator==
bool operator==(const Error &other) const noexcept
Definition: result.hpp:75
fp::ErrorCode::UNAUTHENTICATED
@ UNAUTHENTICATED
fp::ErrorCode::PERMISSION_DENIED
@ PERMISSION_DENIED
fp::Error::what
std::string what
Definition: result.hpp:73
fp::AlreadyExists
constexpr auto AlreadyExists
Definition: result.hpp:98
fp::ErrorCode::UNKNOWN
@ UNKNOWN
fp::Internal
constexpr auto Internal
Definition: result.hpp:119
fp::toStringView
constexpr std::string_view toStringView(const ErrorCode &code)
convert ErrorCode to string_view for easy formatting
Definition: result.hpp:140
fp::ErrorCode::NOT_FOUND
@ NOT_FOUND
fp::ErrorCode::INVALID_ARGUMENT
@ INVALID_ARGUMENT
fp::ErrorCode::CANCELLED
@ CANCELLED
fp::DataLoss
constexpr auto DataLoss
Definition: result.hpp:125
fp::ErrorCode::ALREADY_EXISTS
@ ALREADY_EXISTS
fp::Error::code
ErrorCode code
Definition: result.hpp:72
fp::PermissionDenied
constexpr auto PermissionDenied
Definition: result.hpp:101
fp::OutOfRange
constexpr auto OutOfRange
Definition: result.hpp:113
fp::ResourceExhausted
constexpr auto ResourceExhausted
Definition: result.hpp:104
fp::ErrorCode::DATA_LOSS
@ DATA_LOSS
fp::FailedPrecondition
constexpr auto FailedPrecondition
Definition: result.hpp:107
fp::ErrorCode::INTERNAL
@ INTERNAL
fp::ErrorCode::UNAVAILABLE
@ UNAVAILABLE
fp::Unimplemented
constexpr auto Unimplemented
Definition: result.hpp:116
fp
Definition: monad.hpp:35
fp::Cancelled
constexpr auto Cancelled
Definition: result.hpp:86
fp::ErrorCode::RESOURCE_EXHAUSTED
@ RESOURCE_EXHAUSTED
fp::Unauthenticated
constexpr auto Unauthenticated
Definition: result.hpp:128
fp::ErrorCode::OUT_OF_RANGE
@ OUT_OF_RANGE
fp::Error
Error type used by Result<T>
Definition: result.hpp:71
fp::ErrorCode::FAILED_PRECONDITION
@ FAILED_PRECONDITION
fp::Aborted
constexpr auto Aborted
Definition: result.hpp:110
fp::Unknown
constexpr auto Unknown
Definition: result.hpp:83
fp::InvalidArgument
constexpr auto InvalidArgument
Definition: result.hpp:89
fp::Result
tl::expected< T, E > Result
Definition: result.hpp:187
fp::Unavailable
constexpr auto Unavailable
Definition: result.hpp:122
fp::ErrorCode::EXCEPTION
@ EXCEPTION
fp::try_to_result
Exp try_to_result(F f)
Try to Result<T>. Lifts a function that throws an excpetpion to one that returns a Result<T>
Definition: result.hpp:252
fp::ErrorCode::TIMEOUT
@ TIMEOUT
fp::has_error
constexpr bool has_error(const tl::expected< T, E > &exp)
Filter function for testing if a result has an error.
Definition: result.hpp:212
fp::ErrorCode::ABORTED
@ ABORTED