Converting between C enum and XML

前端 未结 5 354
遇见更好的自我
遇见更好的自我 2020-12-04 20:21

What\'s the cleanest way to store an enum in XML and read it back out again? Say I\'ve got:

enum ETObjectType {ETNormalObjectType, ETRareObjectType, ETEssent         


        
相关标签:
5条回答
  • 2020-12-04 21:02

    I haven't found a better way than duplicating the enum in a string. However, I do it slightly differently, namely:

    typedef enum {
        kManipulateWindowTargetFrontWindow,
        kManipulateWindowTargetNamedWindow, 
        kManipulateWindowTargetWindowNameContaining, 
        kManipulateWindowTargetDEFAULT = kManipulateWindowTargetFrontWindow, 
    } ManipulateWindowTargetType;
    #define kManipulateWindowTargetTypeNamesArray @"FrontWindow", @"NamedWindow", @"WindowNameContaining", nil
    

    then in the implementation:

    static NSArray* kManipulateWindowTargetTypeArray = [[NSArray alloc] initWithObjects: kManipulateWindowTargetTypeNamesArray];
    
    NSString* ManipulateWindowTargetTypeToString( ManipulateWindowTargetType mwtt )
    {
        return [kManipulateWindowTargetTypeArray objectAtIndex:mwtt];
    }
    
    ManipulateWindowTargetType ManipulateWindowTargetTypeFromString( NSString* s )
    {
        NSUInteger n = [kManipulateWindowTargetTypeArray indexOfObject:s];
        check( n != NSNotFound );
        if ( n == NSNotFound ) {
            n = kManipulateWindowTargetDEFAULT;
        }
        return (ManipulateWindowTargetType) n;
    }
    

    The reason I use the #define is to avoid declaring the array in the header file, but it would be insane to separate the definition of the enum from the definition of the sequence of strings, so this is the best compromise I've found.

    Since the code is boilerplate, you can actually make them a category on NSArray.

    @interface NSArray (XMLExtensions)
    
    - (NSString*) stringWithEnum: (NSUInteger) e;
    - (NSUInteger) enumFromString: (NSString*) s default: (NSUInteger) def;
    - (NSUInteger) enumFromString: (NSString*) s;
    
    @end
    
    @implementation NSArray (XMLExtensions)
    
    - (NSString*) stringWithEnum: (NSUInteger) e;
    {
        return [self objectAtIndex:e];
    }
    
    - (NSUInteger) enumFromString: (NSString*) s default: (NSUInteger) def;
    {
        NSUInteger n = [self indexOfObject:s];
        check( n != NSNotFound );
        if ( n == NSNotFound ) {
            n = def;
        }
        return n;
    }
    
    - (NSUInteger) enumFromString: (NSString*) s;
    {
        return [self enumFromString:s default:0];
    }
    
    
    @end
    

    and then:

    NSLog( @"s is %@", [kManipulateWindowTargetTypeArray stringWithEnum:kManipulateWindowTargetNamedWindow] );
    ManipulateWindowTargetType mwtt = (ManipulateWindowTargetType)[kManipulateWindowTargetTypeArray enumFromString:@"WindowNameContaining" default:kManipulateWindowTargetDEFAULT];
    NSLog( @"e is %d", mwtt );
    
    0 讨论(0)
  • 2020-12-04 21:03

    I'm experimenting with this solution -

    static NSString *stepTypeEnum[kStepTypeCount] = {@"one",@"two",@"three",@"four"};
    
    int enumFromStrings(NSString*findString,NSString *strings[],int enumMax){
        for (int i=0;i<enumMax;i++){
            if ([findString isEqual: strings[i]]) {
                return i;
            }
        }
        NSLog(@"enum was not found for string %@", findString);
        assert(false);
        return INT_MAX;
    }
    

    I like it because it checks the length of the string array at compile time, and the enumFromStrings function is generic and reusable. You call it like so:

    -(void)setType:(NSString*)typeString{
        type = enumFromStrings(typeString,stepTypeEnum,kStepTypeCount);
    }
    
    0 讨论(0)
  • 2020-12-04 21:06

    Here's how I typically write these styles of methods:

    #define countof(array) (sizeof(array)/sizeof(array[0]))
    
    enum {
        ETNormalObjectType,
        ETRareObjectType,
        ETEssentialObjectType
    };
    typedef NSInteger ETObjectType;
    
    NSString *ETObjectTypesAsStrings[] = {[ETNormalObjectType] = @"ETNormalObjectType", 
                                          [ETRareObjectType] = @"ETRareObjectType", 
                                          [ETEssentialObjectType] = @"ETEssentialObjectType"};
    
    NSString *ETStringFromObjectType(ETObjectType type) {
        return ETObjectTypesAsStrings[type];
    }
    
    ETObjectType ETObjectTypeFromString(NSString *string) {
        NSString *match = nil;
        for(NSInteger idx = 0; !match && (idx < countof(ETObjectTypesAsStrings)); idx += 1) {
            if ([string isEqualToString:ETObjectTypesAsStrings[idx]]) {
                match = ETObjectTypesAsStrings[idx];
            }
        }
        return match;
    }
    

    You end up having to put your enumeration values in two places, the original enumeration, and the array that maps integer values to their string names. The two functions that actually do the mapping don't have copies of the maps though.

    0 讨论(0)
  • 2020-12-04 21:06

    I echo Jon's solution, but you can use the dreaded X-macro to avoid repeating yourself at all. I don't know how to comment on Jon's answer with code formatting, so here it is as a new answer.

    #define ETObjectTypeEntries \
    ENTRY(ETNormalObjectType) \
    ENTRY(ETRareObjectType) \
    ENTRY(ETEssentialObjectType)
    
    typedef enum ETObjectType {
    #define ENTRY(objectType) objectType, 
        ETObjectTypeEntries
    #undef ENTRY
    } ETObjectType;
    
    NSString *ETObjectTypesAsStrings[] = {
    #define ENTRY(objectType) [objectType] = @"" # objectType, 
        ETObjectTypeEntries
    #undef ENTRY
    };
    
    #define countof(array) (sizeof(array)/sizeof(array[0]))
    
    NSString *ETStringFromObjectType(ETObjectType type) {
        return ETObjectTypesAsStrings[type];
    }
    
    NSString *ETObjectTypeFromString(NSString *string) {
        NSString *match = nil;
        for(NSInteger idx = 0; !match && (idx < countof(ETObjectTypesAsStrings)); idx += 1) {
            if ([string isEqualToString:ETObjectTypesAsStrings[idx]]) {
                match = ETObjectTypesAsStrings[idx];
            }
        }
        return match;
    }
    
    0 讨论(0)
  • 2020-12-04 21:13

    The great thing of XML is that it can transform to nearly anything, even to code. It just takes (a lot of) effort to make the translation once. I have worked in several projects where XML was translated to code. And this saved a lot of time. This technique is for instance, covered in chapter 12 of the book "XSLT Cookbook 2nd edition, S. Mangano, O'Reilley".

    It's not an easy solution, but if you have a good mapping you - have a single point of definition (your xml) - can generate .h files with enum - can generate tables or functions to read/write values in xml

    It depends on the number of enums and how often they change if it is worthwhile. Good luck!

    0 讨论(0)
提交回复
热议问题