2011-12-21 27 views
9

He estado tratando de encontrar una solución creativa a este problema (dentro y fuera) durante algún tiempo, pero todavía no he podido. Recientemente consideré que podría resolverse con metaprogramación de plantillas, aunque no estoy seguro debido a mi relativa falta de experiencia con la técnica.Contando con metaprogramación de plantillas?

¿Es posible utilizar la metaprogramación de plantillas (o cualquier otro mecanismo con el lenguaje C++) para contar el número de clases derivadas de alguna clase base de modo que cada clase derivada tenga un identificador de clase único y estático?

¡Gracias de antemano!

+0

¿Los ID estáticos son un requisito o los pusiste allí porque crees que son parte de la solución? una solución ideal para este problema probablemente no necesitaría estos identificadores, y se basaría en el sistema de tipos en su lugar. es decir, los nombres de clase en sí mismos servirían como los únicos identificadores estáticos. – wilhelmtell

+0

Estoy tratando de evitar RTTI y permanecer dentro de C++. –

+0

@ DanM.Katz: Tal vez he hecho lo que quieres. Publique aquí si todavía está interesado. –

Respuesta

6

No. Este es un problema que surge en la práctica mucho, y por lo que yo sepa sólo hay dos soluciones:

  1. asignar manualmente los ID a cada clase derivada.
  2. Generar dinámicamente y de forma diferida identificaciones no deterministically.

La forma de hacer que el segundo es algo como esto:

class Base 
{ 
    virtual int getId() const = 0; 
}; 

// Returns 0, 1, 2 etc. on each successive call. 
static int makeUniqueId() 
{ 
    static int id = 0; 
    return id++; 
} 

template <typename Derived> 
class BaseWithId : public Base 
{ 
    static int getStaticId() 
    { 
     static int id = makeUniqueId(); 
     return id; 
    } 

    int getId() const { return getStaticId(); } 
}; 

class Derived1 : public BaseWithId<Derived1> { ... }; 
class Derived2 : public BaseWithId<Derived2> { ... }; 
class Derived3 : public BaseWithId<Derived3> { ... }; 

Esto le da identificadores únicos para cada clase:

Derived1::getStaticId(); // 0 
Derived2::getStaticId(); // 1 
Derived3::getStaticId(); // 2 

Sin embargo, esas identificaciones se asignan con pereza, por lo el orden al que llama getId() afecta los ID devueltos.

Derived3::getStaticId(); // 0 
Derived2::getStaticId(); // 1 
Derived1::getStaticId(); // 2 

Independientemente de si esto está bien para su aplicación depende de sus necesidades particulares (por ejemplo, no sería bueno para la serialización).

+0

Sí, la serialización es mi objetivo. Siento que esto debería ser posible aunque ... –

0

Estoy publicando esto con mi problema en mente. Será una publicación larga. Estoy escribiendo un sistema de eventos, y quiero registrar eventos solo en un lugar.

----- ----- Event.h

typedef int EventAddress; 
typedef int EventId; 
typedef int EventType; 

static const EventAddress EVENT_FROM_ALL=-1; 
static const EventAddress EVENT_TO_ALL=-1; 

static const EventId EVENT_ID_INITIAL=-1; 
static const EventType EVENT_TYPE_INITIAL=-1; 

static const EventId EVENT_ID_ALL=0; 
static const EventType EVENT_TYPE_ALL=0; 

struct Event 
{ 
    public: 
     EventId eventId; 
     EventType eventType; 
     EventAddress from; 

     Event(const EventId eventId, const EventType eventType): 
      eventId(eventId), 
      eventType(eventType) 
     { 
     } 

     virtual ~Event() 
     { 
     } 

     virtual std::string asString()=0; 

    private: 
     Event(); 
}; 

template <class T> 
struct EventBase 
     :public Event 
{ 
    static int EVENT_ID; 
    static int EVENT_TYPE; 

    EventBase(): 
     Event(EVENT_ID,EVENT_TYPE) 
    { 
    } 
}; 
template <class T> 
int EventBase<T>::EVENT_ID=EVENT_ID_INITIAL; 

template <class T> 
int EventBase<T>::EVENT_TYPE=EVENT_TYPE_INITIAL; 

/// Events All 
struct EventAll: 
     public Event 
{ 
    static int EVENT_ID; 
    static int EVENT_TYPE; 

    EventAll(): 
     Event(EVENT_ID,EVENT_TYPE) 
    { 
    } 

    virtual std::string asString() 
    { 
     return __PRETTY_FUNCTION__; 
    } 
}; 

----- ----- Event.cpp

#include "Event.h" 

int EventAll::EVENT_ID=EVENT_ID_ALL; 
int EventAll::EVENT_TYPE=EVENT_TYPE_ALL; 

------ EventGenerator.h ------

struct EventIdGenerator 
{ 
    int generator; 
    EventIdGenerator(): 
     generator(0) 
    { 

    } 
}; 

template <class T, class Base> 
struct UnitId: 
     virtual public Base, 
     public T 
{ 
    UnitId() 
    { 
     ++Base::generator; 
     T::EVENT_ID=Base::generator; 
    } 
}; 

struct EventTypeGenerator 
{ 
    static int generator; 
}; 

template <class T, class Base> 
struct UnitType: 
     virtual public Base, 
     public T 
{ 
    UnitType() 
    { 
     T::EVENT_TYPE=Base::generator; 
    } 
}; 

----- ----- EventGenerator.cpp

#include "EventGenerator.h" 

int EventTypeGenerator::generator=0; 

Y no es la materia de la diversión ...

----- ----- EventsTank.h

#include <loki/Typelist.h> 
#include <loki/HierarchyGenerators.h> 

#include "Event.h" 
#include "EventGenerator.h" 

#define EVENT_CONTEXT__ Tank 

#define EVENT_NAME__ EventTank1 
struct EVENT_NAME__: 
     public EventBase<EVENT_NAME__> 
{ 
    std::string s; 
    double b; 
    void f() 
    { 
    } 

    virtual std::string asString() 
    { 
     return __PRETTY_FUNCTION__; 
    } 
}; 
#undef EVENT_NAME__ 



#define EVENT_NAME__ EventTank2 
struct EVENT_NAME__: 
     public EventBase<EVENT_NAME__> 
{ 
    std::string s; 
    double b; 
    void f() 
    { 
    } 

    virtual std::string asString() 
    { 
     return __PRETTY_FUNCTION__; 
    } 
}; 
#undef EVENT_NAME__ 



#define EVENT_NAME__ EventTank3 
struct EVENT_NAME__: 
     public EventBase<EVENT_NAME__> 
{ 
    std::string s; 
    double b; 
    void f() 
    { 
    } 

    virtual std::string asString() 
    { 
     return __PRETTY_FUNCTION__; 
    } 
}; 
#undef EVENT_NAME__ 

#define TOKENPASTE(x, y, z) x ## y ## z 
#define TOKENPASTE2(x, y, z) TOKENPASTE(x, y, z) 

#define EVENTS_ALL__ TOKENPASTE2(Events,EVENT_CONTEXT__,All) 


template <typename...Ts> 
struct TYPELIST; 

template <> 
struct TYPELIST<> 
{ 
    typedef Loki::NullType Result; 
}; 

template <typename HEAD, typename...Ts> 
struct TYPELIST<HEAD,Ts...> 
{ 
    typedef Loki::Typelist<HEAD, typename TYPELIST<Ts...>::Result> Result; 
}; 

typedef TYPELIST< 
     EventTank1, 
     EventTank2, 
     EventTank3 
    >::Result EVENTS_ALL__; 

/// Do not change below--------------------------------------------------------------------- 

#define EVENT_CONTEXT_ALL__ TOKENPASTE2(Event,EVENT_CONTEXT__,All) 
struct EVENT_CONTEXT_ALL__: 
     public EventBase<EVENT_CONTEXT_ALL__> 
{ 
    virtual std::string asString() 
    { 
     return __PRETTY_FUNCTION__; 
    } 
}; 

#define EVENT_ALL_REVERSED__ TOKENPASTE2(Event,EVENT_CONTEXT__,AllReversed) 
typedef Loki::TL::Reverse<EVENTS_ALL__>::Result EVENT_ALL_REVERSED__; 

#define EVENT_ALL_REVERSED_FIRST__ TOKENPASTE2(Event,EVENT_CONTEXT__,AllReversedFirst) 
typedef Loki::TL::TypeAt<EVENTS_ALL__,0>::Result EVENT_ALL_REVERSED_FIRST__; 

template <class Base> 
struct UnitType<EVENT_ALL_REVERSED_FIRST__,Base>: 
     virtual public Base, 
     public EVENT_ALL_REVERSED_FIRST__ 
{ 
    typedef EVENT_ALL_REVERSED_FIRST__ T; 
    UnitType() 
    { 
     std::cout << __PRETTY_FUNCTION__ << std::endl; 
     ++Base::generator; 
     T::EVENT_TYPE=Base::generator; 
     EVENT_CONTEXT_ALL__::EVENT_ID=EVENT_ID_ALL; 
     EVENT_CONTEXT_ALL__::EVENT_TYPE=Base::generator; 
    } 
}; 

#define ALL_CONTEXT_EVENTS__ TOKENPASTE2(All,EVENT_CONTEXT__,Events) 
typedef Loki::GenLinearHierarchy<EVENT_ALL_REVERSED__,UnitType,EventTypeGenerator> ALL_CONTEXT_EVENTS__; 

#undef ALL_CONTEXT_EVENTS__ 
#undef EVENT_ALL_REVERSED__ 
#undef EVENT_ALL_REVERSED_FIRST__ 
#undef EVENT_NAME_ALL__ 
#undef EVENTS_ALL__ 

----- ----- EventsTank.cpp

#include "EventsTank.h" 

AllTankEvents allTankEvents; 

----- ----- EventRegisterer.cpp

#include <loki/Typelist.h> 
#include <loki/HierarchyGenerators.h> 

#include "../core/Event.h" 

#include "EventsTank.h" 

typedef Loki::GenLinearHierarchy<Loki::TL::Reverse<EventsTankAll>::Result,UnitId,EventIdGenerator> AllEvents; 
AllEvents allEvents; 

Como se trata de una gran cantidad de código, voy a tratar de resumir. Tengo una clase base EventBase que tiene dos miembros importantes: EVENT_ID y EVENT_TYPE. Lo que estoy haciendo es metacomponer dos clases: AllTankEvents que al momento de la instalación inicializan EVENT_TYPE para TankEvents, y AllEvents inicializan EVENT_ID. Lo que el usuario de esta mierda necesita hacer, es agregar otra definición de Evento de Tanque y agregarla a la lista de tipos EVENTS_ALL__. Puede enviar eventos con código como if (event.EVENT_ID==EventTank1::EVENT_ID), etc. Otro código puede tener en cuenta EVENT_ID/EVENT_TYPE inicializado con EVENT_ID_INITIAL/EVENT_TYPE_INITIAL y assert. No tenga miedo de las macros de preprocesador de C. Son simplemente el azúcar, por lo que puedo automatizar algunas tareas. Echa un vistazo, tengo que irme ahora.

2

¿Es posible utilizar la metaprogramación de plantillas (o cualquier otro mecanismo con el lenguaje C++) para contar el número de clases derivadas de alguna clase base de modo que a cada clase derivada se le dé un identificador de clase estático único?

No, no hay tal mecanismo. No importa lo que haga, tendrá que agregar "algo" (muy probablemente una macro) a cada clase derivada manualmente para lograr algo así. Ver macro Qt 4 y Q_OBJECT. También puede crear macros para crear clases derivadas, pero esto no se puede hacer automáticamente.

podría sin embargo, escriba su propia herramienta de preprocesador/análisis de código C++ que escanea el código fuente que proporcionó y luego inserta las directivas necesarias en la fuente.

Además, RTTI proporciona el nombre para cada clase. El problema es que este nombre es específico de la implementación, por lo que no es muy útil.

Cuestiones relacionadas