我想创建一些基本上应该包装它的参数的模板。该参数应该是一个任意函数调用,它通过一些模板元编程魔法包含前缀和后缀代码。
我想使用它如下:
auto result = try_call( some_vector.at(13) );
和try_call
将以某种方式定义它包围some_vector.at(13)
周围的try..catch块。像这样:
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有一些开销吗?我想避免任何不必要的开销。
答案 0 :(得分:5)
实际上,使用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()
时要执行的函数,并且它将被内联。不涉及任何开销。
答案 1 :(得分:0)
我玩弄了这个;而且,这就是我想出的可能的解决方案。
// 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中的错误。
事实上,我建议使用这个调试版本来实际让你的程序崩溃就像在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);