Skip to content

Commit

Permalink
add into macros
Browse files Browse the repository at this point in the history
  • Loading branch information
altalk23 committed Dec 6, 2024
1 parent 7703c90 commit 64792cc
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 107 deletions.
68 changes: 55 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,50 @@ Here are the convenience utils for entering into an if block with the underlying
int main() {
// Only enters the block if the result is ok,
// setting the value into the variable
// Requires ok value to be default constructible
if (GEODE_UNWRAP_IF_OK(p3, integerDivision(3, 2))) {
assert(p3 == 1);
}

int p4 = 0;
if (GEODE_UNWRAP_INTO_IF_OK(p4, integerDivision(3, 2))) {
assert(p4 == 1);
}

// Only enters the block if the result is an error,
// setting the value into the variable
// Requires err value to be default constructible
if (GEODE_UNWRAP_IF_ERR(e1, integerDivision(3, 0))) {
assert(e1 == "Division by zero");
}

std::string e2;
if (GEODE_UNWRAP_INTO_IF_ERR(e2, integerDivision(3, 0))) {
assert(e2 == "Division by zero");
}

// Enters the first block if the result is ok,
// otherwise enters the second block
// Requires both ok and err values to be default constructible
if (GEODE_UNWRAP_EITHER(p5, err, integerDivision(3, 2))) {
assert(p5 == 1);
} else {
assert(false);
}

if (GEODE_UNWRAP_EITHER(p6, err, integerDivision(3, 0))) {
assert(false);
} else {
assert(err == "Division by zero");
}

int p7 = 0;
std::string e3;
if (GEODE_UNWRAP_INTO_EITHER(p7, e3, integerDivision(3, 2))) {
assert(p7 == 1);
} else {
assert(false);
}
}
```

Expand All @@ -75,15 +110,22 @@ Here are the convenience utils for setting a value inline with manually handling
int main() {
// Enters the trailing block if the result is an error,
// otherwise sets the value into the variable
GEODE_UNWRAP_OR_ELSE(p4, err, integerDivision(3, 2)) {
// Requires both ok and err values to be default constructible
GEODE_UNWRAP_OR_ELSE(p8, err, integerDivision(3, 2)) {
return -1;
}
assert(p4 == 1);
assert(p8 == 1);

GEODE_UNWRAP_OR_ELSE(p5, err, integerDivision(3, 0)) {
p5 = -1;
GEODE_UNWRAP_OR_ELSE(p9, err, integerDivision(3, 0)) {
p9 = -1;
}
assert(p9 == -1);

int p10 = 0;
GEODE_UNWRAP_INTO_OR_ELSE(p10, err, integerDivision(3, 2)) {
return -1;
}
assert(p5 == -1);
assert(p10 == 1);
}
```

Expand All @@ -93,26 +135,26 @@ And here are the functions built into the Result to extract the value:
int main() {
// Returns the value if the result is ok,
// otherwise returns the default value for type
int p6 = integerDivision(3, 0).unwrapOrDefault();
assert(p6 == 0);
int p11 = integerDivision(3, 0).unwrapOrDefault();
assert(p11 == 0);

// Returns the value if the result is ok,
// otherwise returns the passed value
int p7 = integerDivision(3, 0).unwrapOr(-1);
assert(p7 == -1);
int p12 = integerDivision(3, 0).unwrapOr(-1);
assert(p12 == -1);

// Returns the value if the result is ok,
// otherwise returns the result of the operation
int p8 = integerDivision(3, 0).unwrapOrElse([](){
int p13 = integerDivision(3, 0).unwrapOrElse([](){
return -1;
});
assert(p7 == -1);
assert(p13 == -1);

// NOT RECOMMENDED!!!
// Returns the value if the result is ok,
// otherwise **throws an exception**
int p9 = integerDivision(3, 2).unwrap();
std::string e2 = integerDivision(3, 0).unwrapErr();
int p14 = integerDivision(3, 2).unwrap();
std::string e4 = integerDivision(3, 0).unwrapErr();
}
```

Expand Down
221 changes: 127 additions & 94 deletions include/Geode/Result.hpp
Original file line number Diff line number Diff line change
@@ -1,99 +1,132 @@
#pragma once
#ifndef GEODE_RESULT_HPP
#define GEODE_RESULT_HPP

#include <concepts>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>

#if !defined(GEODE_CONCAT)
#define GEODE_CONCAT2(x, y) x##y
#define GEODE_CONCAT(x, y) GEODE_CONCAT2(x, y)
#endif

#if !defined(GEODE_UNWRAP)
// Use gcc's scope expression feature, which makes this macro
// really nice to use. Unfortunately not available on MSVC
#if defined(__GNUC__) || defined(__clang__)
#define GEODE_UNWRAP(...) \
({ \
auto GEODE_CONCAT(res, __LINE__) = __VA_ARGS__; \
if (GEODE_CONCAT(res, __LINE__).isErr()) \
return geode::Err(std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr()); \
std::move(GEODE_CONCAT(res, __LINE__)).unwrap(); \
})
#else
#define GEODE_UNWRAP(...) \
if (auto res = __VA_ARGS__; res.isErr()) return geode::Err(res.unwrapErr())
#endif
#endif

#if !defined(GEODE_UNWRAP_INTO)
#define GEODE_UNWRAP_INTO(variable, ...) \
auto GEODE_CONCAT(res, __LINE__) = __VA_ARGS__; \
if (GEODE_CONCAT(res, __LINE__).isErr()) \
return geode::Err(std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr()); \
variable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap()
#endif

#if !defined(GEODE_UNWRAP_IF_OK)
#define GEODE_UNWRAP_IF_OK(variable, ...) \
auto [variable, GEODE_CONCAT(res, __LINE__)] = std::make_pair( \
geode::impl::ResultOkType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, \
(__VA_ARGS__) \
); \
GEODE_CONCAT(res, __LINE__).isOk() && \
(variable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap(), true)
#endif

#if !defined(GEODE_UNWRAP_IF_ERR)
#define GEODE_UNWRAP_IF_ERR(variable, ...) \
auto [variable, GEODE_CONCAT(res, __LINE__)] = std::make_pair( \
geode::impl::ResultErrType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, \
(__VA_ARGS__) \
); \
GEODE_CONCAT(res, __LINE__).isErr() && \
(variable = std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr(), true)
#endif

#if !defined(GEODE_UNWRAP_IF_SOME)
#define GEODE_UNWRAP_IF_SOME(variable, ...) \
auto [variable, GEODE_CONCAT(res, __LINE__)] = std::make_pair( \
geode::impl::OptionalType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, \
(__VA_ARGS__) \
); \
GEODE_CONCAT(res, __LINE__).has_value() && \
(variable = std::move(GEODE_CONCAT(res, __LINE__)).value(), true)
#endif

#if !defined(GEODE_UNWRAP_OR_ELSE)
#define GEODE_UNWRAP_OR_ELSE(okVariable, errVariable, ...) \
geode::impl::ResultOkType<std::remove_cvref_t<decltype(__VA_ARGS__)>> okVariable; \
auto GEODE_CONCAT(res, __LINE__) = __VA_ARGS__; \
if (geode::impl::ResultErrType<std::remove_cvref_t<decltype(__VA_ARGS__)>> errVariable; \
GEODE_CONCAT(res, __LINE__).isErr() && \
(errVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr(), true) || \
(okVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap(), false))

#endif

#if !defined(GEODE_UNWRAP_EITHER)
#define GEODE_UNWRAP_EITHER(okVariable, errVariable, ...) \
auto [okVariable, errVariable, GEODE_CONCAT(res, __LINE__)] = std::make_tuple( \
geode::impl::ResultOkType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, \
geode::impl::ResultErrType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, \
(__VA_ARGS__) \
); \
GEODE_CONCAT(res, __LINE__).isOk() && \
(okVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap(), true) || \
(errVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr(), false)
#define GEODE_RESULT_HPP

#include <concepts>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>

#if !defined(GEODE_CONCAT)
#define GEODE_CONCAT2(x, y) x##y
#define GEODE_CONCAT(x, y) GEODE_CONCAT2(x, y)
#endif

#if !defined(GEODE_UNWRAP)
// Use gcc's scope expression feature, which makes this macro
// really nice to use. Unfortunately not available on MSVC
#if defined(__GNUC__) || defined(__clang__)
#define GEODE_UNWRAP(...) \
({ \
auto GEODE_CONCAT(res, __LINE__) = __VA_ARGS__; \
if (GEODE_CONCAT(res, __LINE__).isErr()) \
return geode::Err(std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr()); \
std::move(GEODE_CONCAT(res, __LINE__)).unwrap(); \
})
#else
#define GEODE_UNWRAP(...) \
if (auto res = __VA_ARGS__; res.isErr()) return geode::Err(std::move(res).unwrapErr())
#endif
#endif

#if !defined(GEODE_UNWRAP_INTO)
#define GEODE_UNWRAP_INTO(variable, ...) \
auto GEODE_CONCAT(res, __LINE__) = __VA_ARGS__; \
if (GEODE_CONCAT(res, __LINE__).isErr()) \
return geode::Err(std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr()); \
variable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap()
#endif

#if !defined(GEODE_UNWRAP_IF_OK)
#define GEODE_UNWRAP_IF_OK(variable, ...) \
auto [variable, GEODE_CONCAT(res, __LINE__)] = std::make_pair( \
geode::impl::ResultOkType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, (__VA_ARGS__) \
); \
GEODE_CONCAT(res, __LINE__).isOk() && \
(variable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap(), true)
#endif

#if !defined(GEODE_UNWRAP_INTO_IF_OK)
#define GEODE_UNWRAP_INTO_IF_OK(variable, ...) \
auto GEODE_CONCAT(res, __LINE__) = (__VA_ARGS__); \
GEODE_CONCAT(res, __LINE__).isOk() && \
(variable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap(), true)
#endif

#if !defined(GEODE_UNWRAP_IF_ERR)
#define GEODE_UNWRAP_IF_ERR(variable, ...) \
auto [variable, GEODE_CONCAT(res, __LINE__)] = std::make_pair( \
geode::impl::ResultErrType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, (__VA_ARGS__) \
); \
GEODE_CONCAT(res, __LINE__).isErr() && \
(variable = std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr(), true)
#endif

#if !defined(GEODE_UNWRAP_INTO_IF_ERR)
#define GEODE_UNWRAP_INTO_IF_ERR(variable, ...) \
auto GEODE_CONCAT(res, __LINE__) = (__VA_ARGS__); \
GEODE_CONCAT(res, __LINE__).isErr() && \
(variable = std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr(), true)
#endif

#if !defined(GEODE_UNWRAP_IF_SOME)
#define GEODE_UNWRAP_IF_SOME(variable, ...) \
auto [variable, GEODE_CONCAT(res, __LINE__)] = std::make_pair( \
geode::impl::OptionalType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, (__VA_ARGS__) \
); \
GEODE_CONCAT(res, __LINE__).has_value() && \
(variable = std::move(GEODE_CONCAT(res, __LINE__)).value(), true)
#endif

#if !defined(GEODE_UNWRAP_INTO_IF_SOME)
#define GEODE_UNWRAP_INTO_IF_SOME(variable, ...) \
auto GEODE_CONCAT(res, __LINE__) = (__VA_ARGS__); \
GEODE_CONCAT(res, __LINE__).has_value() && \
(variable = std::move(GEODE_CONCAT(res, __LINE__)).value(), true)
#endif

#if !defined(GEODE_UNWRAP_OR_ELSE)
#define GEODE_UNWRAP_OR_ELSE(okVariable, errVariable, ...) \
geode::impl::ResultOkType<std::remove_cvref_t<decltype(__VA_ARGS__)>> okVariable; \
auto GEODE_CONCAT(res, __LINE__) = __VA_ARGS__; \
if (geode::impl::ResultErrType<std::remove_cvref_t<decltype(__VA_ARGS__)>> errVariable; \
GEODE_CONCAT(res, __LINE__).isErr() && \
(errVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr(), true) || \
(okVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap(), false))
#endif

#if !defined(GEODE_UNWRAP_INTO_OR_ELSE)
#define GEODE_UNWRAP_INTO_OR_ELSE(okVariable, errVariable, ...) \
auto GEODE_CONCAT(res, __LINE__) = __VA_ARGS__; \
if (geode::impl::ResultErrType<std::remove_cvref_t<decltype(__VA_ARGS__)>> errVariable; \
GEODE_CONCAT(res, __LINE__).isErr() && \
(errVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr(), true) || \
(okVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap(), false))
#endif

#if !defined(GEODE_UNWRAP_EITHER)
#define GEODE_UNWRAP_EITHER(okVariable, errVariable, ...) \
auto [okVariable, errVariable, GEODE_CONCAT(res, __LINE__)] = std::make_tuple( \
geode::impl::ResultOkType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, \
geode::impl::ResultErrType<std::remove_cvref_t<decltype(__VA_ARGS__)>>{}, \
(__VA_ARGS__) \
); \
GEODE_CONCAT(res, __LINE__).isOk() && \
(okVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap(), true) || \
(errVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr(), false)
#endif

#if !defined(GEODE_UNWRAP_INTO_EITHER)
#define GEODE_UNWRAP_INTO_EITHER(okVariable, errVariable, ...) \
auto GEODE_CONCAT(res, __LINE__) = (__VA_ARGS__); \
GEODE_CONCAT(res, __LINE__).isOk() && \
(okVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrap(), true) || \
(errVariable = std::move(GEODE_CONCAT(res, __LINE__)).unwrapErr(), false)
#endif

namespace geode {
template <class OkType, class ErrType>
Expand Down

0 comments on commit 64792cc

Please sign in to comment.