繁体   English   中英

在现代C ++中使用try..catch块通过模板元编程包装任意函数调用

[英]Wrap arbitrary function call via template metaprogramming with try..catch block in modern C++

我想创建一些模板,它实际上应该包装它的参数。 该参数应该是一个任意函数调用,它可以通过带有前缀和后缀代码的模板元编程魔术来包装。

我想像这样使用它:

auto result = try_call( some_vector.at(13) );

try_call将以某种方式定义,它将try..catch块包装在some_vector.at(13)周围。 像这样:

template<typename T>
// some template metaprogramming magic here
try {
    auto value = // execute the parameter here, i.e. some_vector.at(13);
    return std::experimental::optional<T>(value);
} 
catch (std::exception&) {
    return std::experimental::nullopt;
}

有Bjarne Stroustrup的论文,但这并不能准确描述我的需求,因此我无法找到解决该问题的方法。

如果这不可能直接实现,那么我目前正在考虑通过带有lambda的模板化函数来实现:

template<typename Func>
auto try_call(Func f) {
    try {
        return f();
    } catch(std::exception&) {
        return std::experimental::nullopt;
    }
}

但是我不知道这是个好主意。 我猜lambda有一些开销吗? 我想避免任何不必要的开销。

实际上,您使用lambda的解决方案是非常有效的。 从类型理论的角度来看, try_call是一个高阶函数:它将另一个函数作为参数,并在try catch上下文中执行它。

template<typename Func>
auto try_call(Func f) -> std::experimental::optional<std::decay_t<decltype(f())>> {
    try {
        return std::experimental::make_optional(f());
    } catch(std::exception&) {
        return std::experimental::nullopt;
    }
}

用lambda调用它会产生您想要的东西而没有任何开销。 使用重载的函数调用运算符将​​lambda编译为匿名结构。 该结构用作try_call函数的模板参数。 因此,编译器在调用f()时完全知道要执行的函数,它将被内联。 不涉及开销。

我玩这个; 这就是我想出一个可能的解决方案的方法。

// Function
template<typename ReturnType, typename... Args, typename... UArgs>
ReturnType no_throw_call(ReturnType (*f)(Args...), UArgs... args)
{
    try { return f(args...); }
    catch (...) {}
    return ReturnType();
}

// Method
template<typename ReturnType, typename Class, typename... Args, typename... UArgs>
ReturnType no_throw_call_method(Class &c, ReturnType(Class::*m)(Args...), UArgs... args)
{
    try { return (c.*m)(args...); }
    catch (...) {}
    return ReturnType();
}

注意:如果在类型之间存在允许的转换时,如果类型与函数的签名不完全匹配,则使用UArgs可以自动转换类型。

同样,此代码保证返回默认的初始化值。

我建议如果使用这种类型的代码,则应在catch块中包括某种错误记录。 这可能会向客户隐藏您的错误,但是您不想向开发人员和质量检查人员隐藏您的错误。

实际上,我建议您使用此版本的调试版本来使您的程序真正崩溃,就像在QA测试期间所做的那样。 否则,您的客户可能会遇到一些不好的未定义行为,因为您没有发现错误是因为您隐藏了它们。

用法示例:

#define nt_strcpy(X,Y) no_throw_call(strcpy, (X), (Y))
nt_strcpy(nullptr, nullptr);

B b;
// this calls a method named do_it which takes an int as a parameter and returns void
no_throw_call_method<void>(b, &B::do_it, 1);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM