提问人:Artem Selivanov 提问时间:4/19/2023 最后编辑:Remy LebeauArtem Selivanov 更新时间:4/20/2023 访问量:129
是否可以在没有 RTTI 的情况下向上发送擦除类型?
Is that possible to upcast erased type without RTTI?
问:
我有一个自定义实现,但有一个类型擦除错误。它使预期的类型看起来类似于异常。我们可以使用下面的代码: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 相同时,我才能转换为确切的类型。也许类似的东西会对我有所帮助(我可以枚举所有类并列出它们的 id),但此功能在标准中不可用。std::bases
那么,有机会吗?
答:
0赞
Artem Selivanov
4/20/2023
#1
我找到了很好的解决方案来解决没有RTTI的问题
我们可以声明可以使用此类接口捕获的类。expected
首先,声明 的基类并添加返回自身类型 id 的静态函数。RuntimeError
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> {};
现在,当我们将 error 设置为 时,我们可以发送每个错误父类的所有 id:expected
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();
}
评论
std::expected