error handling for this method?

Hi

I'm writing a library where one of the methods of a class has the following signature:

 
  UniqueID findCommonIDs(UniqueID first_id, UniqueID second_id) const;


where UniqueID is a custom type (perhaps an unsigned int).

It is valid for the method to return no UniqueID and so I have therefore changed the method to use std::optional:

 
  std::optional<UniqueID> findCommonIDs(UniqueID first_id, UniqueID second_id) const;


However, it is possible that the method may fail because the parameters, first_id and second_id, are UniqueIDs which have not been registered in the class (to which the method belongs) i.e. the user inserted a non-valid UniqueID. In this case, how should I handle this error?

A couple of options come to mind:

1. Just return the std::option with a null value. I don't like this approach as it doesn't distinguish between where a parameter is invalid and where there is simply no UniqueID to return (which is a valid result).

2. Use exceptions. Is a bad parameter supplied by the user exceptional?

3. Try to return some type of error code. I'm not sure how this would work in combination with the std::optional. Perhaps something like std::variant might help here?

4. anything else?

Thanks
One option is something like:

std::optional<UniqueID> findCommonIDs(UniqueID first_id, UniqueID second_id, bool& error) const;

The caller provides a bool ref, and if the function changes it to true, there was an error; bad parameters or some such.
Is a bad parameter supplied by the user exceptional?

https://en.cppreference.com/w/cpp/error/invalid_argument

i.e. the user inserted a non-valid UniqueID. In this case, how should I handle this error?

I’d go with exceptions, but let me add as a personal preference I’d prefer not to have not properly initialized objects around, if possible.
I mean, if it might happen that your UniqueIDs aren’t correctly initialized, not only some (many? all?) of your methods, but perhaps also some standalone functions, even some you’ll add in the future, should be aware of this and ready to deal with it.
It looks like it can make your code harder to write and manage.

- - -
Before std::optional were introduced, it was not uncommon to see things like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct RetVal {
    bool valid { false };
    int val {};
};


RetVal myfunc( int par_1, int par_2 )
{
    // ...
    if ( /*something*/ ) {
        return // a RetVal with valid == false (and val == 0)
    }
    // ...
    return ... // a RetVal with valid == true (and val == a proper value)
}


int main()
{
    auto var { myfunc( 13, 666 ) };
    if (var.valid) {
        //...
    }
}


You could adjust the above scheme to deal with different error codes:
1
2
3
4
struct RetVal {
    int valid {};   // 0 == valid; 1 == something; 2 == something else
    int val {};
};


- - -
Let me insist you could take into consideration the idea of preventing your not properly initialized object from being passed around.
Or you could at least ask your objects to state their status clearly, maybe by means of some conversion method.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyUniqueID {
public:
    // ...
    operator bool() const
    {
        // If my invariants have not been properly initialized:
        if (/* something */) {
            return false;
        }
        return true;
    }
private:
    // ...
};


MyUniqueID doSomething( const MyUniqueID& myd )
{
    if (!myd) {
        // myd is in an invalid state
    }
    // ...
}

Or:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyClass {
public:
    // ...
    MyUniqueID doSomething()
    {
        if (!my_state) {
            // ...
        }
        // ...
    }

private:
    // ...
    MyUniqueID one;
    MyUniqueID two;
    bool my_state { false };    // set to true when both 'one' and 'two' are valid
};

Topic archived. No new replies allowed.