Design template for safe trampolines

This question follows from here . However, the previous question was formulated so poorly (actually erroneously) that it was suggested to ask again from scratch.

I have a table of pointers to C functions.

Some C-codes (let's call it lib-X) have a basic building block (let them call it an X-object). Each X object can call functions in this table.

These table functions usually have different signatures (see typedefs here ), although some functions may share the same signature. The table contains about 100 of these functions.

In C ++, I have an associated Final: Base class for every X object.

And I want to redirect these calls to the corresponding C ++ Final instance corresponding to the X-object, but I want to enclose this in try / catch, since the C ++ consumer can provide Final buggies.

So, I have a C ++ base class that has a virtual function for every record in the table.

Then I have a final C ++ class (maybe a lot: Final1 Final2 Final3, etc.) that comes from the base class.

So now I just need to write a handler that

  • Gets the first parameter "self" (which will always be a pointer to the X-object that called the function)

  • Gets the associated C ++ base class instance.

  • Inside the catch try block, the corresponding virtual function is called, forwarding all other parameters with

  • ... which will actually cause an override in Final.

This is a bit like trying to understand the plot for Inception. lib-X is actually a Python runtime, although I am trying to keep the general.

The fact is that there are dozens of these functions, and this leads to some very dirty and actionless C ++ code - if I need to manually write a trampoline function for each of them, which looks like this:

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

(source here )

I am trying to create a compact design that allows this safe batumina.

My current progress [REMOVED, see answer below]

+2
source share
2 answers

something like that?

 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
source

I got a job thanks to Peter answered my previous question , from which I removed the main mechanism (so please support his answer).

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!) } 
+3
source

Source: https://habr.com/ru/post/980658/


All Articles