这个问题来自here。然而,之前的问题措辞非常糟糕(实际上是错误的),建议我再次从头开始问。
我有一个C函数指针表。
一些C代码(我们称之为lib-X)有一个基本的构建块(让它称之为X对象)。每个X对象都可以在此表上调用函数。
这些表函数通常具有不同的签名(请参阅typedef here),尽管多个函数可以共享相同的签名。表中大约有100个这样的功能。
在C ++中,我为每个X对象都有一个关联的Final:Base类。
我想将这些调用转发给X-object的相应C ++ Final实例,但是我想把它放在try / catch中,因为C ++使用者可能会提供一个错误的Final。
所以我有一个C ++ Base类,它为表中的每个条目都有一个虚函数。
然后我有一个C ++ Final类(可能很多; Final1 Final2 Final3等)派生自基类。
所以现在我只需要编写一个
的处理程序获得第一个' self'参数(它始终是指向调用函数的X对象的指针)
检索关联的C ++基类实例。
在try catch块中,调用相应的虚函数,通过转发所有剩余参数,
...实际上会调用Final中的覆盖。
这有点像试图了解Inception的情节。 lib-X实际上是Python运行时,尽管我试图保持一般性。
事情是有很多这些函数,这使得一些非常混乱和不可维护的C ++代码 - 如果我必须手动为每个函数编写一个trampoline函数,看起来像:
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
}
}
(来源here)
我试图提出一种紧凑的设计,允许这种异常安全的蹦床。
我目前的进展是[删除,见下面的答案]
答案 0 :(得分:3)
这样的事情?
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;
}
}
答案 1 :(得分:3)
感谢我Piotr's answer to my previous question,我已经解除了核心机制(所以请提出他的回答)。
Coliru here
#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!)
}