9

一段时间以来,我一直试图想出一个创造性的解决方案来解决这个问题(打开和关闭),但我还没有能够做到。我最近认为它可能可以通过模板元编程来解决,但由于我相对缺乏该技术的经验,我不确定。

是否可以使用模板元编程(或使用 C++ 语言的任何其他机制)来计算从某个基类派生的类的数量,从而为每个派生类赋予唯一的静态类标识符?

提前致谢!

4

3 回答 3

6

不,这是一个在实践中经常出现的问题,据我所知,只有两种解决方案:

  1. 手动为每个派生类分配 ID。
  2. 动态地和延迟地以非确定性方式生成 ID。

你做第二个的方式是这样的:

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

这为您提供了每个类的唯一 ID:

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

但是,这些 ID 是延迟分配的,因此您调用的顺序getId()会影响返回的 ID。

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

这是否适合您的应用程序取决于您的特定需求(例如,对序列化没有好处)。

于 2011-12-21T21:47:14.820 回答
2

是否可以使用模板元编程(或使用 C++ 语言的任何其他机制)来计算从某个基类派生的类的数量,从而为每个派生类赋予唯一的静态类标识符?

不,没有这样的机制。无论您做什么,您都必须手动向每个派生类添加“某些东西”(很可能是一个宏)以实现类似的效果。请参阅 Qt 4 和 Q_OBJECT 宏。您也可以创建一个宏来创建派生类,但这不能自动完成。

但是,您可以编写自己的 C++ 代码预处理器/分析工具来扫描您提供的源代码,然后将必要的指令插入源代码。

此外,RTTI 为每个类提供名称。问题是这个名称是特定于实现的,所以它不是很有用。

于 2012-03-29T15:52:00.770 回答
0

我发布这个是考虑到我的问题。这将是一个很长的帖子。我正在编写事件系统,我只想在一个地方注册事件。

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

-----事件.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_IDEVENT_TYPE. 我正在做的是元组合两个类:AllTankEvents,它在初始化时为 TankEvents 初始化 EVENT_TYPE,而 AllEvents 初始化 EVENT_ID。这个废话的用户需要做的是添加另一个坦克事件定义,并将其添加到EVENTS_ALL__类型列表中。您可以使用诸如此类的代码调度事件if (event.EVENT_ID==EventTank1::EVENT_ID)。其他代码可以注意用和EVENT_ID/EVENT_TYPE初始化。不要害怕 C 预处理器宏。它们只是糖,所以我可以自动化一些任务。看看,我现在得走了。EVENT_ID_INITIAL/EVENT_TYPE_INITIALassert

于 2012-03-29T15:01:32.573 回答