Using Lambda / Template / SFINAE to automate the protection of trampolines from attempts / capture

I have about 100 trampoline functions. I would like to know if it is possible to automate the wrapping of each of them inside a try / catch block.

Please be warned in advance, this is not an easy question. I will start by describing the problem with the (simplified) code and try to answer it as best as possible so that the reader can see where I am.

Foo has a function pointer table:

EDIT . This is table C <pointer> . Therefore, it can take static W::w .
Signatures here: http://svn.python.org/projects/python/trunk/Include/object.h

EDIT: I tried to run a test case here :

 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' is guaranteed } // Foo also has a method for loading a particular slot: : void load53() { table->fp_53 = func53; } void load54() { table->fp_54 = func54; } : } 

If a specific slot is β€œloaded”, this is what loads into it:

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

I am trying to accomplish this using lambdas to get around the need to define all of these func53 separately. Something like that:

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

However, this does not lead to errors. I need to put try / catch on the return statement:

 try{ return get_base(s)->f53(a,b); } catch{ return 42; } 

However, this creates a lot of confusion. It would be nice if I could:

 return trap( get_base(s)->f53(a,b); ) 

My question is: is there a way to write this trap function (without using #define)?


This is what I came up with so far:

I think this will convey all the necessary information:

 trap<int, &Base::f53>(s,a,b) 

a trap definition might look like this:

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

This can provide very clean syntax:

 class Foo { : void load53() { table->fp_53 = &trap<int, &Base::f53>; } void load54() { table->fp_54 = &trap<float, &Base::f54>; } } 

At this moment, I’m not even sure whether some laws have been violated. table->fp_53 must be a valid C function pointer.

Passing a non-static member function ( &Base::f53> ) to the address will not violate this, since it is a template parameter and does not affect the signature for trap

Likewise, ... should be fine, since C allows varargs.

So, if this is true, can it be cleaned?

My thoughts:

1), perhaps ... should be returned to the template parameter as a package.
2) it is possible to deduce the return type for the trap and save one template parameter

3) that the Base::Func template parameter is illegal syntax. And I suspect that he is not even close to something legal. Which can disrupt the whole approach.

+6
source share
3 answers
 #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); } } }; 

Using:

 table->fp_53 = &trap<decltype(&Base::f53), &Base::f53>::call; table->fp_54 = &trap<decltype(&Base::f54), &Base::f54>::call; 

Demo


Note. std::forward can still be used, although Args not the forwarding link itself.

+5
source

trap_gen is a function that returns a pointer to a function created on the fly, equivalent to your trap function.

This is how you use it

 table->fp_53 = trap_gen<>(Base::f53); table->fp_54 = trap_gen<>(Base::f54); ... 

Where Base::f53 and Base::f54 are static member functions (or function pointers or global functions in the namespace).

Proof of concept:

 #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
source
 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); } } }; 

Using:

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

Demo

Perhaps you can also use partial specialization to extract RET and Args from decltype(&base::f53) , etc.

+4
source

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


All Articles