Использование Lambda / Template / SFINAE для автоматизации защиты батута от попыток / улова

У меня около 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;}
}

Я пытаюсь сделать это, используя лямбды, чтобы обойти необходимость определения всех этих 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); }
}

Тем не менее, это не удается перехватить ошибки. Мне нужно поместить try / catch в оператор return:

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 позволяет varargs.

Так что, если это действительно так, можно ли это убрать?

Мои мысли:

1) может быть … следует вернуться к параметру шаблона в виде пакета.
2) возможно, можно определить тип возвращаемого значения для ловушки и сохранить один параметр шаблона

3) что Base::Func Параметр шаблона имеет неверный синтаксис. И я подозреваю, что это даже близко не к чему-то законному. Что может сорвать весь подход.

6

Решение

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

DEMO


Замечания: std::forward все еще можно использовать хотя Args не является ссылкой для пересылки.

5

Другие решения

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

демонстрация.

Вы также можете использовать частичную специализацию для извлечения RET а также Args от decltype(&base::f53) и т.п.

4

trap_gen это функция, которая возвращает указатель на функцию, сгенерированную на лету, эквивалент вашего 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;
}
4
По вопросам рекламы [email protected]