I have a custom implementation of std::expected
, but with a type-erased error. It makes the expected type looks similar to exceptions. We just can use the next code:
Expected<int> V = 123;
V.SetError(std::string("Error occurred"));
And when we try to get the value from Expected
, we can just output the stored value to screen (if it supports some function overloading for std::string
or another type that is stored in Expected
).
I think this is good practice if we don't want to use exceptions.
But, it would also be nice to handle the error by its parent class, similar to a try
/catch
:
struct MathError{};
struct ZeroDivisionError : MathError{};
Expected V = 321;
V.SetError(ZeroDivisionError{});
if (auto Error = V.Catch<MathError>())
{
// we go here if error is subclass of MathError
}
But, I have disabled RTTI in my project, and now I can't use dynamic_cast
to upcast the stored error (as void*
) to MathError*
.
Ok, I can make an individual type identifier for each type that is used as an error using a static template function (1 instantiated type = 1 type id).
But, in this case, I can only cast to the exact type if their ids are the same. Maybe something like std::bases
would help me (I could enumerate all the classes and make a list of their ids), but this feature is not available in the standard.
So, is there any chance?
I found good solution to solve it without RTTI
We can declare class that can be caught by using such
expected
interface.First, declare the base class of
RuntimeError
and add static function that returns type id of itself.Next, create a template class that will add some stuff to intermediate parent:
Now we can derive error from RuntimeError easily.
Now, when we set error to
expected
, we can send all ids of each error parent class:Catch method:
Now we can set and catch errors: