Apache Mesos
try.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_TRY_HPP__
14 #define __STOUT_TRY_HPP__
15 
16 #include <assert.h>
17 
18 #include <iostream>
19 #include <string>
20 #include <utility>
21 
22 #include <stout/abort.hpp>
23 #include <stout/error.hpp>
24 #include <stout/option.hpp>
25 #include <stout/some.hpp>
26 
27 // This class can represent only one of these states at a time:
28 // 1) A value of T.
29 // 2) An error state, with a corresponding error string.
30 // Calling 'isSome' will return true if it stores a value, in which
31 // case calling 'get' will return a constant reference to the T
32 // stored. Calling 'isError' will return true if it stores an error,
33 // in which case calling 'error' will return the error string.
34 template <typename T, typename E = Error>
35 class Try
36 {
37 public:
38  static_assert(
39  std::is_base_of<Error, E>::value,
40  "An error type must be, or be inherited from 'Error'.");
41 
42  static Try some(const T& t) { return Try(t); }
43  static Try error(const E& e) { return Try(e); }
44 
45  Try(const T& t)
46  : data(Some(t)) {}
47 
48  template <
49  typename U,
50  typename = typename std::enable_if<
51  std::is_constructible<T, const U&>::value>::type>
52  Try(const U& u) : data(Some(u)) {}
53 
54  Try(const E& error) : error_(error) {}
55 
56  Try(T&& t)
57  : data(Some(std::move(t))) {}
58 
59  template <typename U>
60  Try(const _Some<U>& some) : data(some) {}
61 
62  template <typename U>
63  Try(_Some<U>&& some) : data(std::move(some)) {}
64 
65  // We don't need to implement these because we are leveraging
66  // Option<T>.
67  Try(const Try& that) = default;
68  Try(Try&& that) = default;
69 
70  ~Try() = default;
71 
72  Try& operator=(const Try& that) = default;
73  Try& operator=(Try&& that) = default;
74 
75  // 'isSome' and 'isError' are mutually exclusive. They correspond
76  // to the underlying state of the Option.
77  bool isSome() const { return data.isSome(); }
78  bool isError() const { return data.isNone(); }
79 
80  T& get() & { return get(*this); }
81  const T& get() const & { return get(*this); }
82  T&& get() && { return get(std::move(*this)); }
83  const T&& get() const && { return get(std::move(*this)); }
84 
85  const T* operator->() const { return &get(); }
86  T* operator->() { return &get(); }
87 
88  const T& operator*() const& { return get(); }
89  T& operator*() & { return get(); }
90  const T&& operator*() const&& { return std::move(*this).get(); }
91  T&& operator*() && { return std::move(*this).get(); }
92 
93  // NOTE: This function is intended to return the error of type `E`.
94  // However, we return a `std::string` if `E` == `Error` since that's what it
95  // used to return, and it's the only data that `Error` holds anyway.
96  const typename std::conditional<
97  std::is_same<E, Error>::value, std::string, E>::type& error() const
98  {
99  assert(data.isNone());
100  assert(error_.isSome());
101  return error_impl(error_.get());
102  }
103 
104 private:
105  static const std::string& error_impl(const Error& err) { return err.message; }
106 
107  template <typename Self>
108  static auto get(Self&& self) -> decltype(std::forward<Self>(self).data.get())
109  {
110  if (!self.data.isSome()) {
111  assert(self.error_.isSome());
112  ABORT("Try::get() but state == ERROR: " + self.error_->message);
113  }
114  return std::forward<Self>(self).data.get();
115  }
116 
117  template <typename Err>
118  static const Err& error_impl(const Err& err) { return err; }
119 
120  // We leverage Option<T> to avoid dynamic allocation of T. This
121  // means that the storage for T will be included in this object
122  // (Try<T>). Since Option<T> keeps track of whether a value is
123  // stored, we just ask it when we want to know whether we are
124  // storing a value or an error. Another advantage of leveraging
125  // Option<T> is that it takes care of all the manual construction
126  // and destruction. This makes the code for Try<T> really simple!
127  Option<T> data;
128  Option<E> error_;
129 };
130 
131 
132 #endif // __STOUT_TRY_HPP__
Definition: errorbase.hpp:36
Definition: option.hpp:29
#define ABORT(...)
Definition: abort.hpp:40
const T && operator*() const &&
Definition: try.hpp:90
const std::conditional< std::is_same< E, Error >::value, std::string, E >::type & error() const
Definition: try.hpp:97
Definition: check.hpp:33
T * operator->()
Definition: try.hpp:86
T & operator*()&
Definition: try.hpp:89
Definition: type_utils.hpp:570
Try(_Some< U > &&some)
Definition: try.hpp:63
bool isSome() const
Definition: option.hpp:116
Try(const E &error)
Definition: try.hpp:54
Definition: some.hpp:33
bool isSome() const
Definition: try.hpp:77
const T & get() const &
Definition: option.hpp:119
static Try error(const E &e)
Definition: try.hpp:43
Try(T &&t)
Definition: try.hpp:56
T && operator*()&&
Definition: try.hpp:91
static Try some(const T &t)
Definition: try.hpp:42
const std::string message
Definition: errorbase.hpp:46
_Some< typename std::decay< T >::type > Some(T &&t)
Definition: some.hpp:42
bool isError() const
Definition: try.hpp:78
const T & operator*() const &
Definition: try.hpp:88
~Try()=default
Try(const T &t)
Definition: try.hpp:45
Try< uint32_t > type(const std::string &path)
Try(const _Some< U > &some)
Definition: try.hpp:60
Try(const U &u)
Definition: try.hpp:52
Try & operator=(const Try &that)=default
const T * operator->() const
Definition: try.hpp:85