LCOV - code coverage report
Current view: top level - libs/common - result.hpp (source / functions) Hit Total Coverage
Test: coverage_cleared.info Lines: 16 18 88.9 %
Date: 2018-12-05 17:11:35 Functions: 668 838 79.7 %

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

Generated by: LCOV version 1.13