LCOV - code coverage report
Current view: top level - libs/common - result.hpp (source / functions) Hit Total Coverage
Test: cleared_cor.info Lines: 16 18 88.9 %
Date: 2019-03-07 14:46:43 Functions: 596 693 86.0 %

          Line data    Source code
       1             : /**
       2             :  * Copyright Soramitsu Co., Ltd. All Rights Reserved.
       3             :  * SPDX-License-Identifier: Apache-2.0
       4             :  */
       5             : 
       6             : #ifndef IROHA_RESULT_HPP
       7             : #define IROHA_RESULT_HPP
       8             : 
       9             : #include <ciso646>
      10             : 
      11             : #include <boost/optional.hpp>
      12             : #include <boost/variant.hpp>
      13             : 
      14             : #include "common/visitor.hpp"
      15             : 
      16             : /*
      17             :  * Result is a type which represents value or an error, and values and errors
      18             :  * are template parametrized. Working with value wrapped in result is done using
      19             :  * match() function, which accepts 2 functions: for value and error cases. No
      20             :  * accessor functions are provided.
      21             :  */
      22             : 
      23             : namespace iroha {
      24             :   namespace expected {
      25             : 
      26             :     /*
      27             :      * Value and error types can be constructed from any value or error, if
      28             :      * underlying types are constructible. Example:
      29             :      *
      30             :      * @code
      31             :      * Value<std::string> v = Value<const char *>("hello");
      32             :      * @nocode
      33             :      */
      34             : 
      35             :     template <typename T>
      36             :     struct Value {
      37             :       T value;
      38             :       template <typename V>
      39             :       operator Value<V>() {
      40        2090 :         return {value};
      41             :       }
      42             :     };
      43             : 
      44             :     template <>
      45             :     struct Value<void> {};
      46             : 
      47             :     template <typename E>
      48             :     struct Error {
      49             :       E error;
      50             :       template <typename V>
      51             :       operator Error<V>() {
      52          64 :         return {error};
      53             :       }
      54             :     };
      55             : 
      56             :     template <>
      57             :     struct Error<void> {};
      58             : 
      59             :     /**
      60             :      * Result is a specialization of a variant type with value or error
      61             :      * semantics.
      62             :      * @tparam V type of value
      63             :      * @tparam E error type
      64             :      */
      65             :     template <typename V, typename E>
      66             :     class Result : public boost::variant<Value<V>, Error<E>> {
      67             :       using variant_type = boost::variant<Value<V>, Error<E>>;
      68             :       using variant_type::variant_type;  // inherit constructors
      69             : 
      70             :      public:
      71             :       using ValueType = Value<V>;
      72             :       using ErrorType = Error<E>;
      73             : 
      74             :       /**
      75             :        * match is a function which allows working with result's underlying
      76             :        * types, you must provide 2 functions to cover success and failure cases.
      77             :        * Return type of both functions must be the same. Example usage:
      78             :        * @code
      79             :        * result.match([](Value<int> v) { std::cout << v.value; },
      80             :        *              [](Error<std::string> e) { std::cout << e.error; });
      81             :        * @nocode
      82             :        */
      83             :       template <typename ValueMatch, typename ErrorMatch>
      84             :       constexpr auto match(ValueMatch &&value_func, ErrorMatch &&error_func) {
      85       40410 :         return visit_in_place(*this,
      86       40410 :                               std::forward<ValueMatch>(value_func),
      87       40410 :                               std::forward<ErrorMatch>(error_func));
      88             :       }
      89             : 
      90             :       /**
      91             :        * Const alternative for match function
      92             :        */
      93             :       template <typename ValueMatch, typename ErrorMatch>
      94             :       constexpr auto match(ValueMatch &&value_func,
      95             :                            ErrorMatch &&error_func) const {
      96        3341 :         return visit_in_place(*this,
      97        3341 :                               std::forward<ValueMatch>(value_func),
      98        3341 :                               std::forward<ErrorMatch>(error_func));
      99             :       }
     100             : 
     101             :       /**
     102             :        * Lazy error AND-chaining
     103             :        * Works by the following table (aka boolean lazy AND):
     104             :        * err1 * any  -> err1
     105             :        * val1 * err2 -> err2
     106             :        * val1 * val2 -> val2
     107             :        *
     108             :        * @param new_res second chain argument
     109             :        * @return new_res if this Result contains a value
     110             :        *         otherwise return this
     111             :        */
     112             :       template <typename Value>
     113             :       constexpr Result<Value, E> and_res(const Result<Value, E> &new_res) const
     114             :           noexcept {
     115           2 :         return visit_in_place(
     116             :             *this,
     117             :             [res = new_res](ValueType) { return res; },
     118             :             [](ErrorType err) -> Result<Value, E> { return err; });
     119           0 :       }
     120             : 
     121             :       /**
     122             :        * Lazy error OR-chaining
     123             :        * Works by the following table (aka boolean lazy OR):
     124             :        * val1 * any  -> val1
     125             :        * err1 * val2 -> val2
     126             :        * err1 * err2 -> err2
     127             :        *
     128             :        * @param new_res second chain argument
     129             :        * @return new_res if this Result contains a error
     130             :        *         otherwise return this
     131             :        */
     132             :       template <typename Value>
     133             :       constexpr Result<Value, E> or_res(const Result<Value, E> &new_res) const
     134             :           noexcept {
     135           2 :         return visit_in_place(
     136             :             *this,
     137             :             [](ValueType val) -> Result<Value, E> { return val; },
     138             :             [res = new_res](ErrorType) { return res; });
     139           0 :       }
     140             :     };
     141             : 
     142             :     template <typename ResultType>
     143             :     using ValueOf = typename ResultType::ValueType;
     144             :     template <typename ResultType>
     145             :     using ErrorOf = typename ResultType::ErrorType;
     146             : 
     147             :     /**
     148             :      * Get a new result with the copied value or mapped error
     149             :      * @param res base Result for getting new one
     150             :      * @param map callback for error mapping
     151             :      * @return result with changed error
     152             :      */
     153             :     template <typename Err1, typename Err2, typename V, typename Fn>
     154             :     Result<V, Err1> map_error(const Result<V, Err2> &res, Fn &&map) noexcept {
     155           1 :       return visit_in_place(res,
     156             :                             [](Value<V> val) -> Result<V, Err1> { return val; },
     157             :                             [map](Error<Err2> err) -> Result<V, Err1> {
     158           1 :                               return Error<Err1>{map(err.error)};
     159             :                             });
     160             :     }
     161             : 
     162             :     // Factory methods for avoiding type specification
     163             :     template <typename T>
     164             :     Value<T> makeValue(T &&value) {
     165       25731 :       return Value<T>{std::forward<T>(value)};
     166             :     }
     167             : 
     168             :     template <typename E>
     169             :     Error<E> makeError(E &&error) {
     170        1287 :       return Error<E>{std::forward<E>(error)};
     171             :     }
     172             : 
     173             :     /**
     174             :      * Bind operator allows chaining several functions which return result. If
     175             :      * result contains error, it returns this error, if it contains value,
     176             :      * function f is called.
     177             :      * @param f function which return type must be compatible with original
     178             :      * result
     179             :      */
     180             :     template <typename T, typename E, typename Transform>
     181             :     constexpr auto operator|(Result<T, E> r, Transform &&f) ->
     182             :         typename std::enable_if<
     183             :             not std::is_same<decltype(f(std::declval<T>())), void>::value,
     184             :             decltype(f(std::declval<T>()))>::type {
     185             :       using return_type = decltype(f(std::declval<T>()));
     186         249 :       return r.match(
     187             :           [&f](const Value<T> &v) { return f(v.value); },
     188             :           [](const Error<E> &e) { return return_type(makeError(e.error)); });
     189             :     }
     190             : 
     191             :     /**
     192             :      * Bind operator overload for functions which do not accept anything as a
     193             :      * parameter. Allows execution of a sequence of unrelated functions, given
     194             :      * that all of them return Result
     195             :      * @param f function which accepts no parameters and returns result
     196             :      */
     197             :     template <typename T, typename E, typename Procedure>
     198             :     constexpr auto operator|(Result<T, E> r, Procedure f) ->
     199             :         typename std::enable_if<not std::is_same<decltype(f()), void>::value,
     200             :                                 decltype(f())>::type {
     201             :       using return_type = decltype(f());
     202         732 :       return r.match(
     203             :           [&f](const Value<T> &v) { return f(); },
     204             :           [](const Error<E> &e) { return return_type(makeError(e.error)); });
     205             :     }
     206             : 
     207             :     /**
     208             :      * Polymorphic Result is simple alias for result type, which can be used to
     209             :      * work with polymorphic objects. It is achieved by wrapping V and E in a
     210             :      * polymorphic container (std::shared_ptr is used by default). This
     211             :      * simplifies declaration of polymorphic result.
     212             :      *
     213             :      * Note: ordinary result itself stores both V and E directly inside itself
     214             :      * (on the stack), polymorphic result stores objects wherever VContainer and
     215             :      * EContainer store them, but since you need polymorphic behavior, it will
     216             :      * probably be on the heap. That is why polymorphic result is generally
     217             :      * slower, and should be used ONLY when polymorphic behaviour is required,
     218             :      * hence the name. For all other use cases, stick to basic Result
     219             :      */
     220             :     template <typename V,
     221             :               typename E,
     222             :               typename VContainer = std::shared_ptr<V>,
     223             :               typename EContainer = std::shared_ptr<E>>
     224             :     using PolymorphicResult = Result<VContainer, EContainer>;
     225             : 
     226             :   }  // namespace expected
     227             : }  // namespace iroha
     228             : #endif  // IROHA_RESULT_HPP

Generated by: LCOV version 1.13