使用 Lambda/Template/SFINAE 实现蹦床功能的自动化 try/catch-safeguarding
Using Lambda/Template/SFINAE to automate try/catch-safeguarding of trampoline functions
我有 100 个左右的蹦床功能。我想知道是否可以自动将每个包装在 try/catch 块中。
请提前警告,这不是一个简单的问题。我将首先用(简化的)代码描述问题,然后将尝试在下面尽我所能回答它,因此 reader 可能会看到我所在的位置。
Foo 有一个函数指针 table:
编辑:这是一个C 函数指针 table。所以它可以接受 static W::w
.
签名在这里:http://svn.python.org/projects/python/trunk/Include/object.h
编辑:我尝试了一个测试用例 here:
class Foo {
Table table;
Foo() {
// Each slot has a default lambda.
:
table->fp_53 = [](S s, A a, B b) -> int {cout<<"load me!";};
table->fp_54 = [](S s, C c, D d, E e) -> float {cout<<"load me!";};
// ^ Note: slots MAY have different signatures
// only the first parameter 'S s' is guaranteed
}
// Foo also has a method for loading a particular slot:
:
void load53() { table->fp_53 = func53; }
void load54() { table->fp_54 = func54; }
:
}
如果特定插槽是 'loaded',这就是加载到其中的内容:
int func53(S s, A a, B b) {
try{
return get_base(s)->f53(a,b);
}
catch(...) { return 42;}
}
float func54(S s, C c, D d, E e) {
try{
return get_base(s)->f54(c,d,e);
}
catch(...) { return 3.14;}
}
我正在尝试使用 lambda 来完成此操作,以便绕过必须单独定义所有这些 func53
。像这样:
class Foo {
:
void load53() {
table->fp_53 =
[](S s, A a, B b)->int { return get_base(s)->f53(a,b); }
}
void load54() {
table->fp_54 =
[](S s, C c, D d, E e)->float { return get_base(s)->f54(c,d,e); }
}
但是,这是无法捕获错误。我需要在 return 语句周围放置一个 try/catch:
try{ return get_base(s)->f53(a,b); } catch{ return 42; }
但是,这会造成很多混乱。如果我能做到就好了:
return trap( get_base(s)->f53(a,b); )
我的问题是:有什么方法可以编写这个 trap
函数(不使用 #define)?
这是我到目前为止的想法:
我认为这会传递所有必要的信息:
trap<int, &Base::f53>(s,a,b)
trap 的定义可能如下所示:
template<typename RET, Base::Func>
static RET
trap(S s, ...) {
try {
return get_base(s)->Func(...);
}
catch {
return std::is_integral<RET>::value ? (RET)(42) : (RET)(3.14);
}
}
这可能允许一个非常干净的语法:
class Foo {
:
void load53() { table->fp_53 = &trap<int, &Base::f53>; }
void load54() { table->fp_54 = &trap<float, &Base::f54>; }
}
此时我什至不确定是否违反了某些法律。 table->fp_53
必须是有效的 C 函数指针。
传入非静态成员函数的地址 (&Base::f53>
) 不会违反这一点,因为它是模板参数,不会影响 trap
[=30= 的签名]
同样,...
应该没问题,因为 C 允许可变参数。
所以如果这确实有效,是否可以清理它?
我的想法是:
1) 也许 ... 应该作为一个包移回模板参数。
2)也许可以推导出陷阱的return类型,并保存一个模板参数
3) Base::Func
模板参数是非法语法。而且我怀疑它甚至不接近合法。这可能会破坏整个方法。
trap_gen
是一个函数,returns 一个指向动态生成的函数的函数指针,相当于您的 trap
函数。
这是你如何使用它
table->fp_53 = trap_gen<>(Base::f53);
table->fp_54 = trap_gen<>(Base::f54);
...
其中Base::f53
和Base::f54
是静态成员函数(或函数指针,或命名空间中的全局函数)。
概念验证:
#include <iostream>
template<typename R, class...A>
R (*trap_gen(R(*f)(A...)))(A...)
{
static auto g = f;
return [](A... a)
{
try {
return g(a...);
} catch (...) {
return std::is_integral<R>::value ? static_cast<R>(42)
: static_cast<R>(3.14);
}
};
}
int add(int a, int b)
{
return a+b;
}
int main() {
int(*f)(int, int) = trap_gen<>(add);
std::cout << f(2, 3) << std::endl;
return 0;
}
template<typename RET, typename... Args>
struct trap_base {
template<RET (Base::* mfptr)(Args...)>
static RET
trap(S s, Args... args) {
try {
return (get_base(s).*mfptr)(args...);
}
catch (...) {
return std::is_integral<RET>::value ? (RET)(42) : (RET)(3.14);
}
}
};
用法:
void load53() { table.fp_53 = &trap_base<int, int>::trap<&Base::f53>; }
void load54() { table.fp_54 = &trap_base<float, int, float>::trap<&Base::f54>; }
Demo.
您或许还可以使用偏特化从 decltype(&base::f53)
等中提取 RET
和 Args
#include <utility>
template <typename T, T t>
struct trap;
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(int s, Args... args)
{
try
{
return (get_base(s)->*t)(std::forward<Args>(args)...);
}
catch (...)
{
return std::is_integral<R>::value ? static_cast<R>(42)
: static_cast<R>(3.14);
}
}
};
用法:
table->fp_53 = &trap<decltype(&Base::f53), &Base::f53>::call;
table->fp_54 = &trap<decltype(&Base::f54), &Base::f54>::call;
注意: std::forward
仍然可以使用,尽管 Args
本身不是转发引用。
我有 100 个左右的蹦床功能。我想知道是否可以自动将每个包装在 try/catch 块中。
请提前警告,这不是一个简单的问题。我将首先用(简化的)代码描述问题,然后将尝试在下面尽我所能回答它,因此 reader 可能会看到我所在的位置。
Foo 有一个函数指针 table:
编辑:这是一个C 函数指针 table。所以它可以接受 static W::w
.
签名在这里:http://svn.python.org/projects/python/trunk/Include/object.h
编辑:我尝试了一个测试用例 here:
class Foo {
Table table;
Foo() {
// Each slot has a default lambda.
:
table->fp_53 = [](S s, A a, B b) -> int {cout<<"load me!";};
table->fp_54 = [](S s, C c, D d, E e) -> float {cout<<"load me!";};
// ^ Note: slots MAY have different signatures
// only the first parameter 'S s' is guaranteed
}
// Foo also has a method for loading a particular slot:
:
void load53() { table->fp_53 = func53; }
void load54() { table->fp_54 = func54; }
:
}
如果特定插槽是 'loaded',这就是加载到其中的内容:
int func53(S s, A a, B b) {
try{
return get_base(s)->f53(a,b);
}
catch(...) { return 42;}
}
float func54(S s, C c, D d, E e) {
try{
return get_base(s)->f54(c,d,e);
}
catch(...) { return 3.14;}
}
我正在尝试使用 lambda 来完成此操作,以便绕过必须单独定义所有这些 func53
。像这样:
class Foo {
:
void load53() {
table->fp_53 =
[](S s, A a, B b)->int { return get_base(s)->f53(a,b); }
}
void load54() {
table->fp_54 =
[](S s, C c, D d, E e)->float { return get_base(s)->f54(c,d,e); }
}
但是,这是无法捕获错误。我需要在 return 语句周围放置一个 try/catch:
try{ return get_base(s)->f53(a,b); } catch{ return 42; }
但是,这会造成很多混乱。如果我能做到就好了:
return trap( get_base(s)->f53(a,b); )
我的问题是:有什么方法可以编写这个 trap
函数(不使用 #define)?
这是我到目前为止的想法:
我认为这会传递所有必要的信息:
trap<int, &Base::f53>(s,a,b)
trap 的定义可能如下所示:
template<typename RET, Base::Func>
static RET
trap(S s, ...) {
try {
return get_base(s)->Func(...);
}
catch {
return std::is_integral<RET>::value ? (RET)(42) : (RET)(3.14);
}
}
这可能允许一个非常干净的语法:
class Foo {
:
void load53() { table->fp_53 = &trap<int, &Base::f53>; }
void load54() { table->fp_54 = &trap<float, &Base::f54>; }
}
此时我什至不确定是否违反了某些法律。 table->fp_53
必须是有效的 C 函数指针。
传入非静态成员函数的地址 (&Base::f53>
) 不会违反这一点,因为它是模板参数,不会影响 trap
[=30= 的签名]
同样,...
应该没问题,因为 C 允许可变参数。
所以如果这确实有效,是否可以清理它?
我的想法是:
1) 也许 ... 应该作为一个包移回模板参数。
2)也许可以推导出陷阱的return类型,并保存一个模板参数
3) Base::Func
模板参数是非法语法。而且我怀疑它甚至不接近合法。这可能会破坏整个方法。
trap_gen
是一个函数,returns 一个指向动态生成的函数的函数指针,相当于您的 trap
函数。
这是你如何使用它
table->fp_53 = trap_gen<>(Base::f53);
table->fp_54 = trap_gen<>(Base::f54);
...
其中Base::f53
和Base::f54
是静态成员函数(或函数指针,或命名空间中的全局函数)。
概念验证:
#include <iostream>
template<typename R, class...A>
R (*trap_gen(R(*f)(A...)))(A...)
{
static auto g = f;
return [](A... a)
{
try {
return g(a...);
} catch (...) {
return std::is_integral<R>::value ? static_cast<R>(42)
: static_cast<R>(3.14);
}
};
}
int add(int a, int b)
{
return a+b;
}
int main() {
int(*f)(int, int) = trap_gen<>(add);
std::cout << f(2, 3) << std::endl;
return 0;
}
template<typename RET, typename... Args>
struct trap_base {
template<RET (Base::* mfptr)(Args...)>
static RET
trap(S s, Args... args) {
try {
return (get_base(s).*mfptr)(args...);
}
catch (...) {
return std::is_integral<RET>::value ? (RET)(42) : (RET)(3.14);
}
}
};
用法:
void load53() { table.fp_53 = &trap_base<int, int>::trap<&Base::f53>; }
void load54() { table.fp_54 = &trap_base<float, int, float>::trap<&Base::f54>; }
Demo.
您或许还可以使用偏特化从 decltype(&base::f53)
等中提取 RET
和 Args
#include <utility>
template <typename T, T t>
struct trap;
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(int s, Args... args)
{
try
{
return (get_base(s)->*t)(std::forward<Args>(args)...);
}
catch (...)
{
return std::is_integral<R>::value ? static_cast<R>(42)
: static_cast<R>(3.14);
}
}
};
用法:
table->fp_53 = &trap<decltype(&Base::f53), &Base::f53>::call;
table->fp_54 = &trap<decltype(&Base::f54), &Base::f54>::call;
注意: std::forward
仍然可以使用,尽管 Args
本身不是转发引用。