Search code examples
c++option-type

Is using a wrapper with a 'check' function a good approach to optional types rather than the traditional method?


languages like C++ and Rust have an Option type, which is basically an enum/bool with a value.

But I feel like this can have a few problems like:

  • the extra overhead of returning/passing an extra field with your type
  • breaking ABI compatibility if you decide that a function should return an optional instead of the raw value.

So why not instead of putting a bool with the value, have a function associated with the type to check if the value returned is valid or not.

for example:

template<typename T, bool(*ISOK)(T&)>
struct Errorable
{
    private:
    T val;

    public:
    Errorable(T v)
    {
        val = v;
    }
    
    bool is_ok()
    {
        return ISOK(val);
    }

    bool is_err()
    {
        return !ISOK(val);
    }

    T unwrap_or(T o)
    {
        if(ISOK(val))
            return val;
        return o;
    };

    T unwrap_forced()
    {
        return val;
    }
};

which can be used like:

bool idx_ok(int& idx)
{
    return idx >= 0;
}

Errorable<int, idx_ok> array_idx_of(int *arr, int len, int elm)
{
    for(int i = 0 ; i < len ; i++)
    {
        if(arr[i] == elm)
        {
            Errorable<int, idx_ok> ok(i);
            return ok;
        }
    }
    Errorable<int, idx_ok> err(-1);
    return err;
}

and then

int main()
{
    int arr[5] = {1, 2, 3, 4, 5};
    auto idx = array_index_of(arr, 5, 6);
    if(idx.is_ok())
    { ... }
}

sorry if my C++ is bad, I'm a C programmer who needed templates for a sec


Solution

  • The approach you gave requires that the returned object has some sort of invalid state, but not all objects have such a state.

    As an example, the C function atoi converts a C-style string to an int, and returns 0 if it fails. The problem is that 0 is also a valid integer. Since there's no value of of an int that can't be the result of successful conversion there's no way to tell just from looking at the return value if atoi failed or not.

    Your Errorable has the same problem. For instance, if you were to try to write your own atoi using Errorable you couldn't do any better than the existing implementation.

    The value of std::optional (and Option types in general) is that they avoid this problem by decoupling the existence of the object from the value of the object. You could write a std::optional<int> my_atoi(const char*) that can return any value in the entire range of an int and allows you to determine if the function failed to process its input or not.