У меня около 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
Параметр шаблона имеет неверный синтаксис. И я подозреваю, что это даже близко не к чему-то законному. Что может сорвать весь подход.
#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
не является ссылкой для пересылки.
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)
и т.п.
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;
}