lumiera_documentation/src/lib/result.hpp
Ichthyostega 806db414dd Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
 * there is no entity "Lumiera.org" which holds any copyrights
 * Lumiera source code is provided under the GPL Version 2+

== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''

The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!

The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00

227 lines
7.6 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
RESULT.hpp - intermediary token representing the result of an operation
Copyright (C)
2010, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
*/
/** @file result.hpp
** Intermediary value object to represent »either« an operation result or a failure.
** Some operation might have produced a value result or failed with an exception.
** Typically, the Result token is used _inline_ — immediately either invoking
** one of the member function or employing the built-in result type conversion.
** It will be copyable iff the result value is copyable. There is an implicit
** valid or failure state, which can be tested. Any attempt to get the value
** of an invalid result token will cause an exception to be thrown.
** - `Result<void>(bool)` can be used as a success marker
** - a `Result` instance can be created by _perfect forwarding_ from any type
** - any exception is supported for failure, wile direct construction is limited
** to lumiera::Error (to avoid ambiguities in ctor overload resolution)
** - an arbitrary functor or _callable_ can be invoked, capturing the result.
** @todo an _option-style_ interface could be provided for the »right value«
** (i.e. the exception caught), in case this turns out to be of any use;
** this kind of API design however is anything than trivial, given that
** any value can be thrown as exception in C++
** @see vault::ThreadJoinable usage example
*/
#ifndef LIB_RESULT_H
#define LIB_RESULT_H
#include "lib/error.hpp"
#include "lib/wrapper.hpp"
#include "lib/meta/util.hpp"
#include "lib/null-value.hpp"
#include <type_traits>
#include <exception>
#include <utility>
namespace lib {
namespace error = lumiera::error;
/**
* Helper to invoke an arbitrary callable in a failsafe way.
* @param capturedFailure *reference* to a std::exeption_ptr served by side-effect
* @param callable anything std::invoke can handle
* @return _if_ the invokable has a return type, the result is returned,
* _otherwise_ this is a void function
* @todo with C++20 the body of the implementation can be replaced by std::invoke_r //////////////////////TICKET #1245
*/
template<class FUN, typename...ARGS>
inline auto
failsafeInvoke (std::exception_ptr& capturedFailure
,FUN&& callable
,ARGS&& ...args) noexcept
{
using Res = std::invoke_result_t<FUN,ARGS...>;
try {
capturedFailure = nullptr;
if constexpr (std::is_void_v<Res>)
std::invoke (std::forward<FUN>(callable), std::forward<ARGS>(args)...);
else
return std::invoke (std::forward<FUN>(callable), std::forward<ARGS>(args)...);
}
catch(...)
{
capturedFailure = std::current_exception();
if constexpr (not std::is_void_v<Res>)
return lib::NullValue<Res>::get();
}
}
/**
* Representation of the result of some operation, _EITHER_ a value or a failure.
* It can be created for passing a result produced by the operation, or the failure
* to do so. The value can be retrieved by implicit or explicit conversion.
* @throw error::State on any attempt to access the value in case of failure
* @warning this class has a lot of implicit conversions;
* care should be taken when defining functions
* to take Result instances as parameter....
*/
template<typename RES>
class Result;
/**
* The base case is just to capture success or failure,
* without returning any value result.
*/
template<>
class Result<void>
{
protected:
std::exception_ptr failure_;
public:
/** mark either failure (default) or success */
Result (bool success =false)
: failure_{success? nullptr: std::make_exception_ptr (error::State{"operation failed"})}
{ }
/** failed result, with reason given.*/
Result (lumiera::Error const& reason)
: failure_{std::make_exception_ptr (reason)}
{ }
/** invoke a _callable_ and mark success or failure */
template<class FUN, typename...ARGS, typename=lib::meta::enable_if<std::is_invocable<FUN,ARGS...>>>
Result (FUN&& callable, ARGS&& ...args) noexcept
: failure_{}
{
failsafeInvoke (failure_
,std::forward<FUN> (callable)
,std::forward<ARGS>(args)...);
}
explicit
operator bool() const { return isValid(); }
bool isValid() const { return not failure_; }
void
maybeThrow() const
{
if (failure_)
std::rethrow_exception(failure_);
}
};
/**
* Optional Result value or status of some operation.
* It can be created for passing a result produced by the operation, or the
* failure to do so. The value can be retrieved by implicit or explicit conversion.
* @throw error::State on any attempt to access the value in case of failure
* @warning this class has a lot of implicit conversions;
* care should be taken when defining functions
* to take Result instances as parameter....
*/
template<typename RES>
class Result
: public Result<void>
{
wrapper::ItemWrapper<RES> value_;
public:
/** mark failed result, with reason given.*/
Result (lumiera::Error const& reason)
: Result<void>{reason}
{ }
/** standard case: valid result */
template< typename=lib::meta::disable_if<std::is_invocable<RES>>>
Result (RES&& value)
: Result<void>{true}
, value_{std::forward<RES> (value)}
{ }
/** invoke a _callable_ and capture result in one shot */
template<class FUN, typename...ARGS, typename=lib::meta::enable_if<std::is_invocable<FUN,ARGS...>>>
Result (FUN&& callable, ARGS&& ...args) noexcept
: Result<void>{true}
, value_{failsafeInvoke (failure_
,std::forward<FUN> (callable)
,std::forward<ARGS>(args)...)}
{ }
// is or is not copyable depending on RES
operator RES() const
{
maybeThrow();
return *value_;
}
template<typename TY =RES>
TY
get() const
{
maybeThrow();
return static_cast<TY> (*value_);
}
template<typename O>
RES
value_or (O&& defaultVal)
{
return isValid()? *value_ : std::forward<O> (defaultVal);
}
template<typename MAKE, typename...ARGS>
RES
or_else (MAKE&& producer, ARGS ...args)
{
if (isValid())
return *value_;
else
return std::invoke(std::forward<MAKE> (producer), std::forward<ARGS> (args)...);
}
};
/** deduction guide: allow _perfect forwarding_ of a any result into the ctor call. */
template<typename VAL, typename=lib::meta::disable_if<std::is_invocable<VAL>>>
Result (VAL&&) -> Result<VAL>;
/** deduction guide: find out about result value to capture from a generic callable. */
template<typename FUN, typename...ARGS>
Result (FUN&&, ARGS&&...) -> Result<std::invoke_result_t<FUN,ARGS...>>;
} // namespace lib
#endif