Kagome
Polkadot Runtime Engine in C++17
Guide for `outcome::result<T>`

Use outcome::result<T> from <outcome/outcome.hpp> to represent either value of type T or std::error_code.

DO NOT DEFINE CUSTOM ERROR TYPES. There is one good explanation for that – one can not merge two custom types automatically, however error codes can be merged.

Please, read https://ned14.github.io/outcome/ carefully.

Creating outcome::result<T>

#### Example 1 - Enum in namespace:

1 {C++}
2 /////////////
3 // mylib.hpp:
4 
5 #include <outcome/outcome.hpp>
6 
7 namespace my::super:lib {
8  enum class MyError {
9  Case1 = 1, // NOTE: MUST NOT start with 0 (it represents success)
10  Case2 = 2,
11  Case3 = 4, // any codes may be used
12  Case4 // or no codes at all
13  };
14 
15  outcome::result<int> calc(int a, int b);
16 }
17 // declare required functions in hpp
18 // outside of any namespace
19 OUTCOME_HPP_DECLARE_ERROR(my::super::lib, MyError);
20 
21 
22 /////////////
23 // mylib.cpp:
24 #include "mylib.hpp"
25 
26 // outside of any namespace
27 OUTCOME_CPP_DEFINE_CATEGORY(my::super::lib, MyError, e){
28  using my::super::lib::MyError; // not necessary, just for convenience
29  switch(e) {
30  case MyError::Case1: return "Case1 message";
31  case MyError::Case2: return "Case2 message";
32  case MyError::Case3: return "Case3 message";
33  case MyError::Case4: return "Case4 message";
34  default: return "unknown"; // NOTE: do not forget to handle everything else
35  }
36 }
37 
38 namespace my::super::lib {
39  outcome::result<int> calc(int a, int b){
40  // then simply return enum in case of error:
41  if(a < 0) return MyError::Case1;
42  if(a > 100) return MyError::Case2;
43  if(b < 0) return MyError::Case3;
44  if(b < 100) return MyError::Case4;
45 
46  return a + b; // simply return value in case of value:
47  }
48 }

**Example 2** - Enum as class member:

1 /////////////
2 // mylib.hpp:
3 
4 #include <outcome/outcome.hpp>
5 
6 namespace my::super:lib {
7 
8  class MyLib {
9  public:
10  // MyError now is a member of class
11 + enum class MyError {
12 + Case1 = 1, // NOTE: MUST NOT start with 0 (it represents success)
13 + Case2 = 2,
14 + Case3 = 4, // any codes may be used
15 + Case4 // or no codes at all
16 + };
17 
18  outcome::result<int> calc(int a, int b);
19  }
20 }
21 // declare required functions in hpp
22 // outside of any namespace
23 +// NOTE: 1 args is only namespace, class prefix should be added to enum
24 -OUTCOME_HPP_DECLARE_ERROR(my::super::lib, MyError);
25 +OUTCOME_HPP_DECLARE_ERROR(my::super::lib, MyLib::MyError);
26 
27 /////////////
28 // mylib.cpp:
29 #include "mylib.hpp"
30 
31 -OUTCOME_CPP_DEFINE_CATEGORY(my::super::lib, MyError, e){
32 +OUTCOME_CPP_DEFINE_CATEGORY(my::super::lib, MyLib::MyError, e){
33 - using my::super::lib::MyError; // not necessary, just for convenience
34 + using my::super::lib::MyLib::MyError; // not necessary, just for convenience
35  switch(e) {
36  case MyError::Case1: return "Case1 message";
37  case MyError::Case2: return "Case2 message";
38  case MyError::Case3: return "Case3 message";
39  case MyError::Case4: return "Case4 message";
40  default: return "unknown"; // NOTE: do not forget to handle everything else
41  }
42 }
43 
44 namespace my::super::lib {
45 - outcome::result<int> calc(int a, int b)
46 + outcome::result<int> MyLib::calc(int a, int b){
47  // then simply return enum in case of error:
48  if(a < 0) return MyError::Case1;
49  if(a > 100) return MyError::Case2;
50  if(b < 0) return MyError::Case3;
51  if(b > 100) return MyError::Case4;
52 
53  return a + b; // simply return value in case of value
54  }
55 }

Inspecting outcome::result<T>

Inspecting is very straightforward:

1 {C++}
2 
3 outcome::result<int> calc(int a, int b){
4  // then simply return enum in case of error:
5  if(a < 0) return MyError::Case1;
6  if(a > 100) return MyError::Case2;
7  if(b < 0) return MyError::Case3;
8  if(b < 100) return MyError::Case4;
9 
10  return a + b; // simply return value in case of value:
11 }
12 
13 outcome::result<int> parent(int a) {
14  // NOTE: returns error if calc returned it, otherwise get unwrapped calc result
15  OUTCOME_TRY(val, calc(a, 1); // use convenient macro
16  // here val=a+1
17 
18  // or
19 
20  auto&& result = calc(a, 2);
21  if(result) {
22  // has value
23  auto&& v = result.value();
24  return v;
25  } else {
26  // has error
27  auto&& e = result.error(); // get std::error_code
28  return e;
29  }
30 
31  // or
32 
33  // pass result to parent
34  return calc(a, 3);
35 }