枚举中的元素数量

发布于 2024-07-16 19:19:25 字数 156 浏览 2 评论 0原文

在 C 中,有没有一种很好的方法来跟踪枚举中的元素数量? 我见过

enum blah {
    FIRST,
    SECOND,
    THIRD,
    LAST
};

但这只有在项目是连续的并且从零开始时才有效。

In C, is there a nice way to track the number of elements in an enum? I've seen

enum blah {
    FIRST,
    SECOND,
    THIRD,
    LAST
};

But this only works if the items are sequential and start at zero.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

还如梦归 2024-07-23 19:19:25

如果您不分配枚举,您可以执行以下操作:

enum MyType {
  Type1,
  Type2,
  Type3,
  NumberOfTypes
}

NumberOfTypes 将计算为 3,这是实际类型的数量。

If you don't assign your enums you can do somethings like this:

enum MyType {
  Type1,
  Type2,
  Type3,
  NumberOfTypes
}

NumberOfTypes will evaluate to 3 which is the number of real types.

听你说爱我 2024-07-23 19:19:25

我不相信有。 但是,如果这样的数字不是连续的,并且您还没有在某处拥有它们的列表,您会如何处理它们呢? 如果它们是连续的但以不同的数字开始,您始终可以这样做:

enum blah {
    FIRST = 128,
    SECOND,
    THIRD,
    END
};
const int blah_count = END - FIRST;

I don't believe there is. But what would you do with such a number if they are not sequential, and you don't already have a list of them somewhere? And if they are sequential but start at a different number, you could always do:

enum blah {
    FIRST = 128,
    SECOND,
    THIRD,
    END
};
const int blah_count = END - FIRST;
半窗疏影 2024-07-23 19:19:25

老问题,我知道。 这是针对有同样问题的谷歌用户的。

您可以使用 X-Macros

示例:

//The values are defined via a map which calls a given macro which is defined later
#define ENUM_MAP(X) \
      X(VALA, 0)    \
      X(VALB, 10)   \
      X(VALC, 20)

//Using the map for the enum decl
#define X(n, v) [n] = v,
typedef enum val_list {
    ENUM_MAP(X) //results in [VALA] = 0, etc...
} val_list;
#undef X

//For the count of values
#define X(n, v) + 1
int val_list_count = 0 + ENUM_MAP(X); //evaluates to 0 + 1 + 1 + 1
#undef X

这对于 IDE 来说也是透明的,因此会自动完成会工作得很好(因为这一切都是在预处理器中完成的)。

Old question, I know. This is for the googlers with the same question.

You could use X-Macros

Example:

//The values are defined via a map which calls a given macro which is defined later
#define ENUM_MAP(X) \
      X(VALA, 0)    \
      X(VALB, 10)   \
      X(VALC, 20)

//Using the map for the enum decl
#define X(n, v) [n] = v,
typedef enum val_list {
    ENUM_MAP(X) //results in [VALA] = 0, etc...
} val_list;
#undef X

//For the count of values
#define X(n, v) + 1
int val_list_count = 0 + ENUM_MAP(X); //evaluates to 0 + 1 + 1 + 1
#undef X

This is also transparent to an IDE, so auto-completes will work fine (as its all done in the pre-processor).

梅倚清风 2024-07-23 19:19:25

很不幸的是,不行。 那没有。

Unfortunately, no. There is not.

戒ㄋ 2024-07-23 19:19:25

我知道这是一个非常古老的问题,但由于接受的答案是错误的,我觉得有必要发布自己的答案。 我将重用已接受的答案的示例,稍加修改。
(假设枚举是连续的。)

// Incorrect code, do not use!
enum blah {
  FIRST   =  0,
  SECOND, // 1
  THIRD,  // 2
  END     // 3
};
const int blah_count = END - FIRST;
// And this above would be 3 - 0 = 3, although there actually are 4 items.

任何开发人员都知道原因:count = last - first + 1
这适用于任何符号组合(两端都是负数,都是正数,或者只有一端是负数)。 你可以试试。

// Now, the correct version.
enum blah {
  FIRST   =  0,
  SECOND, // 1
  THIRD,  // 2
  END     // 3
};
const int blah_count = END - FIRST + 1; // 4

编辑:再次阅读文本,我有一个疑问。 END 是否意味着不属于所提供项目的一部分? 这对我来说看起来很奇怪,但好吧,我想这可能是有道理的......

I know this is a very old question, but as the accepted answer is wrong, I feel compelled to post my own. I'll reuse the accepted answer's example, slightly modified.
(Making the assumption that enums are sequential.)

// Incorrect code, do not use!
enum blah {
  FIRST   =  0,
  SECOND, // 1
  THIRD,  // 2
  END     // 3
};
const int blah_count = END - FIRST;
// And this above would be 3 - 0 = 3, although there actually are 4 items.

Any developer knows the reason: count = last - first + 1.
And this works with any combination of signs (both ends negative, both positive, or only first end negative). You can try.

// Now, the correct version.
enum blah {
  FIRST   =  0,
  SECOND, // 1
  THIRD,  // 2
  END     // 3
};
const int blah_count = END - FIRST + 1; // 4

Edit: reading the text again, I got a doubt. Is that END meant not to be part of the offered items? That looks weird to me, but well, I guess it could make sense...

心碎无痕… 2024-07-23 19:19:25

好吧,由于枚举无法在运行时更改,因此您可以做的最好的事情是:

enum blah {
    FIRST = 7,
    SECOND = 15,
    THIRD = 9,
    LAST = 12
};
#define blahcount 4 /* counted manually, keep these in sync */

但我发现很难想象该信息会派上用场的情况。 你到底想做什么?

Well, since enums can't change at run-time, the best thing you can do is:

enum blah {
    FIRST = 7,
    SECOND = 15,
    THIRD = 9,
    LAST = 12
};
#define blahcount 4 /* counted manually, keep these in sync */

But I find it difficult to envisage a situation where that information would come in handy. What exactly are you trying to do?

诺曦 2024-07-23 19:19:25
int enaumVals[] =
{
FIRST,
SECOND,
THIRD,
LAST
};

#define NUM_ENUMS sizeof(enaumVals) / sizeof ( int );
int enaumVals[] =
{
FIRST,
SECOND,
THIRD,
LAST
};

#define NUM_ENUMS sizeof(enaumVals) / sizeof ( int );
生生不灭 2024-07-23 19:19:25
#include <stdio.h>

// M_CONC and M_CONC_ come from https://stackoverflow.com/a/14804003/7067195
#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B

#define enum_count_suffix _count
#define count(tag) M_CONC(tag, enum_count_suffix)
#define countable_enum(tag, ...) \
  enum tag {__VA_ARGS__}; \
  const size_t count(tag) = sizeof((int []) {__VA_ARGS__}) / sizeof(int)

// The following declares an enum with tag `color` and 3 constants: `red`,
// `green`, and `blue`.
countable_enum(color, red, green, blue);

int main(int argc, char **argv) {
  // The following prints 3, as expected.
  printf("number of elements in enum: %d\n", count(color));
}
#include <stdio.h>

// M_CONC and M_CONC_ come from https://stackoverflow.com/a/14804003/7067195
#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B

#define enum_count_suffix _count
#define count(tag) M_CONC(tag, enum_count_suffix)
#define countable_enum(tag, ...) \
  enum tag {__VA_ARGS__}; \
  const size_t count(tag) = sizeof((int []) {__VA_ARGS__}) / sizeof(int)

// The following declares an enum with tag `color` and 3 constants: `red`,
// `green`, and `blue`.
countable_enum(color, red, green, blue);

int main(int argc, char **argv) {
  // The following prints 3, as expected.
  printf("number of elements in enum: %d\n", count(color));
}
勿忘初心 2024-07-23 19:19:25

嗯,这已经很古老了,但是我找到了这里没有提到的方法。

大多数编译器都提供了 __COUNTER__ 宏。 该宏最初扩展为 0,每当使用它时,它都会给自身加 1。 它在目标文件的开头重置。 这意味着您可以这样写:

#include <stdio.h>

const int TestSizeInitial = __COUNTER__;
typedef enum {
    TEST_0 = __COUNTER__,
    TEST_1 = __COUNTER__,
    TEST_2 = __COUNTER__
} Test;
const int TestSize = __COUNTER__ - TestSizeInitial - 1;

const int TestSizeInitial2 = __COUNTER__;
typedef enum {
    TEST2_0 = __COUNTER__,
    TEST2_1 = __COUNTER__,
    TEST2_2 = __COUNTER__,
    TEST2_3 = __COUNTER__
} Test2;
const int TestSize2 = __COUNTER__ - TestSizeInitial2 - 1;

int main()
{
    printf("Number of Test variants = %i\n", TestSize);
    printf("Number of Test2 variants = %i\n", TestSize2);
    
    printf("TEST_0 = %i\n", TEST_0);
    printf("TEST_1 = %i\n", TEST_1);
    printf("TEST_2 = %i\n", TEST_2);
    
    printf("TEST2_0 = %i\n", TEST2_0);
    printf("TEST2_1 = %i\n", TEST2_1);
    printf("TEST2_2 = %i\n", TEST2_2);
    printf("TEST2_3 = %i\n", TEST2_3);

    return 0;
}

这将打印:

Number of Test variants = 3
Number of Test2 variants = 4
TEST_0 = 1
TEST_1 = 2
TEST_2 = 3
TEST2_0 = 6
TEST2_1 = 7
TEST2_2 = 8
TEST2_3 = 9

这具有不将最后一个变体用作计数器的优点,这可能会弄乱 switch 语句。 缺点是,正如您所看到的,它使得很难推断枚举变体的实际值。 只要您不需要它们的明确值,就可以了。 例如,如果您将 Test 放入 test.h 中,并将 Test2 放入 test2.h 中,则调用 include 语句的顺序会更改枚举变体的值。 它不会更改 TestSizeTestSize2 的值,因为它们是根据与 __COUNTER__ 初始值的差来定义的。

Well, this is very old, but I have found a method not mentioned here.

Most compilers provided the __COUNTER__ macro. This macro initially expands to 0, and whenever it is used it adds 1 to itself. It is reset at the beginning of object files. This means that you can write this:

#include <stdio.h>

const int TestSizeInitial = __COUNTER__;
typedef enum {
    TEST_0 = __COUNTER__,
    TEST_1 = __COUNTER__,
    TEST_2 = __COUNTER__
} Test;
const int TestSize = __COUNTER__ - TestSizeInitial - 1;

const int TestSizeInitial2 = __COUNTER__;
typedef enum {
    TEST2_0 = __COUNTER__,
    TEST2_1 = __COUNTER__,
    TEST2_2 = __COUNTER__,
    TEST2_3 = __COUNTER__
} Test2;
const int TestSize2 = __COUNTER__ - TestSizeInitial2 - 1;

int main()
{
    printf("Number of Test variants = %i\n", TestSize);
    printf("Number of Test2 variants = %i\n", TestSize2);
    
    printf("TEST_0 = %i\n", TEST_0);
    printf("TEST_1 = %i\n", TEST_1);
    printf("TEST_2 = %i\n", TEST_2);
    
    printf("TEST2_0 = %i\n", TEST2_0);
    printf("TEST2_1 = %i\n", TEST2_1);
    printf("TEST2_2 = %i\n", TEST2_2);
    printf("TEST2_3 = %i\n", TEST2_3);

    return 0;
}

This will print:

Number of Test variants = 3
Number of Test2 variants = 4
TEST_0 = 1
TEST_1 = 2
TEST_2 = 3
TEST2_0 = 6
TEST2_1 = 7
TEST2_2 = 8
TEST2_3 = 9

This has the advantage of not having the last variant used as counter, which can mess up switch statements. The downside is that, as you can see, it makes it very hard to reason about the actual value of the enum variants. As long as you don't need their explicit value, you're fine. For example, if you place Test in, say test.h, and Test2 in test2.h, the order with which you call the include statements changes the value of the enum variants. It does not change the value of TestSize and TestSize2 because they are defined in terms of the difference with the initial value of __COUNTER__.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文