使用Lambda/Template/SFINAE自动化蹦床功能的try/catch-safeguarding

P i*_*P i 6 c++ lambda trampolines template-meta-programming c++11

我有100个左右的蹦床功能.我想知道是否可以在try/catch块中自动包装每个.

请提前通知,这不是一个简单的问题.我将从描述(简化)代码的问题开始,然后尝试在下面尽可能地回答它,以便读者可以看到我在哪里.

Foo有一个函数指针表:

编辑:这是一个C函数指针表.所以它可以接受static W::w.
签名在这里:http://svn.python.org/projects/python/trunk/Include/object.h

编辑:我在这里试过一个测试用例:

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; }
    :
}
Run Code Online (Sandbox Code Playgroud)

如果某个特定的插槽是"已加载",则会将其加载到其中:

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;} 
}
Run Code Online (Sandbox Code Playgroud)

我试图使用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)->float { return get_base(s)->f54(c,d,e); }
    }
Run Code Online (Sandbox Code Playgroud)

但是,这无法捕获错误.我需要在return语句周围添加一个try/catch:

try{ return get_base(s)->f53(a,b); } catch{ return 42; }
Run Code Online (Sandbox Code Playgroud)

然而,这会造成很多混乱.如果我能这样做会很好:

return trap( get_base(s)->f53(a,b); )
Run Code Online (Sandbox Code Playgroud)

我的问题是:有没有办法编写这个trap函数(不使用#define)?


这是我到目前为止所提出的:

我认为这会传递所有必要的信息:

trap<int, &Base::f53>(s,a,b)
Run Code Online (Sandbox Code Playgroud)

陷阱的定义可能如下所示:

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); 
    }
}
Run Code Online (Sandbox Code Playgroud)

这可能允许非常干净的语法:

class Foo {
    :
    void load53() { table->fp_53 = &trap<int,   &Base::f53>; }
    void load54() { table->fp_54 = &trap<float, &Base::f54>; }
}
Run Code Online (Sandbox Code Playgroud)

在这一点上,我甚至不确定是否违反了某些法律. table->fp_53必须是有效的C函数指针.

传入非静态成员函数(&Base::f53>)的地址不会违反此规则,因为它是模板参数,并且不会影响签名trap

同样,...应该没问题,因为C允许varargs.

所以,如果这确实有效,可以清理吗?

我的想法是:

1)也许......应该作为包移回模板参数.
2)也许可以推导出陷阱的返回类型,并保存一个模板参数

3)该Base::Func模板参数是非法语法.我怀疑它甚至不接近合法的东西.这可能会破坏整个方法.

Pio*_*cki 5

#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); 
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

用法:

table->fp_53 = &trap<decltype(&Base::f53), &Base::f53>::call;
table->fp_54 = &trap<decltype(&Base::f54), &Base::f54>::call;
Run Code Online (Sandbox Code Playgroud)

DEMO


注意: std::forward虽然Args不是转发引用本身,但仍然可以使用.

  • @Pi不,没有宏,有[没办法](http://stackoverflow.com/a/27374370/3953764) (3认同)