15

I have an enum:

enum myenum{
  typeA,
  typeB,
  typeC
} myenum_t;

Then, a functions is to be called with an enum parameter:

int myfunction(myenum_t param1)
{
  switch(param1)
  {
    case typeA:
    case typeB:
    case typeC:
      //do the work
      break;

    default:
      printf("Invalid parameter");
  }
  return 0;
}

But, as myenum_t grows with more and more values, myfunction doesn't seem so elegant.

Is there a better way to check if an enum is valid or not?

hmjd
  • 120,187
  • 20
  • 207
  • 252
mustafa
  • 3,605
  • 7
  • 34
  • 56
  • Not having a copy of the standard, I would get ripped for saying this without quoting it, so I'll make it a comment: In every C or C++ implementation I have seen, `enum` values are allocated in increasing numeric order. So all you need to do is add `firstEnum = typeA, lastEnum = typeC` to your `enum` and then range check with `if(int(inputEnum) < int(firstEnum) || int(inputEnum) > int(lastEnum)) { /* handle error */ }`. – Mike DeSimone Feb 06 '12 at 14:49

7 Answers7

23

A common convention for this is to do something like this:

typedef enum {
  typeA,
  typeB,
  typeC,
  num_types
} myenum_t;

Then you can check for (t < num_types).

If you subsequently add more enums, e.g.

typedef enum {
  typeA,
  typeB,
  typeC,
  typeD,
  typeE,
  num_types
} myenum_t;

then num_types is automatically updated and your error checking code doesn't need to change.

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • What does the `myenum_t` mean after your enum declaration? The [C# Reference](http://msdn.microsoft.com/en-us/library/sbbt4032.aspx) doesn't seem to mention it; none of their examples have anything after the closing brace. – Patrick M Aug 12 '13 at 17:02
  • @PatrickM it is required in C but not in C++ or C# due to the fact that in C you can't use an enum tag as a type. However to make sense in the above answer I'd expect to see the `typedef` keyword before the `enum` keyword, declaring `myenum_t` to be the same type as `enum myenum`. – j b Nov 15 '13 at 18:27
  • If that enum is public facing, then you're exposing num_types to the world. Is there any way around that? – duhanebel May 06 '15 at 14:14
  • 1
    @duhanebel: well it needs to be exposed, so that it can be used for error checking (the whole point of the question, after all), and it is also useful for defining array sizes, etc. Obviously you would use a name other than `num_types`, though, i.e. something appropriate to your particular `enum` definition. – Paul R May 06 '15 at 14:17
  • @PaulR correct me if I'm wrong but I think `num_types` doesn't need to be exposed to the public API if all you need is to do some sanity check on the implementation of the function that accepts the enum as a parameter. All you'd need is a enum.count (which is not available on c/objc). – duhanebel May 06 '15 at 14:52
  • 2
    @duhanebel: it depends on how widely used the enum is - if it's defined in a common header file and used in multiple APIs then all the modules that comprise those APIs might want to use num_types for sanity checking, validation, etc. There's also my additional point that it is very useful for defining array sizes etc, where e.g. an array dimension needs to correspond to the no of types in an enum. – Paul R May 06 '15 at 15:01
6

You could use a bitwise enum:

enum myEnum {
    typeA = 1 << 0;
    typeB = 1 << 1;
    typeC = 1 << 2;
}

int myFunction(myEnum arg1)
{
    int checkVal = typeA | typeB | typeC;

    if (checkVal & arg1)
    {
        // do work here;
    }
    else
    {
        printf("invalid argument!");
    }

    return 0;
}

Excuse me, it seems I have read the question wrong.

It appears what you want to do is determine if you have a proper value passed in, not some random invalid option. In that case, the most logical option is this:

if (arg1 < typeA || arg1 > typeC)
    printf("invalid argument");

This is, of course, assuming you don't set manual values for your enum, which, is quite rare unless using bitwise enums.

Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
  • 4
    "But, as myenum_t grows with more and more values". Isn't there a chance of overflow with `1 << n`? – Luchian Grigore Feb 06 '12 at 14:54
  • @LuchianGrigore Sort of. you must add the variable name to the or statement, but if you forget to do that, then no. This is just a more concise way of checking if it is of a set of three values. And yes, it could overflow, depending on the integer size on the machine you were developing for. However, in my experience, it is rare to have to deal with more than 32 different values in an enum, which is a good estimate of the maximum you would have. – Richard J. Ross III Feb 06 '12 at 14:56
  • 1
    Seems a bit of overkill, but that's just me. – Luchian Grigore Feb 06 '12 at 14:57
  • 1
    The thing is, you can't rely on this. It leads to UB. So you might be unlucky and have it working on your config, but when shipping to a client, it will crash. I would avoid this. – Luchian Grigore Feb 06 '12 at 15:03
4

Yes.

Let the compiler do its work, don't cast int to an enum type and you should be good.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
1

Unfortunately there isn't a simple way to do it language level (at least with C), you just have to make sure you are using only variables defined via enum.

Although you could enable one of the following compiler warnings together with -Werror:

  • -Wswitch
  • -Wswitch-default
  • -Wswitch-enum

This makes build fail if one of the enums is missed inside switch.

plaes
  • 31,788
  • 11
  • 91
  • 89
1

One trick I've used in the past:

enum foo {FIRST_FOO, BAR, BLETCH, BLURGA, BLAH, LAST_FOO};

and then check to see if your value is > FIRST_FOO && < LAST_FOO1.

Of course, this assumes that there are no gaps between your enumeration values.

Otherwise, no, there's no good method to do what you're asking (at least in C).


1 From the latest online C Language Standard:
6.7.2.2 Enumeration specifiers
...
3 The identifiers in an enumerator list are declared as constants that have type int and may appear wherever such are permitted.109) An enumerator with = defines its enumeration constant as the value of the constant expression. 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. (The use of enumerators with = may produce enumeration constants with values that duplicate other values in the same enumeration.) The enumerators of an enumeration are also known as its members.
John Bode
  • 119,563
  • 19
  • 122
  • 198
0

Can't you also do something like

enum myEnum {typeA,typeB, typeC};

int myFunction (myEnum arg1) {
    if (arg1 >= typeA && arg1 <= typeC) {
        // do work here
    } else {
        printf("invalid argument!");
    }
    return 0;
}
vextorspace
  • 934
  • 2
  • 10
  • 25
  • Is there a chance that compiler optimizes out the checks? If a *LastType* is added then check `arg1 < LastType` should not get optimized out, similarly a *FirstType* might be needed. – dashesy Jul 13 '12 at 18:33
0

Enumerations in C++ already have stronger types than in C.

Take the following simple program:

#include <iostream>

enum E
{
    A,
    B 
};

void f(E e)
{
}

int main()
{
    f(1);
}

Using the GCC compiler I will get a this error:

enum.cpp: In function ‘int main()’:
enum.cpp:15: error: invalid conversion from ‘int’ to ‘E’
enum.cpp:15: error:   initializing argument 1 of ‘void f(E)’

So as you can see the enumeration members are already checked.

If you want even stronger type checking, and have a C++11 capable compiler, you can use even stronger type checking for enums, see http://en.wikipedia.org/wiki/C%2B%2B11#Strongly_typed_enumerations.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    @LightnessRacesinOrbit I really have no idea what it's supposed to be, my memory is a little hazy after almost four years... :) It's *probably* supposed to an integer literal. – Some programmer dude Dec 01 '15 at 15:25