gpt4 book ai didi

c++ - 实现这个 "on error, throw"回调的最佳方式是什么?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:37:56 27 4
gpt4 key购买 nike

请注意:在我们开始真正的问题之前,这里有很多背景信息。

我有一个相当广泛的 C++ 类层次结构(代表不同类型的表达式):

class BaseValue { virtual ~BaseValue(); };
class IntValue final : public BaseValue { int get() const; };
class DoubleValue final : public BaseValue { double get() const; };
class StringValue final : public BaseValue { std::string get() const; };

另一方面,我有一种方法可以将用户的输入强制转换为预期的类型:

class UserInput { template<class T> get_as() const; };

所以编写匹配器的一种方法是——“用户的输入是否等于这个 BaseValue 的值?” — 会像这样:

class BaseValue { virtual bool is_equal(UserInput) const; };
class IntValue : public BaseValue {
int get() const;
bool is_equal(UserInput u) const override {
return u.get_as<int>() == get();
}
};
// and so on, with overrides for each child class...
bool does_equal(BaseValue *bp, UserInput u) {
return bp->is_equal(u);
}

但是,这不会缩放,无论是在“层次结构的宽度”方向还是在“操作数”方向。例如,如果我想添加 bool does_be_greater(BaseValue*, UserInput) ,这将需要一个完整的虚拟方法,其中 N 个实现分散在层次结构中。所以我决定改为走这条路:

bool does_equal(BaseValue *bp, UserInput u) {
if (typeid(*bp) == typeid(IntValue)) {
return static_cast<IntValue*>(bp)->get() == u.get_as<int>();
} else if (typeid(*bp) == typeid(DoubleValue)) {
return static_cast<DoubleValue*>(bp)->get() == u.get_as<double>();
...
} else {
throw Oops();
}
}

事实上,我可以做一些元编程并将其分解为一个函数 visit采用通用 lambda:

bool does_equal(BaseValue *bp, UserInput u) {
my::visit<IntValue, DoubleValue, StringValue>(*bp, [&](const auto& dp){
using T = std::decay_t<decltype(dp.get())>;
return dp.get() == u.get_as<T>();
});
}

my::visit作为“递归”函数模板实现:my::visit<A,B,C>简单测试 typeid反对A , 如果是,则调用 lambda,并调用 my::visit<B,C>如果不。在调用堆栈的底部,my::visit<C>针对 C 测试 typeid , 如果是的话调用 lambda,并抛出 Oops()如果没有。

好的,现在是我的实际问题!

my::visit 的问题错误行为“throw Oops()”是硬编码的。我真的更喜欢让错误行为由用户指定,如下所示:

bool does_be_greater(BaseValue *bp, UserInput u) {
my::visit<IntValue, DoubleValue, StringValue>(*bp, [&](const auto& dp){
using T = std::decay_t<decltype(dp.get())>;
return dp.get() > u.get_as<T>();
}, [](){
throw Oops();
});
}

我遇到的问题是,当我这样做时,我无法弄清楚如何以这样一种方式实现基类,即编译器将关闭不匹配的返回类型或从功能!这是没有 on_error 的版本回调:

template<class Base, class F>
struct visit_impl {
template<class DerivedClass>
static auto call(Base&& base, const F& f) {
if (typeid(base) == typeid(DerivedClass)) {
using Derived = match_cvref_t<Base, DerivedClass>;
return f(std::forward<Derived>(static_cast<Derived&&>(base)));
} else {
throw Oops();
}
}

template<class DerivedClass, class R, class... Est>
static auto call(Base&& base, const F& f) {
[...snip...]
};

template<class... Ds, class B, class F>
auto visit(B&& base, const F& f) {
return visit_impl<B, F>::template call<Ds...>( std::forward<B>(base), f);
}

这是我真正想要的:

template<class Base, class F, class E>
struct visit_impl {
template<class DerivedClass>
static auto call(Base&& base, const F& f, const E& on_error) {
if (typeid(base) == typeid(DerivedClass)) {
using Derived = match_cvref_t<Base, DerivedClass>;
return f(std::forward<Derived>(static_cast<Derived&&>(base)));
} else {
return on_error();
}
}

template<class DerivedClass, class R, class... Est>
static auto call(Base&& base, const F& f, const E& on_error) {
[...snip...]
};

template<class... Ds, class B, class F, class E>
auto visit(B&& base, const F& f, const E& on_error) {
return visit_impl<B, F>::template call<Ds...>( std::forward<B>(base), f, on_error);
}

也就是说,我希望能够处理这两种情况:

template<class... Ds, class B, class F>
auto visit_or_throw(B&& base, const F& f) {
return visit<Ds...>(std::forward<B>(base), f, []{
throw std::bad_cast();
});
}

template<class... Ds, class B>
auto is_any_of(B&& base) {
return visit<Ds...>(std::forward<B>(base),
[]{ return true; }, []{ return false; });
}

所以我想一种方法是编写基本案例的几个特化:

  • is_void_v<decltype(on_error())> , 使用 {on_error(); throw Dummy();}使编译器警告静音

  • is_same_v<decltype(on_error()), decltype(f(Derived{}))> , 使用 {return on_error();}

  • 否则,静态断言

但我觉得我缺少一些更简单的方法。谁能看到?

最佳答案

I guess one way to do it would be write several specializations of the base case

除此之外,您可以将“编译时分支”隔离到一个专门处理调用 on_error 的函数,并调用该新函数而不是 on_errorvisit_impl::call 中。

template<class DerivedClass>
static auto call(Base&& base, const F& f, const E& on_error) {
if (typeid(base) == typeid(DerivedClass)) {
using Derived = match_cvref_t<Base, DerivedClass>;
return f(std::forward<Derived>(static_cast<Derived&&>(base)));
} else {
return error_dispatch<F, Derived>(on_error);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
}
}

template <typename F, typename Derived, typename E>
auto error_dispatch(const E& on_error)
-> std::enable_if_t<is_void_v<decltype(on_error())>>
{
on_error();
throw Dummy();
}

template <typename F, typename Derived, typename E>
auto error_dispatch(const E& on_error)
-> std::enable_if_t<
is_same_v<decltype(on_error()),
decltype(std::declval<const F&>()(Derived{}))>
>
{
return on_error();
}

关于c++ - 实现这个 "on error, throw"回调的最佳方式是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44334895/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com