异常安全蹦床的设计模式 [英] Design pattern for exception-safe trampolines

查看:147
本文介绍了异常安全蹦床的设计模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题来自这里。然而,上一个问题的措辞非常严重(事实上是错误的),建议我从头再次询问。



我有一个C函数指针表。 / p>

一些C代码(我们称之为lib-X)有一个基本的构建块(我们称之为X对象)。每个X对象可以调用此表上的函数。



这些表函数通常具有不同的签名(请参阅typedefs 这里),尽管几个函数可能共享相同的签名。在表中有大约100个这些函数。



在C ++中,每个X对象都有一个相关的Final:Base类。



我想将这些调用转发到X对象的相应的C ++ Final实例,但是我想把它包含在try / catch中,因为C ++的消费者可能提供一个Bug的Final。



所以我有一个C ++ Base类,对表上的每个条目都有一个虚函数。



然后我有一个C ++ Final



所以现在我只需要写一个处理程序


(可能很多; Final1 Final2 Final3等)


  1. 获取第一个'self'参数(它将始终是一个指向调用函数的X对象的指针)

  2. $


  3. 在try catch块中,调用相应的虚函数,转发所有


的剩余参数。




  • 这有点像试图了解Inception的情节。 lib-X实际上是Python运行时,虽然我想保持一般的东西。



    事情是有几十个这些函数,这使一些非常混乱和不可维护的C ++代码 - 如果我必须手动写一个trampoline函数为每个,看起来像:

      extern CPyObject * call_handler(PyObject * self,PyObject * args,PyObject * kw)
    {
    try
    {
    PythonExtensionBase * p = getPythonExtensionBase
    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; //表示错误
    }
    }

    (source 这里



    我试图想出一个紧凑的设计,允许这个异常安全的蹦床。



    我目前的进展是[REMOVED,请回答下面]

    解决方案

    由于

    Coliru http://coliru.stacked-crooked.com/a/c8cbf4ca2f5e9752 =nofollow>此处

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

    类最后: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;投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;
    使用F2 = auto(void * self,int a,int b) - >浮动;
    使用F3 = auto(void * self,char a) - >浮动;

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

    void enable_f1(){ta​​ble-> fp_1 = TRAP(Base :: func_1); }
    void enable_f2(){ta​​ble-> fp_2 = TRAP(Base :: func_2); }
    void enable_f3(){ta​​ble-> 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并且Final提供覆盖)
    float v = trampoline.table-> fp_2(base_as_pvoid,3,5); std :: cout<< v<< std :: endl; // expect:8(启用但不覆盖)
    float w = trampoline.table-> fp_3(base_as_pvoid,'x'); std :: cout<< w < std :: endl; // expect:-3.14(enabled,Final提供覆盖,它会抛出!)
    }


    This question follows from here. However, the previous question was worded so badly (wrongly in fact) that it was suggested I ask again from scratch.

    I have a table of C-function pointers.

    Some C code (let us call it lib-X) has a basic building block (let's call it a X-object). Each X-object can invoke functions on this table.

    These table functions generally have different signatures (see typedefs here), although it is possible for several functions to share the same signature. There are about 100 of these functions in the table.

    In C++ I have an associated Final:Base class for each X-object.

    And I want to forward these calls to the X-object's corresponding C++ Final instance, but I want to enclose this within try/catch, as the C++ consumer may provide a buggy Final.

    So I have a C++ Base class that has a virtual function for each entry on the table.

    I then have a C++ Final class (possibly many; Final1 Final2 Final3 etc) that derives from the base class.

    So now I just need to write a handler that

    1. Gets the first 'self' parameter (which will always be a pointer to the X-object that invoked the function)

    2. Retrieves the associated C++ base class instance.

    3. Within a try catch block, invokes the corresponding virtual function, forwarding all the remaining parameters through,

    4. ... which will actually invoke the override in Final.

    It's a bit like trying to understand the plot for Inception. lib-X is in fact the Python runtime, although I'm trying to keep things general.

    The thing is there are dozens of these functions, and this makes for some very messy and unmaintainable C++ code -- if I have to manually write a trampoline function for each one, looking like:

    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'm trying to come up with a compact design that allows this exception-safe trampolining.

    My current progress is [REMOVED, see answer below]

    解决方案

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

    这篇关于异常安全蹦床的设计模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆