【问题标题】:Generic method to display enum value names显示枚举值名称的通用方法
【发布时间】:2010-08-11 06:52:26
【问题描述】:

有没有办法显示枚举值的名称? 说我们有:

enum fuits{
    APPLE,
    MANGO,
    ORANGE,
};

main(){
enum fruits xFruit = MANGO;
...
printf("%s",_PRINT_ENUM_STRING(xFruit));
...
}

使用预处理器

#define _PRINT_ENUM_STRING(x) #x

不起作用,因为我们需要获取变量 'x' 的值,然后将其转换为字符串。 这在 c/C++ 中完全可能吗?

【问题讨论】:

  • 这是一个关于 intertubes 的常见问题解答,很可能是一个骗子,尽管我没有检查。回答:不,这是不可能的(如果没有像@Naveen 建议的那样做一些圈套,并冒着失去同步的风险。如果你准备冒这个风险,似乎会有很多人使用这个解决方案。总比没有好,但它是诱人的命运......)。
  • @Eugen Constantin Dinca 这个问题只是 C++,而这里的 OP 似乎想要一个 C 解决方案。
  • @schot 此问题已被 OP 标记为 C++。
  • 天啊!感谢cmets,我也认为这也做不到。好吧,只是为了确保 C 每隔一天就让我惊叹不已。我想我会把 shot 标记为最接近的答案。

标签: c++ c


【解决方案1】:

你可以使用预处理器来做到这一点,我相信这种技术被称为X-Macros

/* fruits.def */
X(APPLE)
X(MANGO)
X(ORANGE)

/* file.c */
enum fruits {
#define X(a) a,
#include "fruits.def"
#undef X
};

const char *fruit_name[] = {
#define X(a) #a,
#include "fruits.def"
#undef X
};

请注意,最后一个条目包含一个尾随逗号,这在 C99 中是允许的(但在 C89 中是不允许的)。如果这是一个问题,您可以添加标记值。也可以通过为自定义名称或枚举值等提供多个参数来使宏更复杂:

X(APPLE, Apple, 2)
#define X(a,b,c) a = c,        /* in enum */
#define X(a,b,c) [c] = #b,     /* in name array */

限制:你不能有负常数,你的数组是sizeof (char *) * largest_constant但是您可以通过使用额外的查找表来解决这两个问题:

int map[] = {
#define X(a,b,c) c,
#include "fruits.def"
#undef X
};

这当然行不通。起作用的是生成一组额外的enum 常量作为名称的键:

enum fruits {
#define X(a,b,c) a ## _KEY,
#include "fruits.def"
#undef X
#define X(a,b,c) a = c,
#include "fruits.def"
#undef X
};

现在您可以使用fruit_name[PINEAPPLE_KEY] 找到X(PINEAPPLE, Pineapple, -40) 的名称。

人们注意到他们不喜欢额外的包含文件。你不需要这个额外的文件,你也可以使用#define。这可能更适合小型列表:

#define FRUIT_LIST X(APPLE) X(ORANGE)

并将前面示例中的#include "fruits.def 替换为FRUIT_LIST

【讨论】:

  • 这是我所知道的最好的方法。我对带有 2 参数 X 宏的错误字符串使用了类似的技术:X(name,desc),其中宏扩展为错误代码(类似于errno 值)或描述性字符串,具体取决于错误列表文件的位置包括在内。
  • +1。这几乎击败了我通常在 perl 脚本中定义枚举的技巧,该脚本将枚举写入 .h 文件并将匹配的翻译数组写入 .c 文件。我的方式需要一个自定义构建步骤,使用 make 很容易,但在完整的 IDE 中更难。
  • 我不喜欢它。有时可能有用,但我不会过度使用它。也许我会将它用于大型且经常更改的枚举。这样做的成本太大了。
  • ...额外的头文件,分散的包含,使用预处理器。你可能会因此而犯错。对于中小型枚举,最好自己编写查找表,靠近枚举声明。
  • ...有一些技术可以降低查找表不同步的风险,例如在枚举中定义第一个和最后一个标记,然后根据这些标记对查找表大小进行静态断言。通常这些标记对其他目的很有帮助,所以这并不是为了保持查找表同步而造成的额外混乱。
【解决方案2】:

在这种情况下,您可以使用映射。

char *a[10] = { "APPLE","MANGO","ORANGE"};

printf("%s",a[xFruit]);

是的,除非您提供准确的 enum 值,否则预处理器将无法工作。

还可以查看this question 了解更多信息。

【讨论】:

    【解决方案3】:

    我已经成功地使用预处理器编程来获得这种宏:

    DEFINE_ENUM(Fruits, (Apple)(Mango)(Orange));
    

    它不仅仅是打印名称,但如果需要,它可以很容易地简化为 2 个开关。

    它基于 Boost.Preprocessor 工具(特别是 BOOST_PP_SEQ_FOREACH),这是预处理器编程的必备工具,我发现它比 X 工具及其文件重新包含系统要优雅得多。

    【讨论】:

      【解决方案4】:

       公共枚举 LDGoalProgressUpdateState
          {
      
      
          [Description("Yet To Start")]
          YetToStart = 1,
          [Description("In Progress")]
          InProgress = 2,
          [Description("Completed")]
          Completed = 3
      }
      
      
          var values = (ENUMList[])Enum.GetValues(typeof(ENUMList));
          var query = from name in values
                      select new EnumData//EnumData is a Modal or Entity
                      {
                          ID = (short)name,
                          Name = GetEnumDescription(name)//Description of Particular Enum Name
                      };
          return query.ToList();
      

      区域 HelperMethods

      public static string GetEnumDescription(Enum value) { FieldInfo fi = value.GetType().GetField(value.ToString()); DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); if (attributes != null && attributes.Length > 0) return attributes[0].Description; else return value.ToString(); } #endregion

      【讨论】:

      • 这似乎不是问题中要求的 C 或 C++
      猜你喜欢
      • 2016-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-02
      相关资源
      最近更新 更多