9

i have some enum class

enum class Foo { A=1, B=18 , Z=42 };

i want to check if some integer can be converted into a Foo. What would be the ideal way to do this? this is for runtime check (the integer is not known yet at compile-time)

Obviously i can do this the hard way (write a function bool CheckEnum(Foo); with a big-ass switch returning true for all cases except the default one), but i was hoping a more elegant mechanism that avoided so much writing. MPL or Boost.Preprocessor would be a perfectly acceptable solution, but one of which i sadly know very little about

ildjarn
  • 62,044
  • 9
  • 127
  • 211
lurscher
  • 25,930
  • 29
  • 122
  • 185
  • possible duplicate of [How to check if enum value is valid?](http://stackoverflow.com/questions/4969233/how-to-check-if-enum-value-is-valid) – John Zwinck Nov 22 '11 at 03:57
  • @JohnZwinck, almost; this is the c++11 enum class, which supposedly is type-safe-er – lurscher Nov 22 '11 at 03:59
  • C++11 doesn't bring you any new feature for what you want to do. So you're stuck with what the other poster got. I wish I had better news, truly! – John Zwinck Nov 22 '11 at 04:01
  • @lurscher: It is more type-safe. But it isn't *magic*. The only way for you to put a value in an `enum class` variable that is not one of the enumerators is for you to *subvert the type system* by using a cast. That's really about as type-safe as it's going to get in C++: if you play by the rules, it works. If you start casting random integers to an enumerator, you've deliberately given up your guarantees. – Nicol Bolas Nov 22 '11 at 04:14

3 Answers3

2

A solution to this problem is to ditch the enums, and replace it with some arrays which are created using XMACROs.

EvilTeach
  • 28,120
  • 21
  • 85
  • 141
0

There is no "ideal" way to do it. All ways are going to have to involve some manual work.

You need to create a data structure that contains all of the acceptable values. Then search that data structure with the runtime value you need. A std::set or std::unordered_set would be adequate for this purpose.

Your main difficulty will be in maintaining that list, as it will need to be updated every time you change your enum class.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
0

Ok i'm a little bit fed up with this issue (some of my enums are nearly 100 items) so i decided to tackle it with code generation, which might not be everyone's cup of tea, but i've realised that is really no such a big deal.

Basically i went for Python Cog, which allows me to embed python snippets inside comments in my .h and .cpp files and auto-generate code. I use it basically like a really smart, imperative macro system:

i added the following to Test.h

/*[[[cog
#----------- definitions

import cog

def createCategoryConstants( enumVar , bitShift ):
    categoryIndex = 0
    for cat in enumVar:
        cog.outl(' const unsigned int %s_op_mask = (%d << %d); ' %(cat[0] , categoryIndex , bitShift))
        categoryIndex += 1
    cog.outl('\n\n')

def createMultiCategoryEnum( enumVar , enumTypename ):
    cog.outl(' enum class %s { ' % enumTypename )
    categoryIndex = 0
    for i in enumVar:
        itemIndex = 0
        catName = 'NotExpected'
        remainingCategories = len(enumVar)- categoryIndex - 1
        for j in i:
            if (itemIndex == 0):
                catName = j
                itemIndex = 1
                continue
            enumItemIndex = 0
            for enumItem in j:
                remainingEnums = len(j) - enumItemIndex - 1
                currentLine = ' %s = %s_op_mask | %d ' %(enumItem, catName, enumItemIndex)
                if (remainingCategories != 0 or remainingEnums != 0):
                    currentLine += ' , '
                cog.outl(currentLine)
                enumItemIndex += 1
            itemIndex += 1
        cog.outl('') #empty line to separate categories
        categoryIndex += 1
    cog.outl(' };\n\n')

def createIndexFromEnumFunction( enumVar , enumTypename , functionName ):
    cog.outl('uint32_t %s(%s a) { \n switch (a)\n {' % (functionName , enumTypename) )
    absoluteIndex = 0
    for cat in enumVar:
        elemInCat = 0
        for i in cat:
          if elemInCat != 0:
             for enumItem in i:
               cog.outl('case %s:' % enumItem)
               cog.outl(' return %d; \n' % absoluteIndex)
               absoluteIndex += 1
          elemInCat += 1
    cog.outl(' } \n } \n\n ')


def createMultiEnum( enumVar , enumTypename ):
    createCategoryConstants( enumVar , 4)
    createMultiCategoryEnum( enumVar , enumTypename )
    createIndexFromEnumFunction( enumVar , enumTypename , 'FromOpToIndex' )

#------------- generation

multiEnum =[ ['CatA', ['A1', 'A2' , 'A3_foo']] , ['CatSuper8' , ['Z1_bla' , 'Z10' , 'Z11']] ]

createMultiEnum( multiEnum , 'multiFooEnum')

]]]*/
//[[[end]]]

Then i added cog invocation in my Makefile pre-build step:

.build-pre:
# Add your pre 'build' code here...
    python /usr/local/bin/cog.py -I../../../tools/cog/ -r *.h

And the results show up just below:

]]]*/
 const unsigned int CatA_op_mask = (0 << 4); 
 const unsigned int CatSuper8_op_mask = (1 << 4); 



 enum class multiFooEnum { 
 A1 = CatA_op_mask | 0  , 
 A2 = CatA_op_mask | 1  , 
 A3_foo = CatA_op_mask | 2  , 

 Z1_bla = CatSuper8_op_mask | 0  , 
 Z10 = CatSuper8_op_mask | 1  , 
 Z11 = CatSuper8_op_mask | 2 

 };


uint32_t FromOpToIndex(multiFooEnum a) { 
 switch (a)
 {
case A1:
 return 0; 

case A2:
 return 1; 

case A3_foo:
 return 2; 

case Z1_bla:
 return 3; 

case Z10:
 return 4; 

case Z11:
 return 5; 

 } 
 } 


//[[[end]]]

So, now my enum validation is about making sure the code generation (invoked at compile time) is done correctly

lurscher
  • 25,930
  • 29
  • 122
  • 185
  • I've seen the code generation solution done multiple times. A few more tricks you can do: convert to/from strings, to/from integer values (with validation/assert when invalid), and enclose it in a struct, automatically generate max/invalid constants (with a consistent name for all your enums). It's a swell thing. – blais Aug 08 '12 at 19:06