112

This question came to my mind, when I had something like

enum Folders {FA, FB, FC};

and wanted to create an array of containers for each folder:

ContainerClass*m_containers[3];
....
m_containers[FA] = ...; // etc.

(Using maps it's much more elegant to use: std::map<Folders, ContainerClass*> m_containers;)

But to come back to my original question: What if I do not want to hard-code the array size, is there a way to figure out how many items are in Folders? (Without relying on e.g. FC being the last item in the list which would allow something like ContainerClass*m_containers[FC+1] if I'm not mistaken.)

Crast
  • 15,996
  • 5
  • 45
  • 53
fuenfundachtzig
  • 7,952
  • 13
  • 62
  • 87
  • This post may answer your question: http://stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c . – StackedCrooked Jan 20 '10 at 17:58
  • 1
    The question is a bit underspecified. Per the C++ standard, `int(FA) | int(FB) | int (FC)` is also a legal value for a `Folders` variable. If you're sizing `m_containers` so that any `Folders` variable is a valid index, `[FC+1]` wouldn't be big enough. – MSalters Jul 25 '11 at 08:52
  • I've asked a very related thing on http://stackoverflow.com/questions/12972317/count-on-enum-c-automatic – sergiol Nov 27 '13 at 14:15
  • I'd recomend you the solution from https://stackoverflow.com/a/60216003/12894563 and improved variant from https://stackoverflow.com/a/60271408/12894563 – ixjxk Feb 18 '20 at 11:11

7 Answers7

138

There's not really a good way to do this, usually you see an extra item in the enum, i.e.

enum foobar {foo, bar, baz, quz, FOOBAR_NR_ITEMS};

So then you can do:

int fuz[FOOBAR_NR_ITEMS];

Still not very nice though.

But of course you do realize that just the number of items in an enum is not safe, given e.g.

enum foobar {foo, bar = 5, baz, quz = 20};

the number of items would be 4, but the integer values of the enum values would be way out of the array index range. Using enum values for array indexing is not safe, you should consider other options.

edit: as requested, made the special entry stick out more.

wich
  • 16,709
  • 6
  • 47
  • 72
  • 31
    Call it LAST or ALWAYS_AT_END or something not so cryptic. Make it __stick out__. So that subsequent maintainers don't accidentally add new entries after you end marker. – Martin York Jan 20 '10 at 15:54
  • 3
    For better or worse, this is the approach we take in our organization. We normally call it FINAL_enumname_ENTRY, such as FINAL_foobar_ENTRY. I've also seen people use a separate static const FOOBAR_COUNT variable defined right after the enum declaration, an approach that is slightly more error prone. – Darryl Jan 20 '10 at 16:00
  • 1
    At least it's fairly easy to see that "enum foo {a=10,LAST}" is going to be odd. And I thought "int arr[LAST]" would be 11 items in this case, not 2, so most code will work (but you're wasting memory on invalid index values) – Code Abominator Jul 15 '15 at 05:44
36

For C++, there are various type-safe enum techniques available, and some of those (such as the proposed-but-never-submitted Boost.Enum) include support for getting the size of a enum.

The simplest approach, which works in C as well as C++, is to adopt a convention of declaring a ...MAX value for each of your enum types:

enum Folders { FA, FB, FC, Folders_MAX = FC };
ContainerClass *m_containers[Folders_MAX + 1];
....
m_containers[FA] = ...; // etc.

Edit: Regarding { FA, FB, FC, Folders_MAX = FC} versus {FA, FB, FC, Folders_MAX]: I prefer setting the ...MAX value to the last legal value of the enum for a few reasons:

  1. The constant's name is technically more accurate (since Folders_MAX gives the maximum possible enum value).
  2. Personally, I feel like Folders_MAX = FC stands out from other entries out a bit more (making it a bit harder to accidentally add enum values without updating the max value, a problem Martin York referenced).
  3. GCC includes helpful warnings like "enumeration value not included in switch" for code such as the following. Letting Folders_MAX == FC + 1 breaks those warnings, since you end up with a bunch of ...MAX enumeration values that should never be included in switch.
switch (folder) 
{
  case FA: ...;
  case FB: ...;
  // Oops, forgot FC!
}
Community
  • 1
  • 1
Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
  • Excellent solution that I've never seen before. I was in the middle of suggesting a Boost.Preprocessor hack that I've used, but this is way more elegant (and what I've been looking for). – Travis Gockel Jan 20 '10 at 15:56
  • 3
    Why not do: `enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];` ? – Bill Jan 20 '10 at 15:57
  • 1
    I prefer to make clear that the last is a number, and they all have the same name thanks to: `struct SomeEnum { enum type {FA, FB, FC, NB__};};` – Luc Hermitte Jan 21 '10 at 10:06
  • 2
    Actually I feel those "helpful" warnings are a right pain in the ass. I like good warnings I always set -Wall -pedantic, etc. when I'm developing, but these warnings are just stupid. There are only a few worse, like suggest parens for && || and & ^ | operator precedence. I mean, I thought java was the babysitter language, what the hell is happening to C and C++... – wich Jan 23 '10 at 21:29
  • 2
    The downside of having Folders_max=FC is that you have to change it everytime you add something to the enum! – Étienne Mar 13 '14 at 13:29
  • To suppress those warnings, add a default: case and throw an exception complaining about unsupported enum value. The only problem with this is that you then won't get a warning if you really do forget a case, and then have to wait for the exception rather than the compilation error to prompt you to fix your bug. – Jim Oldfield Nov 28 '14 at 20:45
  • 2
    enum Folders { FA , FB, FC, Folders_MIN = FA, Folders_MAX = FC }; Just to emphasize that it is useful for iteration? – gjpc Jun 11 '15 at 15:56
  • Of course, if someone forgets to edit Folders_MAX=FB to FC when they add an entry the problem is at least as severe as without the =FC tweak. It might fail earlier with a SIGSEVF if you're lucky, but I suspect it'll be just as likely to end up in production. Any experience? – Code Abominator Jul 15 '15 at 05:41
10

How about traits, in an STL fashion? For instance:

enum Foo
{
    Bar,
    Baz
};

write an

std::numeric_limits<enum Foo>::max()

specialization (possibly constexpr if you use c++11). Then, in your test code provide any static assertions to maintain the constraints that std::numeric_limits::max() = last_item.

Wojciech Migda
  • 738
  • 7
  • 16
  • 2
    Unfortunately this will not work as per [this answer](http://stackoverflow.com/a/9201960/2016221). – rr- Dec 01 '15 at 22:15
  • An example showing when this fails would be useful. – Wojciech Migda Dec 02 '15 at 10:58
  • 3
    `std::numeric_limits::max()` always returns zero... (see the question for the linked answer) Tested on normal enums (`enum Foo { ... }`), type-hinted enums (`enum class Foo : uint8_t { ... }`) with gcc 5.2.0 @ Linux and MinGW 4.9.3 @ Windows. – rr- Dec 02 '15 at 11:56
  • 1
    (...and in case of `std::numeric_limits::type>::max()`, it returns maximum value of the underlying type i.e. 0xFFFFFFFF for 32-bit integers, which is not useful in this context.) – rr- Dec 02 '15 at 11:58
  • 1
    Strange, because I have working code which does what I described. `namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } ` Then: `namespace std { template<> struct numeric_limits { typedef enum ::gx::DnaNucleobase value_type; static constexpr value_type max() { return value_type::T; } (...) ` And `std::cout << std::numeric_limits<::gx::DnaNucleobase>::max() << std::endl;` prints expected result. Tested with gcc 5.2.1 and 4.8/4.9 flavors. – Wojciech Migda Dec 03 '15 at 17:39
  • 1
    @rr- are you sure that with the specialization of ::max() for this enum type it would still fail? I read your comment as if you had missed the "specialization" in this answer – moala May 03 '17 at 09:49
  • 2
    -1; ping me if you change the answer, so I can undo downvote. This answer is a bad convention to follow. It is a misuse of the concept of `numeric_limits::max()`. The only thing that function could reasonably return is the highest enumerated value. It would return `2`, but the OP (in this specific case) would need it to return `3`. Once you have non-default values for the enum (`FB = 2057`), all bets are off, can't even `+ 1` to hack around the off-by-one error. If there were a `numeric_limits::number_of_elements_of_the_set()` (or a shorter name), that could be used without ambiguity. – Merlyn Morgan-Graham Jun 08 '17 at 20:44
3

Add a entry, at the end of your enum, called Folders_MAX or something similar and use this value when initializing your arrays.

ContainerClass* m_containers[Folders_MAX];
Kevin Doyon
  • 3,464
  • 2
  • 33
  • 38
2

I really do not see any way to really get to the number of values in an enumeration in C++. Any of the before mention solution work as long as you do not define the value of your enumerations if you define you value that you might run into situations where you either create arrays too big or too small

enum example{ test1 = -2, test2 = -1, test3 = 0, test4 = 1, test5 = 2 }

in this about examples the result would create a array of 3 items when you need an array of 5 items

enum example2{ test1 , test2 , test3 , test4 , test5 = 301 }

in this about examples the result would create a array of 301 items when you need an array of 5 items

The best way to solve this problem in the general case would be to iterate through your enumerations but that is not in the standard yet as far as I know

2

I like to use enums as arguments to my functions. It's an easy means to provide a fixed list of "options". The trouble with the top voted answer here is that using that, a client can specify an "invalid option". As a spin off, I recommend doing essentially the same thing, but use a constant int outside of the enum to define the count of them.

enum foobar { foo, bar, baz, quz };
const int FOOBAR_NR_ITEMS=4;

It's not pleasant, but it's a clean solution if you don't change the enum without updating the constant.

BuvinJ
  • 10,221
  • 5
  • 83
  • 96
0

Here is the best way to do it in compilation time. I have used the arg_var count answer from here.

#define PP_NARG(...) \
     PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)

#define PP_ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

#define TypedEnum(Name, ...)                                      \
struct Name {                                                     \
    enum {                                                        \
        __VA_ARGS__                                               \
    };                                                            \
    static const uint32_t Name##_MAX = PP_NARG(__VA_ARGS__);      \
}

#define Enum(Name, ...) TypedEnum(Name, __VA_ARGS__)

To declare an enum:

Enum(TestEnum, 
Enum_1= 0,
Enum_2= 1,
Enum_3= 2,
Enum_4= 4,
Enum_5= 8,
Enum_6= 16,
Enum_7= 32);

the max will be available here:

int array [TestEnum::TestEnum_MAX];
for(uint32_t fIdx = 0; fIdx < TestEnum::TestEnum_MAX; fIdx++)
{
     array [fIdx] = 0;
}