6

我有 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; }
    :
}

如果一个特定的插槽被“加载”,这就是加载到其中的内容:

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;} 
}

我正在尝试使用 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); }
    }

但是,这无法捕获错误。我需要在 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)

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

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

同样,...应该没问题,因为 C 允许可变参数。

那么如果这确实是有效的,它可以被清理吗?

我的想法是:

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

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

4

3 回答 3

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); 
        }
    }
};

用法:

table->fp_53 = &trap<decltype(&Base::f53), &Base::f53>::call;
table->fp_54 = &trap<decltype(&Base::f54), &Base::f54>::call;

演示


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

于 2015-01-07T14:53:00.477 回答
4

trap_gen是一个函数,它返回指向动态生成的函数的函数指针,相当于你的trap函数。

这是你如何使用它

table->fp_53 = trap_gen<>(Base::f53);
table->fp_54 = trap_gen<>(Base::f54);
...

whereBase::f53Base::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;
}
于 2015-01-07T14:32:29.130 回答
4
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>; }

演示

您可能还可以使用部分专业化来提取RETArgs提取decltype(&base::f53)等。

于 2015-01-07T14:37:25.873 回答