kendryte-standalone-sdk/lib/nncase/v1/include/nncase/runtime/result.h

374 lines
10 KiB
C++

/* Copyright 2019-2021 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "compiler_defs.h"
#include <functional>
#include <mpark/variant.hpp>
#include <system_error>
#include <type_traits>
namespace nncase
{
#define try_(x) \
{ \
auto v = (x); \
if (!v.is_ok()) \
return nncase::err(std::move(v.unwrap_err())); \
}
#define try_var(name, x) \
typename decltype((x))::traits::ok_type name; \
{ \
auto v = (x); \
if (v.is_ok()) \
name = std::move(v.unwrap()); \
else \
return nncase::err(std::move(v.unwrap_err())); \
}
#define try_var_err(name, x, e) \
typename decltype((x))::traits::ok_type name; \
{ \
auto v = (x); \
if (v.is_ok()) \
{ \
name = std::move(v.unwrap()); \
} \
else \
{ \
e = nncase::err(std::move(v.unwrap_err())); \
return; \
} \
}
#define try_set(name, x) \
{ \
auto v = (x); \
if (v.is_ok()) \
name = std::move(v.unwrap()); \
else \
return nncase::err(std::move(v.unwrap_err())); \
}
template <class T>
struct Ok
{
constexpr Ok(T &&value)
: value(std::move(value)) { }
constexpr Ok(const T &value)
: value(value) { }
template <class... Args>
constexpr explicit Ok(mpark::in_place_t, Args &&... args)
: value(std::forward<Args>(args)...) { }
T value;
};
template <>
struct Ok<void>
{
};
struct Err
{
template <class ErrCode, class = std::enable_if_t<std::is_error_condition_enum<ErrCode>::value>>
Err(ErrCode value)
: err(value) { }
Err(std::error_condition err)
: err(std::move(err)) { }
std::error_condition err;
};
inline constexpr Ok<void> ok()
{
return {};
}
template <class T, class... Args>
constexpr Ok<T> ok(Args &&... args)
{
return Ok<T>(mpark::in_place, std::forward<Args>(args)...);
}
template <class T>
constexpr Ok<std::decay_t<T>> ok(T &&value)
{
return Ok<std::decay_t<T>>(std::forward<T>(value));
}
inline Err err(std::error_condition value) noexcept
{
return Err(std::move(value));
}
template <class ErrCode, class = std::enable_if_t<std::is_error_condition_enum<ErrCode>::value>>
Err err(ErrCode value)
{
return err(std::error_condition(value));
}
template <class T>
class NNCASE_NODISCARD result;
namespace detail
{
template <class T>
NNCASE_INLINE_VAR bool constexpr is_result_v = false;
template <class T>
NNCASE_INLINE_VAR bool constexpr is_result_v<result<T>> = true;
template <class T>
struct result_traits
{
static_assert(!is_result_v<T>, "Cannot use nested result");
using ok_type = T;
};
template <class T, class U, class Func>
class map_call_impl
{
result<U> operator()(Func &&func, Ok<T> &value) noexcept
{
return ok(func(value.value));
}
};
template <class T, class Func>
struct map_traits;
template <class U, class Func>
class map_call_void_impl
{
result<U> operator()(Func &&func) noexcept
{
return ok(func());
}
};
template <class Func>
struct map_traits<void, Func>
{
using U = invoke_result_t<Func>;
static_assert(!is_result_v<U>, "Cannot map a callback returning result, use and_then instead");
result<U> operator()(Func &&func, NNCASE_UNUSED Ok<void> &value) noexcept
{
return map_call_void_impl<U, Func>()(std::forward<Func>(func));
}
};
template <class T, class Func>
struct map_err_traits;
template <class T, class Func>
struct and_then_traits
{
using result_t = invoke_result_t<Func, T>;
using traits_t = typename result_t::traits;
using U = typename traits_t::ok_type;
static_assert(is_result_v<result_t>, "Cannot then a callback not returning result, use map instead");
result_t operator()(Func &&func, Ok<T> &value) noexcept
{
return func(value.value);
}
};
template <class Func>
struct and_then_traits<void, Func>
{
using result_t = invoke_result_t<Func>;
using traits_t = typename result_t::traits;
using U = typename traits_t::ok_type;
static_assert(is_result_v<result_t>, "Cannot then a callback not returning result, use map instead");
result_t operator()(Func &&func, NNCASE_UNUSED Ok<void> &value) noexcept
{
return func();
}
};
template <class T>
struct unwrap_impl
{
T &operator()(Ok<T> &value) noexcept
{
return value.value;
}
T &&operator()(Ok<T> &&value) noexcept
{
return std::move(value.value);
}
};
template <>
struct unwrap_impl<void>
{
void operator()(NNCASE_UNUSED Ok<void> &value) noexcept
{
}
void operator()(NNCASE_UNUSED Ok<void> &&value) noexcept
{
}
};
}
template <class T>
class NNCASE_NODISCARD result
{
public:
using traits = detail::result_traits<T>;
constexpr result(Ok<T> value)
: ok_or_err_(std::move(value)) { }
result(Err err)
: ok_or_err_(std::move(err)) { }
constexpr bool is_ok() const noexcept { return ok_or_err_.index() == 0; }
constexpr bool is_err() const noexcept { return ok_or_err_.index() == 1; }
constexpr decltype(auto) unwrap() noexcept
{
if (is_ok())
return detail::unwrap_impl<T>()(value());
else
std::terminate();
}
constexpr decltype(auto) unwrap_or_throw() &
{
if (is_ok())
return detail::unwrap_impl<T>()(value());
else
throw std::runtime_error(unwrap_err().message());
}
constexpr decltype(auto) unwrap_or_throw() &&
{
if (is_ok())
return detail::unwrap_impl<T>()(std::move(value()));
else
throw std::runtime_error(unwrap_err().message());
}
constexpr std::error_condition &unwrap_err() noexcept
{
if (is_ok())
std::terminate();
else
return err().err;
}
constexpr auto expect(NNCASE_UNUSED gsl::cstring_span message) noexcept
{
if (is_ok())
return detail::unwrap_impl<T>()(value());
else
std::terminate();
}
template <class Func, class Traits = detail::map_traits<T, Func>>
constexpr typename Traits::result_t map(Func &&func) noexcept
{
if (is_ok())
return Traits()(std::forward<Func>(func), value());
else
return err();
}
template <class Func, class Traits = detail::map_err_traits<T, Func>>
constexpr typename Traits::result_t map_err(Func &&func) noexcept
{
if (is_ok())
return value();
else
return Traits()(std::forward<Func>(func), err());
}
template <class Func, class Traits = detail::and_then_traits<T, Func>>
constexpr typename Traits::result_t and_then(Func &&func) noexcept
{
if (is_ok())
return Traits()(std::forward<Func>(func), value());
else
return err();
}
private:
constexpr Ok<T> &&value() &&noexcept { return mpark::get<Ok<T>>(ok_or_err_); }
constexpr Ok<T> &value() &noexcept { return mpark::get<Ok<T>>(ok_or_err_); }
constexpr Err &err() noexcept { return mpark::get<Err>(ok_or_err_); }
private:
mpark::variant<Ok<T>, Err> ok_or_err_;
};
namespace detail
{
template <class T, class Func>
struct map_traits
{
using U = invoke_result_t<Func, T>;
static_assert(!is_result_v<U>, "Cannot map a callback returning result, use and_then instead");
using result_t = result<U>;
result<U> operator()(Func &&func, Ok<T> &value) noexcept
{
return map_call_impl<T, U, Func>()(std::forward<Func>(func), value);
}
};
template <class T, class Func>
struct map_err_traits
{
using U = invoke_result_t<Func, Err>;
static_assert(!is_result_v<U>, "Cannot map a callback returning result, use and_then instead");
result<U> operator()(Func &&func, Err &value) noexcept
{
return err(func(value.err));
}
};
template <class T, class Func>
class map_call_impl<T, void, Func>
{
result<void> operator()(Func &&func, Ok<T> &value) noexcept
{
func(value.value);
return ok();
}
};
template <class Func>
class map_call_void_impl<void, Func>
{
result<void> operator()(Func &&func) noexcept
{
func();
return ok();
}
};
}
}