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
A solution to this problem is to ditch the enums, and replace it with some arrays which are created using XMACROs.
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
.
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