2

我在枚举项目中遇到了问题。在 EventDef.h 中,

enum EventDef {
    EVT1 = 0,
    EVT2,
    EVT3,
    EVT_NUM,
}

这样,我可以在另一个头 UIEventDef.h 中扩展 EventDef 系统

#include "EventDef.h"
enum UIEventDef {
    UIEVT1 = EVT_NUM,
    UIEVT2,
    UIEVT3,
}

但是,有一个限制,我不能以相同的方式在 NetEvent.h 中执行此操作。

#include "EventDef.h"
enum NetEventDef {
    NETEVT1 = EVT_NUM,
    NETEVT2,   //wrong: this will have the same value as UIEVT2
    NETEVT3,  
}

C++ 中是否有更好的编译时解决方案,例如可以提供帮助的模板?

4

4 回答 4

6

可扩展枚举的想法本质上并不是“糟糕的设计”。在其他语言中有它们的历史,即使 c++ 不直接支持它们。有不同种类的可扩展性。

可扩展枚举有用的东西

  • 错误代码
  • 消息类型
  • 设备标识(OID 是一个分层枚举系统)

枚举可扩展性示例

  • 目标模块二具有可通过继承之类的类进行扩展的枚举。
  • Java 中的可扩展枚举模式,可以在 c++ 中实现。
  • Java 枚举是可扩展的,因为额外的数据和方法可以成为枚举的一部分。
  • 在 c++ 中,typeid 运算符本质上是编译器生成的带有附加值的枚举。

您在示例代码中展示的那种可扩展性在独立的 c++ 中没有优雅的实现。事实上,正如你所指出的,它很容易导致问题。

考虑一下您希望如何使用可扩展枚举。也许一组不可变的单例对象将满足您的需求。

在 c++ 中使用可扩展枚举的另一种方法是使用代码生成器。每个想要添加到可扩展枚举的编译单元都将 id 记录在自己的单独的 .enum 文件中。在构建时,在编译之前,一个脚本(即 perl、bash、...)会查找所有 .enum 文件,读取它们,为每个 id 分配数值,并写出一个头文件,该文件与其他文件一样包含在内。

于 2012-08-25T04:50:24.023 回答
2

为什么你希望你的事件枚举被这样声明?如果你愿意的话,通过将它们“链接”起来,你会得到什么,就像你描述的那样?

我会让它们成为完全独立的枚举。其次,我建议您不要再使用旧式枚举。c++11 在这里并且在 gcc 中可用。您应该使用枚举类:

enum class EventDef  : unsigned { Evt1 = 0, Evt2, Evt3, ... LastEvt }
enum class NetEvtDef : unsigned { NetEvt1 = 0, NetEvt2, NetEvt3, ... NetLastEvt }

如果要切换,可以这样做:

void doSwitch(EventDef evt_def)
{
  switch(evt_def)
  {
    case EventDef::Evt1
    {
     // Do something;
     break;
    }
    default:
    // Do something;
  };
}

void doSwitch(NetEvtDef net_def)
{
  switch(net_def)
  {
    case NetEvtDef::NetEvt1
    {
     // Do something;
     break;
    }
    default:
    // Do something;
  };
}

通过为 doSwitch 创建重载函数,您可以隔离所有枚举类型。将它们放在不同的类别中是一种好处而不是问题。它为您提供了以不同方式处理每个事件枚举类型的灵活性。

如您所描述的那样将它们链接在一起会使问题不必要地复杂化。

我希望这会有所帮助。

于 2012-08-25T02:37:13.847 回答
0

我发现以下是复杂性、功能和类型安全之间的有用折衷。它使用具有默认构造函数的自定义类的全局变量来简化初始化。下面的示例是一组可扩展的错误代码。您可能还想包含在名称空间中(但我通常不打扰)。

//
//  ErrorCodes.h
//  ExtendableEnum
//
//  Created by Howard Lovatt on 10/01/2014.
//

#ifndef ErrorCodes_h
#define ErrorCodes_h

#include <string>

class ErrorCodes {
public:
    static int nextValue_;
    explicit ErrorCodes(std::string const name) : value_{nextValue_++}, name_{name} {}
    ErrorCodes() : ErrorCodes(std::to_string(nextValue_)) {}
    int value() const { return value_; }
    std::string name() const { return name_; }
private:
    int const value_;
    std::string const name_;
    ErrorCodes(const ErrorCodes &);
    void operator=(const ErrorCodes &);
};

int ErrorCodes::nextValue_ = 0; // Weird syntax, does not declare a variable but rather initialises an existing one!
ErrorCodes first;
ErrorCodes second;
// ...

#endif


//
//  ExtraErrorCodes.h
//  ExtendableEnum
//
//  Created by Howard Lovatt on 10/01/2014.
//

#ifndef ExtraErrorCodes_h
#define ExtraErrorCodes_h

#include "ErrorCodes.h"

ErrorCodes extra{"Extra"};

#endif


//
//  ExtraExtraExtraCodes.h
//  ExtendableEnum
//
//  Created by Howard Lovatt on 10/01/2014.
//

#ifndef ExtendableEnum_ExtraExtraCodes_h
#define ExtendableEnum_ExtraExtraCodes_h

#include "ErrorCodes.h"

ErrorCodes extraExtra{"ExtraExtra"};

#endif


//
//  main.cpp
//  ExtendableEnum
//
//  Created by Howard Lovatt on 10/01/2014.
//

#include <iostream>
#include "ErrorCodes.h"
#include "ExtraErrorCodes.h"
#include "ExtraExtraErrorCodes.h"

// Need even more error codes
ErrorCodes const localExtra;

int main(int const notUsed, const char *const notUsed2[]) {
    std::cout << first.name() << " = " << first.value() << std::endl;
    std::cout << second.name() << " = " << second.value() << std::endl;
    std::cout << extra.name() << " = " << extra.value() << std::endl;
    std::cout << extraExtra.name() << " = " << extraExtra.value() << std::endl;
    std::cout << localExtra.name() << " = " << localExtra.value() << std::endl;
    return 0;
}

输出是:

0 = 0
1 = 1
Extra = 2
ExtraExtra = 3
4 = 4

如果您有多个编译单元,那么您需要使用单例模式的变体:

class ECs {
public:
    static ErrorCode & first() {
        static ErrorCode instance;
        return instance;
    }
    static ErrorCode & second() {
        static ErrorCode instance;
        return instance;
    }
private:
    ECs(ECs const&);
    void operator=(ECs const&);
};
于 2014-01-10T05:51:35.290 回答
0

我们可以在 C++ 中构造一个可扩展的“枚举”,如下所示:

struct Last {};

struct D 
{
    using Next = Last;
    static const char* name = “D”;
};

struct C
{
    using Next = D;
    static const char* name = “C”;
};

struct B 
{
    using Next = C;
    static const char* name = “B”;
};

using First = B;

我们可以使用这些构造遍历上述内容:

void Process(const B&)
{
    // do something specific for B
    cout << “Call me Ishmael” << endl;
}

template <class T>
void Process(const T&)
{
    // do something generic
    cout << “Call me “ << T::name << endl;
}

template <class T>
struct IterateThru
{
   static void iterate() 
   {
       Process(T());
       IterateThru<T::Next>::iterate();
   }
};

template <>
struct IterateThru<Last>
{
    static void iterate()
   {
       // end iteration
   }
};

遍历“枚举”:

IterateThru<First>::iterate();

扩展“枚举”:

struct A
{
    using Next = B;
    static const char* name = “A”;
}:

using First = A:
于 2020-03-11T03:23:31.240 回答