Design pattern for exception-safe trampolines

你说的曾经没有我的故事 提交于 2019-12-01 11:15:49

something like this?

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

I got it working thanks to Piotr's answer to my previous question, from which I have lifted the core machinery (so please upvote 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!)
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!