Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Traits, не для слабых умов =)


Автор: AtroX 18.8.2005, 11:53
Есть иерархия с базовым классом entity, у которого есть некоторые виртуальные функции, типа ProcessMsg(int msg,..), ProcessInit();

Хочется заменить наследование агрегацией, это нужно, чтобы при выполнении можно было подключать дополнительные обработчики для виртуальных функций entity. (Аналог: множественное виртуальное наследование)

Предполагается создание иерархии trait'ов, которым будут перенаправляться вызовы функций.

Должны выполняться требования:
1) trait должен уметь делать почти все, что и наследник entity
2) trait не должен обрабатывать функции, если это не требутеся (*)
3) Простой процесс инициализации trait (регистрации пожеланий по обработке функций entity и т.п.)
4) Простой процесс деспетчеризации вызовов

Можно сделать удобные define'ы или использовать механизм шаблонов.

(*) А то можно создать иерархию trait с теми же виртуальными функциями, что и у entity и в entity в каждой виртуальной функции пробегаться по массиву trait'ов и вызывать соответствующие функции - это НЕ катит =)

ОЧЕНЬ важно быстродействие, т.к. используется realtime!

Некоторые виртуальные функции entity вызываются десятки раз в секунду.

Автор: Chaos A.D. 18.8.2005, 13:11
Назвать себя человеком, хорошо знающим C++, мне не позволит ни скромность, ни знания, но что-то, на мой взгляд, странноват дизайн, предложенный тобой. Наверное я просто не понял, чего ты хочешь добиться, отказавшись от наследования? Тебе нужен механизм диспетчеризации в runtime, или что?

Автор: Void 18.8.2005, 22:20
Тоже не совсем понял, что требуется... Система подписки на события? Может, http://www.boost.org/doc/html/signals.html пойдет? Насчет быстродействия не знаю... Если это действительно hard-realtime, то вряд ли, но десятки вызовов в секунду выдюжит... в зависимости от тяжести самих ф-ций, конечно.

P.S. Имхо, термин traits употреблен не к месту.

Автор: bel_nikita 19.8.2005, 00:50
Ничего не понял smile
AtroX
Цитата
ОЧЕНЬ важно быстродействие, т.к. используется realtime!
Быстродействие уже теряется, т.к. используются виртуальные вызовы.
И причем тут real-time? Real-time - это не всегда есть быстродействие. Real-time - это прежде всего предсказуемость.

З.Ы.: немного оффтопа smile
Система реального времени (RTOS) это информационная система , в которой корректность выходной информация зависит не только от примененных алгоритмов, но и от моментов времени появления информации.
И система реального времени необязательно быстрая, как кто либо мог считать. Не следует удивляться, что RTOS медлительнее, чем обычные ОС. Например, система навигации корабля в начале могла бы показаться не системой реального времени, потому что скорость мала и обычно "достаточно" времени (порядка минут) для принятия решения. Тем не менее, это действительно система реального времени.
Это, я так smile К тому, что не следует, размахиваться словосочитанием real-time, хотя и модное словечко smile

З.Ы.Ы.: не в обиду конечно smile

Автор: Hroft 19.8.2005, 12:17
Я чего-то не понял, или же после замены наследования на делегирование разные экземпляры entity будут по-разному себя вести, и уже не будет никакого наследования вообще?
Зачем нужна ИЕРАРХИЯ свойств? Их должен быть НАБОР. В чем проблема-то? Ну создавай сущность, задавай ей свойства, где тут виртуальные вызовы? Если только апгрейды всякие делать, но оно тем же самым агрегированием решается...
Не пойму, про что вообще топик...
Не дорос еще, наверно, тоже...

Автор: AtroX 19.8.2005, 13:24
Цитата(bel_nikita @ 19.8.2005, 00:50)
что не следует, размахиваться словосочитанием real-time, хотя и модное словечко

Курс ОС я прошел в полном объеме, так что я в этом разбираюсь ;)
real-time пишу, поскольку основная масса людей это понимает, как то, что от приложения требуется максимальная производительность и реактивность.

Цитата(Void @ 18.8.2005, 22:20)
Имхо, термин traits употреблен не к месту.

Traits назвал потому, что они позволяют Entity приобретать новые свойства.

Цитата(Hroft @ 19.8.2005, 12:17)
Ну создавай сущность, задавай ей свойства

И как это делать в runtime?

Цитата(Chaos @ 18.8.2005, 13:11)
Тебе нужен механизм диспетчеризации в runtime, или что?

Именно!

Нужно иметь простой способ (минимум кода), чтобы сделать перенаправление вызовов функций Entity в функции Trait (коих может быть несколько)

В общем то меня вчера посетило вдохновение и я написал, что-то подходящее, если интересно могу опубликовать.
Но все равное есть надежда на простое и элегантное решение, типа применить некую комбинацию паттернов и т.п.

Автор: Hroft 19.8.2005, 13:32
Цитата(AtroX @ 19.8.2005, 13:24)
Цитата (Hroft @ 19.8.2005, 12:17)
Ну создавай сущность, задавай ей свойства

И как это делать в runtime?

ПОДМЕНОЙ УКАЗАТЕЛЕЙ НА СВОЙСТВА! Именно таким образом агрегирование и может заменить наследование. И только таким (вроде).

Автор: AtroX 19.8.2005, 13:58
Цитата(Hroft @ 19.8.2005, 12:17)
Ну создавай сущность, задавай ей свойства, где тут виртуальные вызовы?


Цитата(Hroft @ 19.8.2005, 13:32)
ПОДМЕНОЙ УКАЗАТЕЛЕЙ НА СВОЙСТВА!


Надо было мне полнее процитировать первый раз, а то неувязочка получается. Меня собственно и заинтерисовало, как задавать свойства без механизма виртуальных вызовов. smile

Автор: Chaos A.D. 22.8.2005, 08:09
AtroX, было бы интересно посмотреть, как ты решил проблему.

Автор: Chaos A.D. 22.8.2005, 17:46
Вообще не совсем понял задачу, но может тебе поможет паттерн Visitor?

Автор: AtroX 22.8.2005, 18:31
Цитата(Chaos @ 22.8.2005, 08:09)
AtroX, было бы интересно посмотреть, как ты решил проблему.


Так оно применяется (что вполне меня устраивает):
main.cpp
Код

#include "entity.h"
#include "trait.h"

class SomeEntity1 : public BaseEntity {
public:
   virtual void f1(void) {
      Dispatch(f1);
      return ;
   }

   virtual int  f2(float p1) {
      Dispatch(f2, p1);
      return 0;
   }
};

class SomeEntity2 : public SomeEntity1 {
public:
   void f3(int p1) {
      Dispatch(f3, p1);
      return ;
   }
};

class SomeTrait1 : public BaseTrait {
public:
   virtual void f1(void) {
      return ;
   }

   virtual int  f2(float p1) {
      return 0;
   }

   virtual void AttachTo(BaseEntity *pEntity) {
      BaseTrait::AttachTo(pEntity);

      pEntity->Attach(SomeEntity1::f1, makeTraitFn(this, f1));
      pEntity->Attach(SomeEntity1::f2, makeTraitFn(this, f2));
   }

};

class SomeTrait2 : public SomeTrait1 {
public:
   virtual void f1() {
      return SomeTrait1::f1();
   }

   virtual void f3(int p1) {
      return ;
   }

   virtual void AttachTo(BaseEntity *pEntity) {
      SomeTrait1::AttachTo(pEntity);

      pEntity->Attach(SomeEntity2::f3, makeTraitFn(this, f3));
   }
};

void main ()
{
   SomeEntity2 e;
   BaseTrait *pTrait = 0;
   
   pTrait = new SomeTrait1;
   pTrait->AttachTo(&e);

   pTrait = new SomeTrait2;
   pTrait->AttachTo(&e);

   e.f1();   //SomeTrait1::f1, SomeTrait2::f1
   e.f2(2.f);//SomeTrait1::f2, SomeTrait1::f2
   e.f3(2);  //SomeTrait2::f3
}


Собственно то, что позволило так кратенько все записывать, хотя я думаю, что все это нафиг переделаю
MemberFn.h
Код

#pragma once
//
// functors for Member function
template <typename T> class BaseMemFn {
public:
   typedef T CLASS_T;

   BaseMemFn(CLASS_T *pC) : class_ptr(pC) { return ; }
   CLASS_T* GetClass(void) const          { return class_ptr; }

private:
   CLASS_T* class_ptr;// here must be smart_ptr with ref counting
};

//
// Functor with 1 param
template <typename T, typename R, typename P1 = void> class MemFn : public BaseMemFn<T> {
public:
   typedef R (T::*FN_T)(P1);
   FN_T fn;

   MemFn(T *pC, FN_T _fn) : BaseMemFn<T>(pC), fn(_fn) {}

   R operator()(P1 p1) {
      return (GetClass()->*fn)(p1);
   }
};

template <typename T, typename C, typename R, typename P1> MemFn<T,R,P1> makeFunctor(T* pClass, R (C::*fn)(P1))
{
   return MemFn<T,R,P1>(pClass, static_cast<MemFn<T,R,P1>::FN_T>(fn));
}

//
// Functor with 0 params
template <typename T, typename R> class MemFn<T,R, void> : public BaseMemFn<T> {
public:
   typedef R (T::*FN_T)();
   FN_T fn;

   MemFn(T *pC, FN_T _fn) : BaseMemFn<T>(pC), fn(_fn) {}

   R operator()() {
      return (GetClass()->*fn)();
   }
};

template <typename T, typename C, typename R> MemFn<T,R> makeFunctor(T* pClass, R (C::*fn)())
{
   return MemFn<T,R>(pClass, static_cast<MemFn<T,R>::FN_T>(fn));
}


entity.h
Код

#pragma once

#include <map>
#include <vector>
#include "member_fn.h"
//
// Dispatching
//
template <typename T> void *MakeVoidPtr(T t) {
   return *(void**)(&t);
}

class BaseTrait;
//
// BaseEntity
//
class BaseEntity {
public:
   typedef BaseEntity THIS_T;
   typedef BaseTrait  TRAIT_T;

   // pure virtual destructor
   virtual ~BaseEntity() = 0 { return ; }

   // dispatch call to functors, T - for using in derived classes
   template <typename T, typename R>              bool Dispatch(R (T::*fn)());
   template <typename T, typename R, typename P1> bool Dispatch(R (T::*fn)(P1), P1);

   // it's convenient for Entity and Trait fn's signatures check for compatibility, T - for using in derived classes
   template <typename T, typename R>              void Attach  (R (T::*fn)(),   MemFn<TRAIT_T,R>*    functor);
   template <typename T, typename R, typename P1> void Attach  (R (T::*fn)(P1), MemFn<TRAIT_T,R,P1>* functor);

private:
   typedef std::vector<BaseMemFn<TRAIT_T>*> FunctorList;
   typedef std::map<void*, FunctorList>     DispatchMap;

   DispatchMap map;

   void _Attach (void *fn, BaseMemFn<TRAIT_T>* functor) {
      map[fn].push_back(functor);
   }
};

//
// Dispatching
// Dispatch with 0 params
template <typename T, typename R> 
bool BaseEntity::Dispatch(R (T::*fn)())
{
   static_cast<THIS_T*>((T*)0);
   DispatchMap::const_iterator iter = map.find(MakeVoidPtr(fn));
   if (iter == map.end()) return false;
   const FunctorList& fnList = iter->second;

   for (unsigned i = 0; i < fnList.size(); i++) {
      //fnList[i] points to MemFn<TRAIT_T,R> it is guaranted by Attach func
      (*static_cast<MemFn<TRAIT_T,R>*>(fnList[i]))();
   }

   return true;
}

// Dispatch with 1 param
template <typename T, typename R, typename P1> 
bool BaseEntity::Dispatch(R (T::*fn)(P1), P1 p1)
{
   static_cast<THIS_T*>((T*)0);
   DispatchMap::const_iterator iter = map.find(MakeVoidPtr(fn));
   if (iter == map.end()) return false;
   const FunctorList& fnList = iter->second;

   for (unsigned i = 0; i < fnList.size(); i++) {
      //fnList[i] points to MemFn<TRAIT_T,R,P1> it is guaranted by Attach func
      (*static_cast<MemFn<TRAIT_T,R,P1>*>(fnList[i]))(p1);
   }

   return true;
}

//
// Attaching
// Attach with 0 params
template <typename T, typename R> 
void BaseEntity::Attach(R (T::*fn)(), MemFn<TRAIT_T,R>* functor) 
{
   static_cast<THIS_T*>((T*)0); 
   _Attach(MakeVoidPtr(fn), functor); 
}

// Attach with 1 param
template <typename T, typename R, typename P1> 
void BaseEntity::Attach (R (T::*fn)(P1), MemFn<TRAIT_T,R,P1>* functor) 

   static_cast<THIS_T*>((T*)0); 
   _Attach(MakeVoidPtr(fn), functor); 
}



trait.h
Код

#pragma once
class BaseEntity;

class BaseTrait {
public:
   ~BaseTrait()                                    { return; }
   virtual void AttachTo(BaseEntity *pEntity)  = 0 { pOwner = pEntity; }
   BaseEntity*  GetOwner(void) const               { return pOwner; }

private:
   BaseEntity *pOwner;
};

// MemFn<BaseTrait> functor with 1 param
template <typename C, typename R, typename P1> MemFn<BaseTrait,R,P1>* makeTraitFn(BaseTrait* pClass, R (C::*fn)(P1))
{
   return new MemFn<BaseTrait,R,P1>(makeFunctor(pClass, fn));
}

// MemFn<BaseTrait> functor with 0 params
template <typename C, typename R> MemFn<BaseTrait,R>* makeTraitFn(BaseTrait* pClass, R (C::*fn)())
{
   return new MemFn<BaseTrait,R>(makeFunctor(pClass, fn));
}


В принципе, можно обойтись без динамического выделения памяти в makeTraitFn.
Это была первая версия smile . Думаю, можно заюзать что-нибудь из boost'а, но я пока только начал его изучать. Хотя уже впечатлился набором полезностей, которые часто приходилось самому реализовывать.

Автор: AtroX 22.8.2005, 23:29
Цитата(AtroX @ 22.8.2005, 18:31)
В принципе, можно обойтись без динамического выделения памяти в makeTraitFn.

Для этого придется решить http://forum.vingrad.ru/index.php?showtopic=62156

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)