Подсчитывать с помощью шаблонного метапрограммирования? - PullRequest
9 голосов
/ 22 декабря 2011

Я уже некоторое время пытаюсь найти креативное решение этой проблемы (вкл. И выкл.), Но пока не смог. Недавно я подумал, что это можно решить с помощью шаблонного метапрограммирования, хотя я не уверен из-за моего относительного недостатка опыта в технике.

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

Заранее спасибо!

Ответы [ 3 ]

6 голосов
/ 22 декабря 2011

Нет. Это проблема, которая возникает на практике довольно часто, и, насколько я знаю, есть только два решения:

  1. Вручную присваивайте идентификаторы каждому производному классу.
  2. Динамически и лениво генерировать идентификаторы недетерминированно.

То, как вы делаете второе, выглядит примерно так:

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> { ... };

Это дает вам уникальные идентификаторы для каждого класса:

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

Однако эти идентификаторы назначаются лениво, поэтому порядок вызова getId() влияет на возвращенный идентификатор.

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

То, нормально ли это для вашего приложения, зависит от ваших конкретных потребностей (например, не подходит для сериализации).

2 голосов
/ 29 марта 2012

Возможно ли использовать шаблонное метапрограммирование (или любой другой механизм с языком C ++) для подсчета количества классов, производных от некоторого базового класса, так что каждому производному классу присваивается уникальный статический идентификатор класса?

Нет, такого механизма нет. Независимо от того, что вы делаете, вам придется добавить «что-то» (скорее всего, макрос) в каждый производный класс вручную , чтобы достичь чего-то подобного. Смотрите макрос Qt 4 и Q_OBJECT. Вы также можете создать макрос для создания производных классов, но это не может быть сделано автоматически.

Вы могли бы однако написать свой собственный препроцессор кода C ++ / инструмент анализа, который сканирует предоставленный вами исходный код и затем вставляет необходимые директивы в исходный код.

Кроме того, RTTI предоставляет имя для каждого класса. Проблема в том, что это имя зависит от реализации, поэтому оно не очень полезно.

0 голосов
/ 29 марта 2012

Я публикую это с моей проблемой.Это будет длинный пост.Я пишу систему событий и хочу регистрировать события только в одном месте.

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

И не самое интересное ...

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

Поскольку это много кодаЯ постараюсь подвести итог.У меня есть базовый класс EventBase, в котором есть два важных члена: EVENT_ID и EVENT_TYPE.Я занимаюсь мета-составлением двух классов: AllTankEvents, которые после инициализации инициализируют EVENT_TYPE для TankEvents, и AllEvents инициализируют EVENT_ID.Пользователю этого дерьма нужно добавить еще одно определение Tank Event и добавить его в список EVENTS_ALL__.Вы можете отправлять события с кодом типа if (event.EVENT_ID==EventTank1::EVENT_ID) и так далее.Другой код может следить за EVENT_ID/EVENT_TYPE, инициализированным с EVENT_ID_INITIAL/EVENT_TYPE_INITIAL и assert.Не бойтесь макросов препроцессора CОни просто сахар, поэтому я могу автоматизировать некоторые задачи.Посмотри, мне нужно идти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...