56

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.

Peter Gibson
  • 19,086
  • 7
  • 60
  • 64

8 Answers8

84

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.

vpicaver
  • 1,771
  • 1
  • 15
  • 16
  • 4
    Can you always be sure that in every C implementation anywhere at all times enums start from 0? I assume so, but is it always really true? – JohnyTex Nov 30 '17 at 11:22
  • 10
    Late answer @JohnyTex, but the C standard mentions under 'Enumeration specifiers': `If the first enumerator has no =, the value of its enumeration constant is 0. Each subsequent enumerator with no = defines its enumeration constant as the value of the constant expression obtained by adding 1 to the value of the previous enumeration constant.` So yeah, this would apply to any standards-adhering implementation. – seri Aug 07 '18 at 21:29
45

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;
Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • can enum types be negative? If so watch out with this one. – ojblass Apr 03 '09 at 03:51
  • 2
    FIRST=-2 would still calculate correctly, I think: 1 - (-2) = 3. – paxdiablo Apr 03 '09 at 03:53
  • 3
    No need to watch out; subtraction works just fine for negative numbers. Pax has it right. – Brian Campbell Apr 03 '09 at 03:59
  • 2
    FIRST = -200, SECOND = -199, THIRD = -198, END = -100. -200 - (-100) = bad news – ojblass Apr 03 '09 at 04:00
  • I meant sequential as in you only ever set the first value, and let each remaining value increment by default, in sequence. enum blah { FIRST= , SECOND, THIRD, END }; – Brian Campbell Apr 03 '09 at 04:12
  • like i am saying thought setting the first to -9 and having 3 elements -9 - (-7) = -2. You need an absolute value or something. – ojblass Apr 03 '09 at 04:14
  • 14
    No, you would get FIRST = -9 SECOND = -8 THIRD = -7 END = -6, so you'd have -6 - (-9) = -6 + 9 = 3 – Brian Campbell Apr 03 '09 at 04:18
  • No problem; an easy mistake to make. – Brian Campbell Apr 03 '09 at 04:27
  • @Brian: the value END could be useful if FIRST=0 and values are used as indexes for a static array (for example a static array of strings describing the values). Then the array can be declared and defined with size 'END', thus no need to change its size each time a value is added to the enum. – Francois Aug 18 '11 at 14:40
  • `enum { E_1 = 0, E_2 = 5, E_3, E_4, E_5 = 10, E_END };`, this will end of error! – S Dao Apr 19 '17 at 04:46
  • I suggest editing the answer and add "the last being a placeholder that always designates the end of the list". Otherwise it's confusing. – Binar Web Dec 05 '19 at 09:17
32

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).

Sam
  • 5,416
  • 7
  • 47
  • 49
12

Unfortunately, no. There is not.

rlbond
  • 65,341
  • 56
  • 178
  • 228
6

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...

David Stosik
  • 879
  • 9
  • 17
  • 3
    The intention is that there are 3 elements in the enum (`FIRST, SECOND & THIRD`), with the last being a placeholder that always designates the end of the list, so that if additional items are added it is not necessary to change the formula for the item count. – Peter Gibson Jan 28 '15 at 01:31
  • 1
    Yeah, that's what I thought when I read again a few hours later. Thanks for confirming. A comment next to `END` that says "Not mean to be used" or something might help understanding the code. – David Stosik Jan 30 '15 at 07:59
  • I like to use a convention something like "blah_end_marker"; the "end_marker" identifies the purpose, while the prefix keeps the end markers distinct. You can then define the count, or for "sequential" enums starting with 0, use the marker value directly (e.g. "blah all_blahs[blah_end_marker];" - though I'm starting to think the const int blah_count makes more sense... – Bob Oct 26 '17 at 14:36
  • Only works if values assigned are monotonically increasing by 1 – Larry_C Jan 06 '22 at 18:06
  • What is the values are assigned non-consecutive value? Not gonna fly. – Larry_C Jan 06 '22 at 18:24
3

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?

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
3
int enaumVals[] =
{
FIRST,
SECOND,
THIRD,
LAST
};

#define NUM_ENUMS sizeof(enaumVals) / sizeof ( int );
DaveShaw
  • 52,123
  • 16
  • 112
  • 141
iuoi
  • 49
  • 1
  • 9
    This seems to require repeating the entire enum in an array, purely to allow use of sizeof - looks like more effort to me than just #define COUNT 4 – Peter Gibson Dec 12 '11 at 00:16
  • 1
    In C size computes at compilation time. C is not an interpreted language, this method is as efficient as using a fix definition and simplifies maintenance. – Fulup Apr 03 '15 at 10:55
  • 3
    Except that it isn't. This will pollute the symbols of your program with an useless array of `NUM_ENUMS` int. Your program will take longer to start, will use more disk space and so on. The only way to avoid this is to declare and define this in an independent compilation unit, but then you don't have `NUM_ENUMS` available so that won't work. Later on, if you add a member to your enum list, you might forget to do so in the array and `NUM_ENUMS` will not match anymore – xryl669 Jun 14 '19 at 16:44
1
#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));
}