使用Lambda / Template / SFINAE自动尝试/捕获保护蹦床功能 [英] Using Lambda/Template/SFINAE to automate try/catch-safeguarding of trampoline functions
问题描述
我有100个蹦床功能。我想知道是否可以自动将每一个包装在try / catch块中。
请提前警告,这不是一个容易的问题。我将首先描述(简化)代码的问题,然后尝试在下面最好地回答它,所以读者可能会看到我在哪里。
Foo有一个函数指针表:
EDIT :这是一个 C函数指针表。因此它可以接受 static W :: w
。
签名在这里: http://svn.python.org/projects/python/trunk/Include/object.h
编辑:我试过了一个测试用例 here : / p>
class Foo {
表table;
Foo(){
//每个插槽都有一个默认的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!;};
// ^注意:slots可能有不同的签名
//只保证第一个参数's s'
}
// Foo也有一个方法用于加载特定的槽:
:
void load53(){table-> fp_53 = func53; }
void load54(){table-> fp_54 = func54; }
:
}
是什么被加载到它:
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;}
}
$ b b
我试图使用lambdas来实现这一点,以便绕过必须分别定义所有这些 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) get_base(s)→f54(c,d,e); }
}
但是,这无法捕获错误。我需要在返回语句的try / catch:
try {return get_base(s) - > f53 a,b); } catch {return 42; }
但是,这会产生很多混乱。这将是很好,如果我能做:
返回trap(get_base(s) - > f53(a,b); )
我的问题是:有没有办法写这个 code> function(不使用#define)?
这是我到目前为止:
我认为这会传递所有必要的信息:
trap< int,& Base :: f53>(s,a,b)
然后看起来像这样:
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>
)不会违反这个,因为它是一个模板参数,并且不影响 / code>
同样, ...
应该没问题,因为C允许varargs。
所以如果这确实有效,可以清理吗?
我的想法是:
1)也许...应该被移回模板参数作为一个包。
2)也许可以推导出陷阱的返回类型,保存一个模板参数
3) Base :: Func
模板参数是非法语法。我怀疑它甚至不接近法律的东西。
#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&
table-> fp_54 =& trap< decltype(& Base :: f54),& Base :: f54&
> std :: forward
仍然可以使用,但 Args
本身不是转发引用。 p>
I have 100 or so trampoline functions. I would like to know whether it is possible to automate wrapping each one inside a try/catch block.
Please be warned in advance, this is not an easy question. I will start by describing the problem with (simplified) code, and will then attempt to answer it as best I can below, so the reader may see where I am at.
Foo has a function pointer table:
EDIT: This is a C function pointer table. So it could accept static W::w
.
Signatures are here: http://svn.python.org/projects/python/trunk/Include/object.h
EDIT: I've attempted a test case 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; }
:
}
If a particular slot is 'loaded', this is what gets loaded into it:
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;}
}
I am trying to accomplish this using lambdas, so as to bypass having to define all of these func53
separately. Something like this:
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); }
}
However, this is failing to trap errors. I need to be putting a try/catch around the return statement:
try{ return get_base(s)->f53(a,b); } catch{ return 42; }
However, this creates a lot of clutter. It would be nice if I could do:
return trap( get_base(s)->f53(a,b); )
My question is: is there any way to write this trap
function (without using #define)?
This is what I've come up with so far:
I think this would pass all the necessary information:
trap<int, &Base::f53>(s,a,b)
trap's definition could then look like this:
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);
}
}
This may allow for a very clean syntax:
class Foo {
:
void load53() { table->fp_53 = &trap<int, &Base::f53>; }
void load54() { table->fp_54 = &trap<float, &Base::f54>; }
}
At this point I'm not even sure whether some laws have been violated. table->fp_53
must be a valid C function pointer.
Passing in the address of a nonstatic member function (&Base::f53>
) won't violate this, as it is a template parameter, and is not affecting the signature for trap
Similarly, ...
should be okay as C allows varargs.
So if this is indeed valid, can it be cleaned up?
My thoughts are:
1) maybe the ... should be moved back to the template parameter as a pack.
2) maybe it is possible to deduce the return type for trap, and save one template parameter
3) that Base::Func
template parameter is illegal syntax. And I suspect it isn't even close to something legal. Which might scupper the whole approach.
#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);
}
}
};
Usage:
table->fp_53 = &trap<decltype(&Base::f53), &Base::f53>::call;
table->fp_54 = &trap<decltype(&Base::f54), &Base::f54>::call;
Note: std::forward
can still be used although Args
is not a forwarding reference itself.
这篇关于使用Lambda / Template / SFINAE自动尝试/捕获保护蹦床功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!