代码之家  ›  专栏  ›  技术社区  ›  chmike

C++中的动态绑定

  •  10
  • chmike  · 技术社区  · 14 年前

    我正在实现一个类似corba的服务器。每个类都有可远程调用的方法和具有两个可能输入的分派方法,一个字符串标识该方法,另一个整数将作为表中方法的索引。字符串到相应整数的映射将由映射实现。

    调用者将在第一次调用时发送字符串,并用响应返回整数,这样它只需在随后的调用中发送整数。这只是一个小的优化。整数可以由服务器对象根据需要动态分配。 服务器类可以从另一个具有重写的虚拟方法的类派生。

    定义方法绑定和分派方法的简单通用方法是什么?

    编辑: 这些方法都有相同的签名(没有重载)。这些方法没有参数并返回布尔值。它们可能是静态的、虚拟的或非静态的,是否重写基类方法。绑定必须正确处理方法重写。

    字符串是类层次结构绑定的。如果a::foo()由字符串“a.foo”标识,而b类继承a并重写方法a::foo(),则仍将其标识为“a.foo”,但如果服务器是a对象,则调度程序将调用a::foo;如果服务器是b对象,则调用b::foo。

    编辑(4月6日): 换句话说,我需要用一个动态分派方法实现自己的虚拟方法表(vftable),该方法使用一个字符串键来标识要调用的方法。vftable应该在同一个类的对象之间共享,并按照多态性(继承的方法重写)的预期行为。

    编辑(4月28日): 请看下面我自己的答案和最后的编辑。

    9 回复  |  直到 14 年前
        1
  •  1
  •   Rakis    14 年前

    你考虑过使用boost::bind和boost::函数的组合吗?在这两个实用工具之间,您可以轻松地将任何C++调用的函数对象封装起来,并将它们轻松地存储在容器中,并且通常希望所有这些都能“正常工作”。作为一个例子,下面的代码示例的工作方式完全符合您的预期。

    #include <boost/bind.hpp>
    #include <boost/function.hpp>
    #include <iostream>
    using namespace std;
    
    struct A            { virtual void hello() { cout << "Hello from A!" << endl; } };
    struct B : public A { virtual void hello() { cout << "Hello from B!" << endl; } };
    
    int main( int argc, char * argv[] )
    {
        A a;
        B b;
        boost::function< void () > f1 = boost::bind( &A::hello, a );
        boost::function< void () > f2 = boost::bind( &A::hello, b );
        f1();  // prints: "Hello from A!"
        f2();  // prints: "Hello from B!"
        return 0;
    }
    
        2
  •  0
  •   Billy ONeal IS4    14 年前

    看起来你在寻找反射或委托之类的东西——我不太确定你想要完成什么,但最好的方法似乎是有一个函数指针的映射:

    typedef size_t (*CommonMethodPointerType)(const unsigned char *);
    std::map<std::string, CommonMethodPointerType> functionMapping;
    
    size_t myFunc(const std::string& functionName, const unsigned char * argument) {
        std::map<std::string, CommonMethodPointerType>::iterator functionPtrIterator
            = functionMapping.find(functionName);
        if (FunctionPtrIterator == functionMapping.end())
            return ERROR_CODE;
        return (*functionPtrIterator)(argument);
    }
    

    只要知道映射不会改变,就可以通过将迭代器返回给客户机来实现类似于整数的某种形式的优化。

    如果您正在寻找像C语言或动态语言(如PHP)中所允许的“动态绑定”,很不幸的是,您无法做到这一点——在编译代码时,C++会破坏类型信息。

    希望能有帮助!

        3
  •  0
  •   Billy ONeal IS4    14 年前

    您可能需要稍微修改这个问题,因为静态和动态绑定实际上在C++中有特定的含义。

    例如,参数的默认值是在编译时确定的,因此如果基类中有一个虚方法声明其参数的默认值,那么这些值是在编译时设置的。

    在运行时将忽略在派生类中声明的这些参数的任何新默认值,其结果是将使用基类中的默认参数值,即使在派生类中调用了成员函数。

    默认参数值被称为静态绑定。

    斯科特·迈耶斯在他那本好书中的一个项目中讨论了这个问题。” Effective C++ “。

    高温高压

        4
  •  0
  •   metasim    14 年前

    Qt4 有一个很好的动态绑定系统,可以通过他们的“元对象编译器”(MOC)实现。上面写着他们的 Qt Object Model page .

        5
  •  0
  •   Community rohancragg    7 年前

    下面是一种在Linux上从共享库动态加载类的方法 http://www.linuxjournal.com/article/3687?page=0,0

    还有一个stackoverflow问题 C++ Dynamic Shared Library on Linux

    在windows中也可以通过从dll动态加载c函数,然后再加载这些函数来实现。

    在获得动态加载解决方案之后,映射部分就变得微不足道了


    真正的好书James O. Coplien的C++编程习语和成语有一个关于增量加载的章节

        6
  •  0
  •   Community rohancragg    7 年前

    这是我实际方法的一个例子。它只是工作(c)但我很确定一个更干净和更好的方式存在。它按原样用g++4.4.2编译和运行。删除构造函数中的指令会很好,但我找不到实现这一点的方法。Dispatcher类基本上是一个可调度的方法表,每个实例的表上都必须有一个指针。

    注意:此代码将隐式地使所有分派的方法成为虚拟的。

    #include <iostream>
    #include <map>
    #include <stdexcept>
    #include <cassert>
    
    // Forward declaration
    class Dispatchable;
    
    //! Abstract base class for method dispatcher class
    class DispatcherAbs
    {
    public:
        //! Dispatch method with given name on object
        virtual void dispatch( Dispatchable *obj, const char *methodName ) = 0;
    
        virtual ~DispatcherAbs() {}
    };
    
    //! Base class of a class with dispatchable methods
    class Dispatchable
    {
    public:
        virtual ~Dispatchable() {}
    
        //! Dispatch the call
        void dispatch( const char *methodName )
        {
            // Requires a dispatcher singleton assigned in derived class constructor
            assert( m_dispatcher != NULL );
            m_dispatcher->dispatch( this, methodName );
        }
    
    protected:
        DispatcherAbs *m_dispatcher; //!< Pointer on method dispatcher singleton
    };
    
    //! Class type specific method dispatcher
    template <class T>
    class Dispatcher : public DispatcherAbs
    {
    public:
        //! Define a the dispatchable method type
        typedef void (T::*Method)();
    
        //! Get dispatcher singleton for class of type T
        static Dispatcher *singleton()
        {
            static Dispatcher<T> vmtbl;
            return &vmtbl;
        }
    
        //! Add a method binding
        void add( const char* methodName, Method method )
            { m_map[methodName] = method; }
    
        //! Dispatch method with given name on object
        void dispatch( Dispatchable *obj, const char *methodName )
        {
            T* tObj = dynamic_cast<T*>(obj);
            if( tObj == NULL )
                throw std::runtime_error( "Dispatcher: class mismatch" );
            typename MethodMap::const_iterator it = m_map.find( methodName );
            if( it == m_map.end() )
                throw std::runtime_error( "Dispatcher: unmatched method name" );
            // call the bound method
            (tObj->*it->second)();
        }
    
    protected:
        //! Protected constructor for the singleton only
        Dispatcher() { T::initDispatcher( this ); }
    
        //! Define map of dispatchable method
        typedef std::map<const char *, Method> MethodMap;
    
        MethodMap m_map; //! Dispatch method map
    };
    
    
    //! Example class with dispatchable methods
    class A : public Dispatchable
    {
    public:
        //! Construct my class and set dispatcher
        A() { m_dispatcher = Dispatcher<A>::singleton(); }
    
        void method1() { std::cout << "A::method1()" << std::endl; }
    
        virtual void method2() { std::cout << "A::method2()" << std::endl; }
    
        virtual void method3() { std::cout << "A::method3()" << std::endl; }
    
        //! Dispatcher initializer called by singleton initializer
        template <class T>
        static void initDispatcher( Dispatcher<T> *dispatcher )
        {
            dispatcher->add( "method1", &T::method1 );
            dispatcher->add( "method2", &T::method2 );
            dispatcher->add( "method3", &T::method3 );
        }
    };
    
    //! Example class with dispatchable methods
    class B : public A
    {
    public:
        //! Construct my class and set dispatcher
        B() { m_dispatcher = Dispatcher<B>::singleton(); }
    
        void method1() { std::cout << "B::method1()" << std::endl; }
    
        virtual void method2() { std::cout << "B::method2()" << std::endl; }
    
        //! Dispatcher initializer called by singleton initializer
        template <class T>
        static void initDispatcher( Dispatcher<T> *dispatcher )
        {
            // call parent dispatcher initializer
            A::initDispatcher( dispatcher );
            dispatcher->add( "method1", &T::method1 );
            dispatcher->add( "method2", &T::method2 );
        }
    };
    
    int main( int , char *[] )
    {
        A *test1 = new A;
        A *test2 = new B;
        B *test3  = new B;
    
        test1->dispatch( "method1" );
        test1->dispatch( "method2" );
        test1->dispatch( "method3" );
    
        std::cout << std::endl;
    
        test2->dispatch( "method1" );
        test2->dispatch( "method2" );
        test2->dispatch( "method3" );
    
        std::cout << std::endl;
    
        test3->dispatch( "method1" );
        test3->dispatch( "method2" );
        test3->dispatch( "method3" );
    
        return 0;
    }
    

    这是程序输出

    A::method1()
    A::method2()
    A::method3()
    
    B::method1()
    B::method2()
    A::method3()
    
    B::method1()
    B::method2()
    A::method3()
    

    编辑(4月28日): 答案是 this related question 很有启发性。使用带有内部静态变量的虚拟方法比使用需要在构造函数中初始化的成员指针变量更可取。

        7
  •  0
  •   Blaisorblade    14 年前

    我看过你的例子和对另一个问题的回答。但如果你谈到m_u dispatcher成员,情况就大不相同了。

    对于最初的问题,没有办法遍历类的方法。只能使用宏删除add(“method”,t::method)中的重复:

    #define ADD(methodname) add(#methodname, T::methodname)
    

    其中,“”将把methodname转换为类似required的字符串(根据需要展开宏)。对于类似命名的方法,这将删除潜在的输入错误源,因此这是非常理想的。

    唯一的方法来列出方法名称IMHO是通过解析“NM”(Linux上,甚至在Windows上通过BINUTILS端口)在这样的文件(你可以要求它解散C++符号)的输出。如果要支持此功能,则可能需要在单独的源文件中定义initdispatcher以自动生成。没有比这更好的方法了,是的,这可能是丑陋或完美的取决于你的限制。顺便说一句,它还允许检查作者没有重载方法。不过,我不知道是否可以过滤公共方法。

    我在回答关于a和b的构造器中的行的问题。我认为这个问题可以用奇怪的重复模板模式来解决,应用于dispatchable:

    template <typename T>
    class Dispatchable
    {
    public:
        virtual ~Dispatchable() {}
    
        //! Dispatch the call
        void dispatch( const char *methodName )
        {
            dispatcher()->dispatch( this, methodName );
        }
    protected:
        static Dispatcher<T> dispatcher() {
            return Dispatcher<T>::singleton();
            //Or otherwise, for extra optimization, using a suggestion from:
            //http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12
            static Dispatcher<T>& disp = Dispatcher<T>::singleton();
            return disp;
        }
    };
    

    免责声明:我无法测试编译这个(我远离编译器)。您可能需要转发声明调度器,但是由于它得到模板参数,所以我认为依赖参数的查找使得不必要的(我不足够的C++ Guru来确保这一点)。

    为了方便起见,我添加了一个dispatcher()方法,如果其他地方需要的话(否则可以在dispatch()中内联它)。

    crtp在这里如此简单,在另一个线程中如此复杂的原因是您的成员在这里不是静态的。我首先想到让它成为静态的,然后我认为没有理由保存对singleton()的调用结果并浪费内存,然后我查找它并找到这个解决方案。我怀疑dispatcher()中的额外引用是否节省了任何额外的时间。 在任何情况下,如果需要m_dispatcher成员,可以在dispatcheble()构造函数中初始化它。

    关于您的示例,由于initDispatcher()是一个模板方法,所以我坦率地怀疑是否有必要读取method1和method2。 A::initDispatcher(Dispatcher<B> dispatcher) 将正确地向Dispatcher添加B::Method1。

        8
  •  0
  •   Brent Arias    14 年前

    顺便说一句-不要忘记,从vtable调度的虚拟函数的数字位置与所有编译器在对应的头文件中出现的顺序一致。你也许可以利用这一点。这是微软COM技术的核心原则。

    另外,你可以考虑马克·德洛拉在《游戏编程宝石》(第一卷)中发表的一种方法。这篇文章的标题是 "generic function binding interface “用于函数的rpc/网络绑定。可能正是你想要的。

        9
  •  0
  •   Jerry Coffin    14 年前
    class Report   //This denotes the base class of C++ virtual function
    { 
    public: 
        virtual void create()   //This denotes the C++ virtual function
        { 
            cout <<"Member function of Base Class Report Accessed"<<endl; 
        } 
    };
    
    class StudentReport: public Report 
    { 
    public: 
        void create() 
        { 
            cout<<"Virtual Member function of Derived class StudentReportAccessed"<<endl; 
        } 
    };
    
    void main() 
    {
        Report *a, *b; 
        a = new Report(); 
        a->create(); 
        b = new StudentReport(); 
        b->create();     
    }