Мультиметоды и С++
От: Клюев Александр  
Дата: 24.07.03 04:54
Оценка: 336 (12) +1
Статья:
Мультиметоды и С++
Автор(ы): Клюев Александр
Дата: 19.07.2003
Мультиметоды — виртуальные функции, принадлежащие сразу нескольким классам. В статье разбирается суть мультиметодов, их особенности, а также возможная реализация мультиметодов на C++.


Авторы:
Клюев Александр

Аннотация:
Мультиметоды — виртуальные функции, принадлежащие сразу нескольким классам. В статье разбирается суть мультиметодов, их особенности, а также возможная реализация мультиметодов на C++.
Re: Мультиметоды и С++
От: WoldemaR Россия  
Дата: 24.07.03 18:44
Оценка:
Здравствуйте, Клюев Александр, Вы писали:
КА>Мультиметоды и С++

КА>Авторы:

КА> Клюев Александр

Вот ещё одна возможная реализация мультиметодов на C++:
#define DEF_ID(n)\
    enum ID {ID = n};\
    virtual int GetID()\
    {    return ID;    };

// Коллекция классов:
struct GObject
{
    DEF_ID(0)
    virtual ~GObject(){};
};

struct GPoint : GObject
{
    DEF_ID(1)
    virtual ~GPoint(){};
};

struct GLine : GObject
{
    DEF_ID(2)
    virtual ~GLine(){};
};

struct GShape : GObject
{
    DEF_ID(3)
    virtual ~GShape(){};
};

#pragma warning (disable : 4786)
#include <map>

// Тип мультиметода:
typedef GShape* (*PJoinFunc)(GObject* a, GObject* b);

// Карта мультиметодов:
struct CFuncMap : std::map<std::pair<int, int>, PJoinFunc>
{
    CFuncMap();
};

// Хелпер для мультиметода:
template<class T1, class T2>
struct CpMultiFunc
{
    static GShape* Join(T1* a, T2* b);
    static void Add2Map(CFuncMap &FnMap)
    {
        FnMap[std::make_pair(T1::ID, T2::ID)] = (PJoinFunc)&Join;
    };
};

// Формирование карты мультиметодов
CFuncMap::CFuncMap()
{
    CpMultiFunc<GPoint, GPoint>::Add2Map(*this);
    CpMultiFunc<GPoint, GLine>::Add2Map(*this);
    CpMultiFunc<GLine, GLine>::Add2Map(*this);
};

// абстрактный мультиметод, вызывает конкретный метод:
GShape* Join(GObject* a, GObject* b)
{
    static CFuncMap FnMap;
    CFuncMap::iterator it = FnMap.find(std::make_pair(a->GetID(), b->GetID()));

    if (it != FnMap.end())
        return (it->second)(a, b);
    return 0;
};

// создает отрезок через две точки
GShape* CpMultiFunc<GPoint, GPoint>::Join(GPoint* a, GPoint* b)
{
    return new GShape();
};
// объединяет точку и отрезок в треугольник
GShape* CpMultiFunc<GPoint, GLine>::Join(GPoint* a, GLine* b)
{
    return new GShape();
};
// соединяет линиями концы отрезков и создает четырехугольник
GShape* CpMultiFunc<GLine, GLine>::Join(GLine* a, GLine* b)
{
    return new GShape();
};


Проверим так:
    GObject g;
    GPoint    p;
    GLine    l;


    GObject *p1 = &l;
    GObject *p2 = &l;


    delete Join(p1, p2);
Re[2]: Мультиметоды и С++
От: yaroslav_v http://yaroslav-v.chat.ru
Дата: 25.07.03 11:45
Оценка: 52 (8)
WR>Вот ещё одна возможная реализация мультиметодов на C++:
WR>#define DEF_ID(n)\
WR>    enum ID {ID = n};\
WR>    virtual int GetID()\
WR>    {    return ID;    };

WR>// Коллекция классов:
WR>struct GObject
WR>{
WR>    DEF_ID(0)
WR>    virtual ~GObject(){};

WR>};


Можно для определения типа использовать такой трюк (я его придумал пару лет назад,
мы его давно включили в нашу библиотеку):

#define DEF_ID()\
 virtual int GetID() const { return classtype(); } \
 static int classtype() { return (int)classtype; }

// преобразование к (int) можно не делать если GetID бы возвращал "const void*",
// так в общем и надо делать - тут я просто сделал упрощение

struct GObject
{
  DEF_ID();
  virtual ~GObject(){};
};


Т.е. не надо каждый раз придумывать константы — в качестве уникального идентификатора класса
используется адрес статической функции, который вычисляется автоматически.

Казалось бы, что в принципе якобы linker может все функции слить в одну,
но на самом деле не может, т.к. например по Строустропу (гл.13), что адрес глобального объекта
обьявленый как "extern" может использоваться как аргумент шаблона.
Словом тут linker ничего поделать не может и для каждого класса генерирует
разные функции, и таким образом получается автоматическая идентифичация класса.
Кстати, а почему бы "typeid()" не использовать — я то его не использовал из-за того, что
RTTI все имена классов в *.exe файл лепит, а это не очень хорошо
Re: Реализация на исключениях
От: WoldemaR Россия  
Дата: 01.08.03 12:17
Оценка: 8 (2)
#define THROW_THIS()\
virtual void ThrowThis()\
{    throw this;        };

// Колекция классов:
struct GObject
{
    THROW_THIS();
};

struct GPoint : GObject
{
    THROW_THIS();
};

struct GLine : GObject
{
    THROW_THIS();
};

struct GShape : GObject
{
    THROW_THIS();
};

GShape* Join(GObject* a, GObject* b)
{
    return 0;
};

// варианты мультиметодов:
GShape* Join(GPoint* a, GPoint* b)
{
    return 0;
};
GShape* Join(GPoint* a, GLine* b)
{
    return 0;
};
GShape* Join(GLine* a, GLine* b)
{
    return 0;
};
GShape* Join(GLine* a, GPoint* b)
{
    return 0;
};

// Макры для классов:
#define CALL_JOIN_FOR1(class_name)\
catch(class_name *p)\
{    return CallJoin(p, p2);}

#define CALL_JOIN_FOR2(class_name)\
catch(class_name *p)\
{    return Join(p1, p);}

// Вторичный обработчик, вызывает конкретный мультиметод:
template <class T>
GShape* CallJoin(T *p1, GObject *p2)
{
    try 
    {
        p2->ThrowThis();
    }
    CALL_JOIN_FOR2(GPoint)
    CALL_JOIN_FOR2(GLine)
    CALL_JOIN_FOR2(GShape)
    CALL_JOIN_FOR2(GObject)
    return NULL;
};
// Первичный обработчик:
GShape* CallJoin(GObject *p1, GObject *p2)
{
    try
    {
        p1->ThrowThis();
    }
    CALL_JOIN_FOR1(GPoint)
    CALL_JOIN_FOR1(GLine)
    CALL_JOIN_FOR1(GShape)
    CALL_JOIN_FOR1(GObject)
    return NULL;
};



Проверим так:
    GObject g;
    GPoint    p;
    GLine    l;


    GObject *p1 = &l;
    GObject *p2 = &l;


    delete Join(p1, p2);
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.