Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

standard template for returning a value with a boolean result flag

As I am beginning to take advantage of the C++17 structured bindings and if operator init statements for more elegant function result reporting and checking, I started doing the following, if accordance with C++ Core Guideline F21:

std::pair<bool, int>Foo()
{
    return {true, 42}; //true means that function complete with no error and that 42 is a good value
}

void main(void)
{
    if (auto [Result, Value] = Foo(); Result)
    {
        //Do something with the return value here
    }
}

Then, of course, I though that it would be nice to have a reusable template for such return types so that nobody has to duplicate bool portion of the pair:

template <typename T> using validated = std::pair<bool,T>;

validated<int> Foo()
{
    return {true, 42};
}

void main(void)
{
    if (auto [Result, Value] = Foo(); Result)
    {
        //Do something with the return value here
    }
}

This works great for me, but now I am wondering if there is some sort of standard equivalent of this template so that I don't have to reinvent the wheel and define it myself. Seems like an arbitrary type value coupled with a validity flag would be a useful construct, but I could not find anything in standard library. Am I missing something?

like image 319
Regus Pregus Avatar asked Nov 13 '18 14:11

Regus Pregus


2 Answers

std::optional is exactly what you are asking about. It's even in the description:

A common use case for optional is the return value of a function that may fail. As opposed to other approaches, such as std::pair<T,bool>, optional handles expensive-to-construct objects well and is more readable, as the intent is expressed explicitly.

The if from the example would look a bit more straightforward:

#include <optional>
#include <iostream>

std::optional<int> Foo(bool fail)
{
    if (!fail) return {42};
    return {};
}

void process(bool fail) {
    if (auto val = Foo(fail)) {
        std::cout << val.value() << '\n';
    } else {
        std::cout << "No value!\n";
    }    
}

int main() {
    std::optional<int> oi;
    process(true);
    process(false);
}

If you really wished to use Value explicitly then you can always unpack it via reference on a successful branch i.e. auto Value = val.value();

You need to beware of some caveats. 2 from the top of my head:

  1. Performance: Why is the construction of std::optional<int> more expensive than a std::pair<int, bool>? although for the given example up-to-date clang with -O3 looks pretty convicing

    Note: static was added for process for brevity - to prevent generation of version for external linking.

  2. It will return false if object was default constructed. That might surprise some, default construction of optional doesn't default construct underlying value.

EDIT: After the comments I decided to explicitly state that there isn't anything like type alias for pair<T,bool> or similar compatible with standard library. It's not easy to prove something does not exist, but if there would such a type the standard library would most certainly used it in declaration of insert, it doesn't; hence, I strongly imply that there isn't any semantic wrapper around it.

like image 127
luk32 Avatar answered Sep 23 '22 17:09

luk32


You might be interested in the proposed std::expected.

Its interface follows std::optional pretty closely. The major advantage of expected<T, E> over optional<T> is the ability to transport an error:

enum class errc {err1, err2, err3};

std::expected<int, errc> Foo()
{
  if (/* error condition 1 */)  return std::unexpected(errc::err1);

  // ... checking other error conditions

  return 42;  // no error condition (42 is a good value)
              // implicit conversion from `int` to `expected<int, errc>`
              // avoid boilerplate code
}

int main()
{
  auto q = Foo();

  if (q)
  {
    // Do something with the return value here
  }
}

You could also take a look at:

  • Functional exceptionless error-handling with optional and expected;
  • the standard proposal;
  • A possible implementation.

As a side note main() must return int.

like image 20
manlio Avatar answered Sep 20 '22 17:09

manlio