What could be the good way to check if N optionals returning from different functions all have values

Dmitry

Let say I have a code like that

#include <string>
#include <optional>
#include <charconv>

std::optional<int> get_1(const std::string& s) {  return s == "A" ? 1 : std::optional<int>{}; }
std::optional<const char*> get_2(const std::string& s) {  return s == "A" ? "2" : std::optional<const char*>{}; }
std::optional<double> get_3(const std::string& s) {  return s == "A" ? 3.0 : std::optional<double>{}; }
std::optional<int64_t> get_4(const std::string& s) {  return s == "A" ? -10000000000 : std::optional<int64_t>{}; }
std::optional<std::string> get_5(const std::string& s) {  return s == "A" ? "5" : std::optional<std::string>{}; }
// ..... more functions like that

struct S {
    int a1;
    const char* a2;
    double a3;
    int64_t a4;
    std::string a5;
private:
    int AGR_INIT_IS_NOT_ALLOWED{0};
};

std::optional<S> getS() {
    S s;
    // Now I'd like to return std::nullopt if any of the following calls return std::nullopt
    // The naive way to do that is smth like that

    if (auto val = get_1("A"); val) {
        s.a1 = *val;
    } else {
        return std::nullopt;
    }

    if (auto val = get_2("A"); val) {
        s.a2 = *val;
    } else {
        return std::nullopt;
    }

    if (auto val = get_3("A"); val) {
        s.a3 = *val;
    } else {
        return std::nullopt;
    }

    if (auto val = get_4("A"); val) {
        s.a4 = *val;
    } else {
        return std::nullopt;
    }

    if (auto val = get_5("A"); val) {
        s.a5 = *val;
    } else {
        return std::nullopt;
    }
    
    return s;
}

int main()
{
    return !getS();
}

Now my question is, can anyone advice on how to get rid of code replication inside getS function? Any pattern or smth that would work here?

The first obvious "solution" is instead of returning an optional just throw an exception inside get_* and then catch it in getS

#include <string>
#include <optional>
#include <charconv>
#include <exception>

int get_1(const std::string& s) {  return s == "A" ? 1 :throw std::exception{};}
const char* get_2(const std::string& s) {  return s == "A" ? "2" : throw std::exception{}; }
double get_3(const std::string& s) {  return s == "A" ? 3.0 : throw std::exception{}; }
int64_t get_4(const std::string& s) {  return s == "A" ? -10000000000 : throw std::exception{}; }
std::string get_5(const std::string& s) {  return s == "A" ? "5" : throw std::exception{}; }
// ..... more functions like that

struct S {
    int a1;
    const char* a2;
    double a3;
    int64_t a4;
    std::string a5;
private:
    int AGR_INIT_IS_NOT_ALLOWED{0};
};

std::optional<S> getS() {
    S s;
    try {
        s.a1 = get_1("A");
        s.a2 = get_2("A");
        s.a3 = get_3("A");
        s.a4 = get_4("A");
        s.a5 = get_5("A");
    } catch (const std::exception&) {
        return std::nullopt;
    }
    return s;
}

int main()
{
    return !getS();
}

I can't use exceptions, so it's not a solution at all (not to mention it is silly to refactor all these methods). Though, for now to me it looks like the best option it terms of avoiding boilerplate.

The second "solution" I was thinking about is to put all the optionals into tuple first, check if all them hold values and then assign from the tuple like that:

#include <string>
#include <optional>
#include <tuple>


std::optional<int> get_1(const std::string& s) {  return s == "A" ? 1 : std::optional<int>{}; }
std::optional<const char*> get_2(const std::string& s) {  return s == "A" ? "2" : std::optional<const char*>{}; }
std::optional<double> get_3(const std::string& s) {  return s == "A" ? 3.0 : std::optional<double>{}; }
std::optional<int64_t> get_4(const std::string& s) {  return s == "A" ? -10000000000 : std::optional<int64_t>{}; }
std::optional<std::string> get_5(const std::string& s) {  return s == "A" ? "5" : std::optional<std::string>{}; }
// ..... more functions like that

struct S {
    int a1;
    const char* a2;
    double a3;
    int64_t a4;
    std::string a5;
private:
    int AGR_INIT_IS_NOT_ALLOWED{0};
};

std::optional<S> getS() {
    auto t = std::make_tuple(get_1("A"), get_2("A"), get_3("A") ,get_4("A") ,get_5("A"));
    if (!std::apply([](auto&&... vals){ return ([](auto&& v){ return v.has_value(); }(vals) && ...);}, t)) {
        return std::nullopt;
    }

    S s;
    s.a1 = *std::get<0>(t);
    s.a2 = *std::get<1>(t);
    s.a3 = *std::get<2>(t);
    s.a4 = *std::get<3>(t);
    s.a5 = *std::get<4>(t);
    return s;
}

int main()
{
    return !getS();
}

That's not nice as we've got to execute all the get_* functions, even if the first one alrede returned std::nullopt (I also don't like to use std::get here)

And here I pretty much ran out of any good ideas how to achieve "exception based approach" w/o any exceptions.

Mestkon

Reducing boilerplate and code duplication like this is still a place where macros are a valid solution, as there are no other language features available to do this in a sensible manner.

#include <optional>

std::optional<int> get1();
std::optional<int> get2();
std::optional<int> get3();
std::optional<int> get4();

struct S
{
    int i1, i2, i3, i4;
};

std::optional<S> get_s()
{
    #define ASSIGN_OR_FAIL(member, func) \
    { \
        auto opt_val = func(); \
        if (!opt_val) \
            return std::nullopt; \
                             \
        member = *opt_val; \
    } 

    S ret;

    ASSIGN_OR_FAIL(ret.i1, get1)
    ASSIGN_OR_FAIL(ret.i2, get2)
    ASSIGN_OR_FAIL(ret.i3, get3)
    ASSIGN_OR_FAIL(ret.i4, get4)

    return ret;

    #undef ASSIGN_OR_FAIL
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

What way could I add score with different values to my code?

Not Returning all values from billpayment check table from Quickbooks?

What is a good way of getting distinct secondary index values from rethinkdb?

Is there a way to have functions as values of a dictionary?

What is the best way to check all JSON properties values type?

What is the most elegant way to check if all values in a boolean array are true?

Returning values from functions in Python

returning multiple values from functions

What's a good way to implement a date filter in a list where the items have different timezones?

Is this a good way to check for undefined values in javascript?

What's a good way to unit test the main function that calls all the simple functions?

What is a good way to check if an observable is completed

Two matrix summation functions returning different values

SQL select values from a column by different ids that all have this value

i have checked all the indentations and i think everything is all good but my script is returning this error from my index.tsx file

Is it good to have a default implementation of all functions of all derived classes

check if the all values of array are different

Check array values are all different

What is the proper way to check multiple form values from the template in Angular?

What is a good way to cast and call variable argument functions from a map dynamically at runtime WITHOUT using an external library

CSS - returning different values from different browsers

What's the correct way of returning one of different result values in one func in Golang?

What could be a good way to maintain reusable objects in Processing?

what is a good way to efficiently store a list of c++ objects from different classes using Flatbuffers?

How to do type check in a way that two arguments have different values in TypeScritp?

Is there a way to have a computed property for optionals in Swift

returning values from decorated functions in python

Returning values from functions when efficiency matters

Returning values from functions in structures in Visual Basic