Здравствуйте, Клюев Александр, Вы писали:
КА>Мультиметоды и С++
КА>Авторы:
КА> Клюев Александр
Вот ещё одна возможная реализация мультиметодов на 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);
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 файл лепит, а это не очень хорошо
#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);