3

通常情况下,您有 2 个等效但编号不同的枚举,并且您需要函数将元素从第一个枚举转换为第二个枚举的元素,反之亦然。这通常对代码来说非常繁琐,并且无论何时添加新元素,您都必须确保将配对添加到转换和反向转换函数中(它违反了 DRY 原则)。

什么是最不容易出错的方法,仍然可以生成高效的代码?我提到了高效的部分,因为如果运行时查找不是问题,您可以制作一堆对并将它们放在 std::maps 中。我更喜欢与手动编写从一个枚举值到另一个枚举值的大 switch 语句一样高性能的东西。

通过一些增强预处理器魔法或一些模板黑客,我相信你可以想出一些东西,你可以编写一个对列表并生成转换和反向转换函数,但我不确定更喜欢哪种方法或为什么。这两种方法都以编译时间慢和难以诊断编译错误而著称。或者也许完全是其他一些方法?

4

6 回答 6

3

正如尼尔所说,我自己从来没有遇到过这个问题。不过,我遇到了代码集的问题(即从字符串映射到枚举,然后返回)。

我的第一反应是表皮:干燥。

正如您所指出的,维护两个枚举和翻译需要时间,所以最好重构并只使用一个。

我的第二个反应是尝试删除枚举。我不喜欢枚举。也许我会用即将到来的 C++0x 来欣赏它们,但就目前而言,它们比我认为的更麻烦。我更喜欢智能对象,有类别等......

但是,我猜这个问题本身很有趣。所以如果你喜欢处理这种乱七八糟的情况,我不妨试着减轻你的负担。

模板在这里不能做太多事情。我已经将它们用于枚举范围检查以及字符串转换(来回)和迭代,但这就是它们所能做的。但是,正如您所怀疑的那样,Boost.Preprocessor 的一些“微妙”应用程序是可能的。

你想写什么:

DEFINE_CORRESPONDING_ENUMS(Server, Client,
  ((Server1, 1, Client1, 6))
  ((Server2, 2, Client2, 3))
  ((Common1, 4, Common1, 4))
  ((Common2, 5, Common2, 5))
  ((Server3, 7, Client3, 1))
);

我们希望它生成:

struct Server
{
  enum type { Server1 = 1, Server2 = 2, Common1 = 4, Common2 = 5, Server3 = 7 };
};

struct Client
{
  enum type { Client1 = 6, Client2 = 3, Common1 = 4, Common2 = 5, Client3 = 1 };
};

Server::type ServerFromClient(Client::type c)
{
  switch(c)
  {
  case Client1: return Server1;
  //...
  default: abort();
  }
}

Client::type ClientFromServer(Server::type s)
{
  //...
}

好消息是,这是可能的。我什至可以做到,尽管我可能会让你做一些工作;)

以下是一些解释:

  • 宏的第三个元素是一个序列。序列的大小是无限的。
  • 序列的每个元素都是一个 4 元组。您需要提前知道它的大小,因此重复Common. 如果您改用序列,则可以处理可变数量的元素(例如,避免重复常见的...),但这会使事情变得更加复杂
  • 你需要看一下BOOST_PP_SEQ_FOREACH,这将是这里的基本操作。
  • 不要忘记BOOST_PP_CAT处理标记的串联。
  • 在选项gcc-E产生预处理器输出,它可能会派上用场......
  • 不要忘记文件中的注释和使用方法,否则你的同事会讨厌你
于 2010-07-07T17:19:42.613 回答
2

你在寻找这样的东西吗?未经测试,但它应该可以工作。

(关于过早优化和分析需求的标准警告适用;std::map 查找可能没那么糟糕,巨大的开关表可能没那么好。)

枚举-impl.h:

// No include guard.
DEFINE_ENUM_PAIR(EGA_BRIGHT_RED, 12, HTML_RED, 0xff0000)
DEFINE_ENUM_PAIR(EGA_BRIGHT_BLUE, 9, HTML_BLUE, 0x0000ff)
DEFINE_ENUM_PAIR(EGA_BRIGHT_GREEN, 10, HTML_GREEN, 0x00ff00)
DEFINE_ENUM_PAIR(EGA_BLACK, 0, HTML_BLACK, 0x000000)

枚举.cpp:

enum EgaColorType {
#define DEFINE_ENUM_PAIR(name1, value1, name2, value2) name1 = value1,
#include "enums-impl.h"
#undef DEFINE_ENUM_PAIR
};

enum HtmlColorType {
#define DEFINE_ENUM_PAIR(name1, value1, name2, value2) name2 = value2,
#include "enums-impl.h"
#undef DEFINE_ENUM_PAIR 
};

HtmlColorType ConvertEgaToHtml(EgaColorType c) {
switch (c) {
#define DEFINE_ENUM_PAIR(name1, value1, name2, value2) case name1: return name2;
#include "enums-impl.h"
#undef DEFINE_ENUM_PAIR
default: assert(false);
}

EgaColorType ConvertHtmlToEga(HtmlColorType c) {
switch (c) {
#define DEFINE_ENUM_PAIR(name1, value1, name2, value2) case name2: return name1;
#include "enums-impl.h"
#undef DEFINE_ENUM_PAIR
default: assert(false);
}
于 2010-07-07T16:12:35.410 回答
2

为什么查找表不起作用?为什么你被迫使用这个巨大的 switch 语句?

于 2010-07-07T16:35:06.010 回答
1

如果枚举范围相对密集(而不是用作位图指示符),则可以只使用数组进行映射。您让编译器计算出数组长度,然后您可以断言该长度是否不是您想要的。你甚至可以 static_assert 它,我不确定。由于您使用的是数组,因此转换应该是恒定时间,并且如果编译器没有在内部生成跳转表,则可能比切换更好。请注意,此代码完全未经测试。

enum A
{
    MIN_A = 1,
    A_ATT_1 = 1,
    A_ATT_2 = 2,
    A_ATT_3 = 3,
    LAST_A
};

enum B
{
    MIN_B = 2
    B_ATT_2 = 2,
    B_ATT_1 = 4,
    B_ATT_3 = 5,
    LAST_B
};

B A_to_B[] =
{
    B_ATT_1,
    B_ATT_2,
    B_ATT_3
};

// Somewhere that will always run, as desired:
assert(LAST_A - MIN_A == sizeof(A_to_B) / sizeof(A_to_B[0]);

B from_A(A in)
{
    B ret = A_to_B[in - MIN_A];
    assert(ret != LAST_B);
    return ret;
}

A B_to_A[] =
{
    A_ATT_2,
    LAST_A,
    A_ATT_1,
    A_ATT_3
};

// Somewhere that will always run, as desired:
assert(LAST_B - MIN_B == sizeof(B_to_A) / sizeof(B_to_A[0]);

A from_B(B in)
{
    A ret = B_to_A[in - MIN_B];
    assert(ret != LAST_A);
    return ret;
}
于 2010-07-07T17:52:24.023 回答
0

好吧,您总是可以尝试制作一个函数(在数学函数的意义上,而不是编程函数),将一个枚举的数量转换为另一个。但是,每次添加元素时,此功能都会发生变化。

于 2010-07-07T16:01:41.147 回答
0

考虑不使用两个枚举。

这些之间没有太大区别:

enum FirstSet { A=4, B=6, C=8, D=5 };
enum SecondSet { E=2, F=5, G=5, H=1 };

和这个:

enum OneEnum { A, B, C, D };
enum TwoEnum { E, F, G, H };
int FirstSet[] = { 4, 6, 8, 5 };
int SecondSet[] = { 2, 5, 5, 1 };

需要更改的访问次数可能令人望而却步,但这比每次转换时的 O(n) 查找要好一些。

于 2010-07-07T16:30:47.737 回答