Search code examples
c++c++11enum-class

validate integer is some enum class item (C++11)


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


Solution

  • 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