4. 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.

4.1. Creating outcome::result<T>

4.1.1. Example 1 - Enum in namespace:

/////////////
// mylib.hpp:

#include <outcome/outcome.hpp>

namespace my::super:lib {
  enum class MyError {
    Case1 = 1,  // NOTE: MUST NOT start with 0 (it represents success)
    Case2 = 2,
    Case3 = 4,  // any codes may be used
    Case4       // or no codes at all
  };

  outcome::result<int> calc(int a, int b);
}
// declare required functions in hpp
// outside of any namespace
OUTCOME_HPP_DECLARE_ERROR(my::super::lib, MyError);


/////////////
// mylib.cpp:
#include "mylib.hpp"

// outside of any namespace
OUTCOME_CPP_DEFINE_CATEGORY(my::super::lib, MyError, e){
  using my::super::lib::MyError; // not necessary, just for convenience
  switch(e) {
    case MyError::Case1: return "Case1 message";
    case MyError::Case2: return "Case2 message";
    case MyError::Case3: return "Case3 message";
    case MyError::Case4: return "Case4 message";
    default: return "unknown"; // NOTE: do not forget to handle everything else
  }
}

namespace my::super::lib {
  outcome::result<int> calc(int a, int b){
    // then simply return enum in case of error:
    if(a < 0)   return MyError::Case1;
    if(a > 100) return MyError::Case2;
    if(b < 0)   return MyError::Case3;
    if(b < 100) return MyError::Case4;

    return a + b; // simply return value in case of value:
  }
}

4.1.2. Example 2 - Enum as class member:

/////////////
// mylib.hpp:

#include <outcome/outcome.hpp>

namespace my::super:lib {

  class MyLib {
   public:
    // MyError now is a member of class
+   enum class MyError {
+     Case1 = 1,  // NOTE: MUST NOT start with 0 (it represents success)
+     Case2 = 2,
+     Case3 = 4,  // any codes may be used
+     Case4       // or no codes at all
+   };

    outcome::result<int> calc(int a, int b);
  }
}
// declare required functions in hpp
// outside of any namespace
+// NOTE: 1 args is only namespace, class prefix should be added to enum
-OUTCOME_HPP_DECLARE_ERROR(my::super::lib, MyError);
+OUTCOME_HPP_DECLARE_ERROR(my::super::lib, MyLib::MyError);

/////////////
// mylib.cpp:
#include "mylib.hpp"

-OUTCOME_CPP_DEFINE_CATEGORY(my::super::lib, MyError, e){
+OUTCOME_CPP_DEFINE_CATEGORY(my::super::lib, MyLib::MyError, e){
- using my::super::lib::MyError; // not necessary, just for convenience
+ using my::super::lib::MyLib::MyError; // not necessary, just for convenience
  switch(e) {
    case MyError::Case1: return "Case1 message";
    case MyError::Case2: return "Case2 message";
    case MyError::Case3: return "Case3 message";
    case MyError::Case4: return "Case4 message";
    default: return "unknown"; // NOTE: do not forget to handle everything else
  }
}

namespace my::super::lib {
-  outcome::result<int> calc(int a, int b)
+  outcome::result<int> MyLib::calc(int a, int b){
    // then simply return enum in case of error:
    if(a < 0)   return MyError::Case1;
    if(a > 100) return MyError::Case2;
    if(b < 0)   return MyError::Case3;
    if(b > 100) return MyError::Case4;

    return a + b; // simply return value in case of value
  }
}

4.2. Inspecting outcome::result<T>

Inspecting is very straightforward:

outcome::result<int> calc(int a, int b){
  // then simply return enum in case of error:
  if(a < 0)   return MyError::Case1;
  if(a > 100) return MyError::Case2;
  if(b < 0)   return MyError::Case3;
  if(b < 100) return MyError::Case4;

  return a + b; // simply return value in case of value:
}

outcome::result<int> parent(int a) {
  // NOTE: returns error if calc returned it, otherwise get unwrapped calc result
  OUTCOME_TRY(val, calc(a, 1);  // use convenient macro
  // here val=a+1

  // or

  auto&& result = calc(a, 2);
  if(result) {
    // has value
    auto&& v = result.value();
    return v;
  } else {
    // has error
    auto&& e = result.error(); // get std::error_code
    return e;
  }

  // or

  // pass result to parent
  return calc(a, 3);
}