4

I may be trying to abuse the preprocessor. I want to see if what I have in mind is even possible.

My class has @properties that all have the same bodies. I want to generate these bodies with a preprocessor macro. E.g.:

- (float) accelerometerSensitivity {
    return [dict floatForSelector:_cmd or:1];
}
- (void) setAccelerometerSensitivity:(float) n {
    [dict setFloat:n forSelector:_cmd];
    [dict writeToFile:[self globalDataFilename] atomically:YES];
}

- (float) returnSpringTension {
    return [dict floatForSelector:_cmd or:0];
}
- (void) setReturnSpringTension:(float) n {
    [dict setFloat:n forSelector:_cmd];
    [dict writeToFile:[self globalDataFilename] atomically:YES];
}
// set*ForSelector methods are in a category on NSMutableDictionary and depend on a function that translates selectors into strings:
// NSString* keyFromSelector(SEL selector);

The idea is that instead of using string literals (or string constants) as keys into the dictionary, I derive the string from the selector name. This way I am sure that the spelling of the key matches the property name and essentially get the benefit of compile-time validation of dictionary keys.

What I want to do is say something like SELECTOR_PROPERY(accelerometerSensitivity) and have it expand into the the getter and the setter. The main difficulty I have in implementing this as a pre-processor macro is generating the setter name from the property name. I need to uppercase the first letter of the property name, and I have no idea how to do that in the preprocessor.

3 Answers 3

5

Nope, you can't do that.

But, you can combine identifiers, so in theory you could define this as:

MACRO(A,a,ccelerometerSensitivity)

It's somewhat klugey, but it's more terse than the alternative.

Sign up to request clarification or add additional context in comments.

1 Comment

Yeah, that's the fallback I'm thinking of. Thank you for confirming that CPP doesn't have the expressive power to do the kind of manipulation that I want.
0

Here's how I'd do it:

#define MACRO(_a) { \
const char *name = #_a; \
NSString *getterName = [NSString stringWithUTF8String:name]; \
NSString *setterName = [NSString stringWithFormat:@"set%c%s:", toupper(name[0]), (name+1)]; \
NSLog(@"getter name: %@", getterName); \
NSLog(@"setter name: %@", setterName); \
}

Basically, you stringify the macro parameter, then use a simple C function to uppercase the first letter, and use an offset to get everything after the first letter.

Now when you do this:

MACRO(foo);
MACRO(bar);

It logs this:

2011-07-19 21:21:24.798 EmptyFoundation[16016:903] getter name: foo
2011-07-19 21:21:24.800 EmptyFoundation[16016:903] setter name: setFoo:
2011-07-19 21:21:24.801 EmptyFoundation[16016:903] getter name: bar
2011-07-19 21:21:24.802 EmptyFoundation[16016:903] setter name: setBar:

HOWEVER, these are strings. You can't use them as method names. Sorry. :(

Comments

0

Actually, you probably really don't want to do this purely for architectural reasons.

You'll likely be better off if you:

  • separate the notion of setting state from persisting state. That you are causing I/O with every single tiny little change is horribly inefficient. It is also a mode rife with potential for problems; what happens if you move to a UI where the values track the UI continuously? ... you really don't want disk I/O for every time a dial/slider is tracked under a finger!

  • use @synthesize for all your @properties and don't even declare ivars. Leverage the tool's ability to generate exactly correct setters / getters for you.

  • that code looks an awful lot like you've re-invented NSUserDefaults? Use NSUserDefaults for any user preference kinda stuff.

11 Comments

My question doesn't ask what I want to do. It says what I want to do. --- The code that I want to generate is code that is already in the source and it performs to my satisfaction. I am only asking how to generate it instead of copy-pasting it. --- There are no ivars in this example except the dictionary. This is a class I go to when I'm ready to persist user settings. I support your notion of separating state and persistence--this is the persistence part. --- NSUserDefaults is not the answer. My UI is more sophisticated and I want it to live inside the app, not in a corner of the Settings.
NSUserDefaults is perfectly usable from your app. It does not require going to Settings. That is the entire point of user defaults. And user defaults generally avoids spamming the disk every time the state changes.
Thank you for your input. It flatters me that you know what is usable in my app. I have limited experience with NSUserDefaults, but where I have used it, it creates UI elements in the Settings app (this is all on iOS). In any case, NSUserDefaults uses string keys to look up values, and the whole point of my question is that I want to avoid using string literals (or constants that resolve to literals) as keys. My experience is that with more than a dozen of these string keys in the application, spelling errors become a nuisance. If I use NSUserDefaults, only the body of the macro changes.
@iter "...it creates UI elements in the Settings app" is a totally false statement. The only elements shown in Settings app are the ones defined in your Settings.bundle. Also if you're worried about spelling errors, then define the strings as constants and refer to them by name. poof spelling problem solved, because if you mis-spell a constant name, the compiler catches it for you.
If you look at how Apple does it, there is typically a global constant or #define like kNNLogIdentifier that has a value like @"AppKit Logging Identifier";. The variable used in code is indicative of its role and source while the compiled value is more descriptive.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.