Дизайн шаблона для исключительных безопасных батутов

Этот вопрос следует из Вот. Тем не менее, предыдущий вопрос был сформулирован настолько плохо (на самом деле неправильно), что было предложено задать вопрос с нуля.

У меня есть таблица указателей на C-функции.

У некоторого C-кода (назовем его lib-X) есть базовый строительный блок (назовем его X-объектом). Каждый X-объект может вызывать функции в этой таблице.

Эти табличные функции обычно имеют разные подписи (см. Typedefs Вот), хотя несколько функций могут использовать одну и ту же подпись. В таблице около 100 таких функций.

В C ++ у меня есть связанный Final: Base класс для каждого X-объекта.

И я хочу перенаправить эти вызовы соответствующему экземпляру C ++ Final в X-объекте, но я хочу заключить это в try / catch, так как потребитель C ++ может предоставить глючный Final.

Итак, у меня есть базовый класс C ++, в котором есть виртуальная функция для каждой записи в таблице.

Затем у меня есть финальный класс C ++ (возможно, много; Final1 Final2 Final3 и т. Д.), Производный от базового класса.

Так что теперь мне просто нужно написать обработчик, который

  1. Получает первый параметр «self» (который всегда будет указателем на X-объект, вызвавший функцию)

  2. Извлекает связанный экземпляр базового класса C ++.

  3. В блоке try catch вызывает соответствующую виртуальную функцию, пересылая все оставшиеся параметры через

  4. … который фактически вызовет переопределение в Final.

Это немного похоже на попытку понять сюжет для начала. lib-X на самом деле является средой исполнения Python, хотя я стараюсь придерживаться общих принципов.

Дело в том, что есть десятки этих функций, и это делает для некоторого очень грязного и не поддерживаемого кода C ++ — если мне нужно вручную написать функцию батута для каждой, похожую на:

extern "C" PyObject *call_handler( PyObject *self, PyObject *args, PyObject *kw )
{
try
{
PythonExtensionBase *p = getPythonExtensionBase( self );
if( kw != NULL )
return new_reference_to( p->call( Object(args), :Object(kw) ) );
else
return new_reference_to( p->call( Object(args), Object() ) );
}
catch( Py::Exception & )
{
return NULL; // indicate error
}
}

(источник Вот)

Я пытаюсь придумать компактный дизайн, позволяющий безопасно прыгать на батутах.

Мой текущий прогресс [УДАЛЕНО, см. Ответ ниже]

2

Решение

что-то вроде этого?

template<typename RET, class ...Args> // <-- one trap for each f in Base that gets enabled!
RET trap( RET (Base::*f)(Args...), void* self, Args&&...args )
{
try {
auto base = reinterpret_cast<Base*>(self);
return (base->*f)(std::forward<Args>(args)...);
}
catch (...) {
return (RET)0;
}
}
3

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

Я получил это работает благодаря Ответ Петра на мой предыдущий вопрос, из которого я поднял основной механизм (поэтому, пожалуйста, подтвердите его ответ).

Coliru Вот

#include <iostream>
#include <typeinfo>

class Base {
public:
virtual int   func_1( int a )        { std::cout << "Base::func_1" << std::endl; return a; }
virtual float func_2( int a, int b ) { std::cout << "Base::func_2" << std::endl; return a+b; }
virtual float func_3( char a )       { std::cout << "Base::func_3" << std::endl; return (float)a; }
};

class Final : public Base {
public:
int   func_1( int a )           override { std::cout << "Final::func_1" << std::endl; return a+1000; }
//float func_2( int a, int b )    override { std::cout << "Final::func_2" << std::endl; return a*b; }
float func_3( char a )          override { std::cout << "Final::func_3" << std::endl; throw 666; }
};

Base* get_base(void* s) {
return reinterpret_cast<Base*>(s);
}

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(void* s, Args... args)
{
std::cout << "trap:" << typeid(t).name() << std::endl;
try
{
return (get_base(s)->*t)(std::forward<Args>(args)...);
}
catch (...)
{
std::cout << "CAUGHT" << std::endl;
return std::is_integral<R>::value ? static_cast<R>(-42) : static_cast<R>(-3.14);
}
}
};#define TRAP(f)  & trap<decltype(&f), &f>::call

class Trampoline
{
using F1 = auto ( void* self, int a )         -> int;
using F2 = auto ( void* self, int a, int b )  -> float;
using F3 = auto ( void* self, char a )        -> float;

struct Table {
F1* fp_1;
F2* fp_2;
F3* fp_3;
};
public:
Table* table = new Table();

void enable_f1() { table->fp_1 = TRAP( Base::func_1 ); }
void enable_f2() { table->fp_2 = TRAP( Base::func_2 ); }
void enable_f3() { table->fp_3 = TRAP( Base::func_3 ); }
};

int main()
{
Trampoline trampoline{};

trampoline.enable_f1();
trampoline.enable_f2();
trampoline.enable_f3();

Final final{};

void* base_as_pvoid = (void*)static_cast<Base*>(&final);

// test
int u    = trampoline.table->fp_1( base_as_pvoid, 2 );     std::cout << u << std::endl; // expect: 1002   (enabled and Final provides override)
float v  = trampoline.table->fp_2( base_as_pvoid, 3, 5 );  std::cout << v << std::endl; // expect: 8      (enabled but no override)
float w  = trampoline.table->fp_3( base_as_pvoid, 'x' );   std::cout << w << std::endl; // expect: -3.14  (enabled and Final provides override, which throws!)
}
3

По вопросам рекламы [email protected]