是否可以在没有 RTTI 的情况下向上转换已擦除的类型?

问题描述 投票:0回答:1

我有一个

std::expected
的自定义实现,但是有一个类型擦除错误。它使预期类型看起来类似于异常。我们可以使用下一个代码:

Expected<int> V = 123;
V.SetError(std::string("Error occurred"));

当我们尝试从

Expected
获取值时,我们可以将存储的值输出到屏幕(如果它支持
std::string
或存储在
Expected
中的其他类型的某些函数重载)。

如果我们不想使用异常,我认为这是一个很好的做法。

但是,由其父类处理错误也很好,类似于

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
}

但是,我在我的项目中禁用了 RTTI,现在我不能使用

dynamic_cast
将存储的错误(如
void*
)转换为
MathError*
.

好的,我可以使用静态模板函数(1 个实例化类型 = 1 个类型 ID)为用作错误的每种类型创建一个单独的类型标识符。

但是,在这种情况下,如果它们的 ID 相同,我只能转换为确切的类型。也许像

std::bases
这样的东西会帮助我(我可以枚举所有类并列出它们的 ID),但标准中没有此功能。

那么,有机会吗?

c++ exception rtti type-erasure std-expected
1个回答
0
投票

我找到了不用 RTTI 解决它的好方法

我们可以声明可以使用这样的

expected
接口捕获的类。

首先声明

RuntimeError
的基类,添加返回自身类型id的静态函数

class RuntimeError
{
    static set<size_t> GetBaseIds()
    {
        return { TypeId::GetTypeId<RuntimeError>() };
    }
}

接下来,创建一个模板类,它将向中间父级添加一些东西:

template<typename Parent>
class DeriveError : Parent
{
   static set<size_t> GetBaseIds()
   {
      set<size_t> BaseIds = Parent::GetBaseIds();
      BaseIds.insert(TypeId::GetTypeId<Parent>());
      return BaseIds ;
   }
}

现在我们可以很容易地从 RuntimeError 导出错误。

class MathError : DeriveError<RuntimeError> {};
class ZeroDivisionError : DeriveError<MathError> {};

现在,当我们将错误设置为

expected
时,我们可以发送每个错误父类的所有ids:

    template<typename E>
    void SetError(E Error)
    {
        // Erased error handler
        ErrorHandler = make_unique<ErrorHandler<E>>(Error);

        if constexpr (is_base_of_v<RuntimeError, E>)
        {
            set<size_t> Bases = E::GetBaseIds();
            Bases.Add(TypeId::GetTypeId<E>());
            // Send parent ids to error handler
            ErrorHandler->SetBases(Bases);
        }
    }

捕捉方法:

    // avoid catching if not derived from RuntimeError
    template<typename E>
    typename enable_if<is_base_of_v<RuntimeError, E>, const E*>::type
    Catch()
    {
        const int32 ErrorTypeId = TypeId::GetTypeId<E>();
        // Error handler can return NULL if ErrorTypeId not matches any type id
        return static_cast<const E*>(ErrorHandler->TryCatchErrorByTypeId(ErrorTypeId));
    }

现在我们可以设置和捕获错误:

Expected Value = 1234;
Value.SetError(ZeroDivisionError>());

if (auto Error = Value.Catch<RuntimeError>())
{
    cout << "Error caught: " << Error.ToString();
}

© www.soinside.com 2019 - 2024. All rights reserved.