Home » C++23: A new way of error handling with std::expected

C++23: A new way of error handling with std::expected

by admin
C++23: A new way of error handling with std::expected

C++23: A new way of error handling with std::expected

The data type std::optional is available since C++17. With C++23 he gets an extended monadic interface, which I will present in more detail below. But before that, let’s take a quick look back at the data type.

Advertisement

The std::optional data type is very useful for calculations such as database queries that can have a result. This data type requires the header.

Rainer Grimm has been working as a software architect, team leader and training manager for many years. He likes to write articles on the programming languages ​​C++, Python and Haskell, but also likes to speak frequently at specialist conferences. On his blog Modernes C++ he deals intensively with his passion for C++.

With the different constructors and the function std::make_optional an optional object opt ​​can easily be defined, with or without a value. opt.emplace constructs the contained value in place and opt.reset destroys the container value. You can explicitly ask a std::optional container if it has a value, or you can test it in a logical expression. opt.value returns the value, and opt.value_or returns the value or a default value. If opt does not contain a value, calling opt.value throws a std::bad_optional_access exception.

Here is a simple example using std::optional:

// optional.cpp

#include
#include
#include

std::optional getFirst(const std::vector& vec){
if ( !vec.empty() ) return std::optional(vec[0]);
else return std::optional();
}

int main() {

std::cout myVec{1, 2, 3};
std::vector myEmptyVec;

auto myInt= getFirst(myVec);

if (myInt){
std::cout I’m using std::optional in the getFirst function. getFirst returns the first element if it exists. If not, you get a std::optional object. The main function has two vectors. Both call getFirst and return a std::optional object. In the case of myInt, the object has a value; in the case of myEmptyInt, the object has no value. The program displays the value of myInt and myEmptyInt. myInt.value_or(2017) returns the value, but myEmptyInt.value_or(2017) returns the default value.

See also  Apple and sustainability, an example to follow

Advertisement

Here is the output of the program:

The monadic extension of std::optional

In C++23 std::optional is extended by the monadic operations opt.and_then, opt.transform and opt.or_else.

opt.and_then returns the result of the specified function call if it exists, otherwise an empty std::optional.opt.transform returns an std::optional containing the transformed value, or an empty std::optional.opt. or_else returns the std::optional if it contains a value, or the result of the specified function otherwise.

These monadic operations allow composition of operations on std::optional:

// optionalMonadic.cpp

#include
#include
#include
#include

std::optional getInt(std::string arg) {
try {
return {std::stoi(arg)};
}
catch (…) {
return { };
}
}

int main() {

std::cout > strings = {“66”, “foo”, “-5”};

for (auto s: strings) {
auto res = s.and_then(getInt)
.transform( [](int n) { return n + 100;})
.transform( [](int n) { return std::to_string(n); })
.or_else([] { return std::optional{std::string(“Error”) }; });
std::cout The range-based for loop iterates through the std::vector<:string>>. First, the getInt function converts each element to an integer, adds 100 to it, converts it back to a string, and finally displays it. If the initial cast to int fails, the String Error is returned and displayed.

std::expected already supports the monadic interface.

std::expected provides a way to store one of two values. An instance of std::expected always contains a value: either the expected value of type T or the unexpected value of type E. This vocabulary type requires the header . Thanks to std::expected you can implement functions that return either a value or an error. The stored value is allocated directly within the memory occupied by the expected object. There is no dynamic memory allocation.

std::expected has an interface similar to std::optional. Unlike std::optional, std::exptected can return an error message.

See also  Software development: The formatting library in C++20

With the different constructors you can define an expected object exp with an expected value. exp.emplace constructs the contained value in place. You can explicitly ask a std::expected container if it has a value, or you can test it in a logical expression. exp.value returns the expected value and exp.value_or returns the expected value or a default value. If exp has an unexpected value, calling exp.value throws a std::bad_expected_access exception.

std::unexpected represents the unexpected value stored in std::expected.

// expected.cpp

#include
#include
#include
#include

std::expected getInt(std::string arg) {
try {
return std::stoi(arg);
}
catch (…) {
return std::unexpected{std::string(arg + “: Error”)};
}
}

int main() {

std::cout strings = {“66”, “foo”, “-5”};

for (auto s: strings) { // (1)
auto res = getInt(s);
if (res) {
std::cout The getInt function converts each string to an integer and returns a std::expected back. int stands for the expected and std::string for the unexpected value. The two range-based for loops (lines 1 and 2) iterate through the std::vector<:string>. The first range-based for loop (line 1) displays the expected (line 3) or unexpected value (line 4). In the second range-based for loop (line 2), the expected or the default value 2023 (line 5) is displayed.

std::exptected supports monadic operations for convenient function composition: exp.and_then, exp.transform, exp.or_else and exp.transform_error.

exp.and_then returns the result of the specified function call if it exists, or an empty std::expected.exp.transform returns a std::expected containing the transformed value, or an empty std::expected.exp. or_else returns the std::expected if it contains a value, or the result of the specified function otherwise. exp.transform_error returns the expected value of the std::expected if it exists. If not, it returns the transformed unexpected value of the std::expected.

The following program is based on the previous optionalMonadic.cpp program. Essentially, I replaced the std::optional data type with std::expected.

See also  Refurbish boom: value of drawer phones underestimated

// expectedMonadic.cpp

#include
#include
#include
#include

std::expected getInt(std::string arg) {
try {
return std::stoi(arg);
}
catch (…) {
return std::unexpected{std::string(arg + “: Error”)};
}
}

int main() {

std::cout strings = {“66”, “foo”, “-5”};

for (auto s: strings) {
auto res = getInt(s)
.transform( [](int n) { return n + 100; })
.transform( [](int n) { return std::to_string(n); });
std::cout The range-based for loop iterates through the std::vector<:string>. First, the getInt function converts each string to an integer, adds 100 to it, converts it back to a string, and finally displays it. If the initial conversion to an integer fails, the string arg + “:Error” is returned and displayed.

The four associative containers std::flat_map, std::flat_multimap, std::flat_set and std::flat_multiset in C++23 are a simple replacement for the ordered associative containers std::map,std::multimap, std:: set and std::multiset. In C++23 we have them for one reason: performance. (map)

To home page

You may also like

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More

Privacy & Cookies Policy