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
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
atoiconverts a C-style string to anint, and returns0if it fails. The problem is that0is also a valid integer. Since there's no value of of anintthat can't be the result of successful conversion there's no way to tell just from looking at the return value ifatoifailed or not.Your
Errorablehas the same problem. For instance, if you were to try to write your ownatoiusingErrorableyou 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 astd::optional<int> my_atoi(const char*)that can return any value in the entire range of anintand allows you to determine if the function failed to process its input or not.