Question

I'm trying to implement a specialized typed logger that takes a dictionary as input and creates string of values out of it.

One sample such dictionary might be:

CGSize size = {300, 200} ;
CGAffineTransform t = ::CGAffineTransformIdentity ;

[self logit: @{
    @"columns": @{
        @"value": @3
    ,   @"type": @"NSNumber"
    }
,   @"size": @{
        @"value": [NSValue valueWithCGSize(size)]
    ,   @"type": @"CGSize"
    }
,   @"transform": @{
        @"value": [NSValue valueWithCGAffineTransform(t)]
    ,   @"type":    @"CGAffineTransform"
    }
}] ;

The issue is: how do I retrieve those values in a generic way?

I could obviously write

    for (NSString * key in dict) {
        NSDictionary * params = dict[key] ;

        if ([params[@"type"] isEqualToString: @"CGRect"]) {
            CGRect r = [params[@"value"] CGRectValue] ;
            NSString * s = ::NSStringFromCGRect(r) ;
            ::NSLog(s) ;
        } else if ([params[@"type"] isEqualToString: @"NSNumber"]) {
            ::NSLog(@"%.3f", [params[@"value"] floatValue]) ;
        } else if ([params[@"type"] isEqualToString: @"CGAffineTransform"]) {
            CGAffineTransform t = [params[@"value"] CGAffineTransformValue]
            NSString * s = ::NSStringFromCGAffineTransform(t) ;
            ::NSLog(s) ;
        }
        ...
    }

But I'd rather write a more "generic" solution. Something along the lines:

SEL valueSelector = ::NSSelectorFromString([NSString stringWithFormat:@"%@Value", params[@"type"]) ;
NSString * rawCFunctionName = [NSString stringWithFormat:@"NSStringFrom%@", params[@"type"]] ;

So now I have a selector and the name of a C function.

  1. How do I go from a string to a C function that I can actually call?

  2. using the selector above is unclear as its return type can vary from id e.g.: in the case of NSNumber * to "plain C object" e.g. in the case of CGRect, CGSize etc ...

Any suggestion?

Was it helpful?

Solution

  1. You can't; see "Call a function named in a string variable in C".

  2. Yup, you have to know the return type at compile time.

All is not lost, however: what you're trying to do can actually be accomplished in a fairly mundane way. NSValue has the very nice feature that its description method gives output very similar to the appropriate NSStringFrom...() function for the type it's storing (it's probably using them under the hood). For example:

NSLog(@"%@", [NSValue valueWithCGAffineTransform:CGAffineTransformMake(10, 10, 10, 10, 10, 10)]);
NSLog(@"%@", [NSValue valueWithCGSize:CGSizeMake(10, 10)]);

yields:

2013-08-11 03:06:43.053 PrintingNSValue[51538:11303] CGAffineTransform: {{10, 10, 10, 10}, {10, 10}}
2013-08-11 03:06:43.055 PrintingNSValue[51538:11303] NSSize: {10, 10}

NSNumber will likewise give a nice representation of its value as its description.

So all you have to do is something like this:

for( NSString * key in theDictionary ){
    NSLog(@"%@: %@", key, theDictionary[key][@"value"]);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top