繁体   English   中英

C ++中的动态绑定

[英]Dynamic binding in C++

我正在实现一个像服务器一样的CORBA。 每个类都有远程可调用方法和一个带有两个可能输入的调度方法,一个标识方法的字符串或一个整数,它将是表中方法的索引。 字符串到相应整数的映射将由映射实现。

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

什么是定义方法绑定和调度方法的简单通用方法?

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

该字符串是类层次结构绑定。 如果我们有由字符串“A.foo”标识的A :: foo(),并且类B继承A并覆盖方法A :: foo(),它仍将被标识为“A.foo”,但是如果服务器是A对象,调度程序将调用A :: foo,如果它是B对象,则调用B :: foo。

编辑(6 apr):换句话说,我需要使用动态调度方法实现我自己的虚方法表(vftable),使用字符串键来标识要调用的方法。 vftable应该在同一个类的对象之间共享,并且与多态性的行为一样(继承的方法覆盖)。

编辑(28 apr):看下面我自己的答案和最后的编辑。

你考虑过使用boost :: bind和boost :: function的组合吗? 在这两个实用程序之间,您可以轻松地将任何C ++ callable包装在一个函数对象中,轻松地将它们存储在容器中,并且通常希望它们“正常工作”。 例如,以下代码示例的工作方式与您期望的完全相同。

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

看起来你正在寻找像反射或代理这样的东西 - 我不是百分之百确定你想要完成什么,但似乎最好的方法就是拥有一个函数指针映射:

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 ++在编译代码时会破坏类型信息。

希望有所帮助!

您可能会稍微改一下这个问题,因为静态和动态绑定实际上在C ++中具有特定含义。

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

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

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

Scott Meyers在他的优秀着作“ Effective C ++ ”中的一个项目中对此进行了讨论。

HTH

这是我的实际方法的一个例子。 它只是工作(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()

编辑(28 apr): 这个相关问题的答案很有启发性。 使用带有内部静态变量的虚方法比使用需要在构造函数中初始化的成员指针变量更可取。

Qt4有一个很好的动态绑定系统,可以通过他们的“元对象编译器”(moc)实现。 在他们的Qt对象模型页面上有一个很好的写作。

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

Linux上的这个C ++动态共享库还有一个stackoverflow问题

通过从DLL动态加载C函数然后加载它们,可以在Windows中完成相同的操作。

获得动态加载解决方案后,地图部分是微不足道的


James O. Coplien的高级C ++编程习语和成语非常好,有一个关于增量加载的部分

我已经看到了你的例子和另一个问题的答案。 但是如果你谈论m_dispatcher成员,情况就大不相同了。

对于原始问题,没有办法迭代类的方法。 您可能只使用宏删除add(“method”,T :: method)中的重复:

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

其中'#'将methodname转换为所需的字符串(根据需要扩展宏)。 在类似命名方法的情况下,这消除了潜在拼写错误的来源,因此恕我直言。

列出方法名称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;
    }
};

免责声明:我无法测试编译(我远离编译器)。 您可能需要转发声明Dispatcher,但由于它获得了模板参数,我认为依赖于参数的查找使得这不必要(我不足以确定这一点的C ++大师)。

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

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

关于你的例子,因为initDispatcher()是一个模板方法,我坦率地怀疑有必要读取method1和method2。 A::initDispatcher(Dispatcher<B> dispatcher)将正确地将B :: method1添加到调度程序。

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

顺便说一下 - 不要忘记从vtable调度的虚函数的数字位置与所有编译器完全对应于它们出现在相应头文件中的序列。 你也许可以利用它。 这是Microsoft COM技术所依据的核心原则。

此外,您可能会考虑Mark DeLoura在“Game Programming Gems”(第一卷)中发布的方法。 该文章的标题是“通用功能绑定接口 ”,用于RPC /网络绑定功能。 它可能正是你想要的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM