OBJ C Macro by Stringname - objective-c

I try to get a macro by NSString value:
Example:
#define FOO1 NSLocalizedString (#"TEXT",#"COMMENT")
#define FOO2 NSLocalizedString (#"TEXT2",#"COMMENT2")
in .m File:
NSString *macroName = [NSString stringWithFormat:"FOO%d",1];
label.text = MACROBYNAME(macroName);
is there a way to do something like this?
Another approach to solution is a Switch-Case in the macro like this:
#define FOOSELECTOR(x) Switch x
case "FOO1"
NSLocalizedString (#"TEXT",#"COMMENT")
break;
case "FOO2"
NSLocalizedString (#"TEXT2",#"COMMENT2")
break;
or something like that... any Ideas?

Okay, I'll bite and make this an answer instead of a comment:
No, it's not possible. There's no way to do what you want to do.
Macros are evaluated during compile time, that's when you hit cmd-b in Xcode. Functions are called during runtime. That's when the program is run, possibly years after compilation.
Furhtermore, why do you want to do it? What's wrong with
NSString *localizedString = NSLocalizedString (#"TEXT",#"COMMENT");
label.text = [NSString stringWithFormat:#"%#%d", localizedString, 1];
?

Related

Objective-C macro to detect if variable is primitive

I'm looking a macro to detect if a variable is an object or a primitive in Objective-C.
In this context, I know the parameter must be a variable and will never be an expression.
Here is the best thing I've come up with:
#define IS_OBJECT(x) ( #encode(__typeof__(x))[0] == '#' )
#define IS_PRIMITIVE(x) ( !IS_OBJECT(x) )
Usage:
NSString *testString = #"test";
NSString *nilString = nil;
NSInteger testInteger = 1;
STAssertTrue(IS_OBJECT(testString), #"IS_OBJECT(testString) must be YES");
STAssertTrue(IS_OBJECT(nilString), #"IS_OBJECT(nilString) must be YES");
STAssertFalse(IS_OBJECT(testInteger), #"IS_OBJECT(testInteger) must be NO");
There must be a better way.
Update
Considering #ChrisDevereux comment, I updated the IS_OBJECT macro.
#define IS_OBJECT(x) ( strchr("##", #encode(__typeof__(x))[0]) != NULL )
It now passes:
NSString *testString = #"test";
NSString *nilString = nil;
NSInteger testInteger = 1;
Class classTest = [NSString class];
STAssertTrue(IS_OBJECT(testString), #"IS_OBJECT(testString) must be YES");
STAssertTrue(IS_OBJECT(nilString), #"IS_OBJECT(nilString) must be YES");
STAssertFalse(IS_OBJECT(testInteger), #"IS_OBJECT(testInteger) must be NO");
STAssertTrue(IS_OBJECT(classTest), #"IS_OBJECT(classTest) must be YES");
I still don't like this answer, and hope there is something slicker. Is there something in the runtime library which does this?
Here's another way using C11's generic selection mechanism. _Generic is standard (modern) C and supported in clang for a while.
#define IS_OBJECT(T) _Generic( (T), id: YES, default: NO)
It feels a bit less runtime-ish to me so I prefer it over the #encode way. But to be honest I just used it for this answer because I love the power _Generic gives to macros and think more people should start using it. If you don't know it you should read Robert Gamble's article linked above.

Why can I define a string as #"this is" #"one string"?

This seems to be a perfectly valid program:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSString* hey = #"hey"
#"there";
NSLog(#"%#",hey);
[pool drain];
return 0;
}
Compiling this in llvm gcc and apple llvm shows no warnings or errors. Why? It seems one should be warned about this, as I can only see this as chaos-inducing. Especially in a situation like this:
NSArray* a = [NSArray arrayWithObjects:#"one",
#"two,
#"three
#"four",
nil];
You'd expect four entries...but no! Tricky, huh? Is it because you may want to split your string definition across multiple lines?
It's a syntax for multi-line strings in Objective-C.
I cannot definitively answer why the language designer(s) designed it that way, but we can probably assume that they wanted the syntax for Objective-C strings to be analogous to the syntax for C strings.
That is, in C you can do the following for a multi-line string:
char *my_string = "Line 1 "
"Line 2";
And in Objective-C, you can do the following for a multi-line string:
NSString *my_string = #"Line1 "
#"Line2"; // The # on this line is optional.
(Code snippets adapted from https://stackoverflow.com/a/797351/25295)
Is it because you may want to split your string definition across multiple lines?
Yes. It is useful when you want to split the very long string for better code reading i.e. in NSLocalizedString key description.

objC Preprocessor NSString Macro

I have a problem to create a preprocessor macro function, that concatenates two Strings and "return" a NSString (#"...") value.
Here is what I tried:
#define ObjectKeyMake(NAME) #"com.test.##NAME"
if I print the result from a call I get:
NSLog(#"%#", ObjectKeyMake(foo)); // com.test.##NAME
so my question is: How can i concatenate 2 Strings in a preprocessor macro and "return" a NSString (#"..") ?
and no I can't use #define ObjectKeyMake(NAME) [#"com.test." stringByAppendingString: NAME] because i need a compile-time constant.
You can take advantage of the fact that the compiler combines string literals that are next to each other, like this:
NSString* greeting = #"Hello, " "world";
The macro implementation would look like this:
#define ObjectKeyMake(NAME) (#"com.test." #NAME)
#define ObjectKeyMake(NAME) #"com.test."#NAME

Define macro expected expression issues

I want to create an macro for trimming my string.
I use this code below for trimming:
[[NSString stringWithString:string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]]
But if I create macro as below I get an error: expected expression
#define TRIM_STRING(string) ([[NSString stringWithString:string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]])
How to correctly create same define macro?
As a macro:
#define TRIM_STRING(string) [(string) stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]
However, there's really no reason not to use an inline function here. You get type checking, and the compiler will give you error messages that make sense. The same as an inline function:
NSString * NSStringTrim(NSString *string)
{
return [string stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
Or, even better, create a category on NSString for this:
#interface NSString (additions)
- (NSString *)stringByTrimmingWhitespace;
#end
#implementation NSString (additions)
- (NSString *)stringByTrimmingWhitespace
{
return [self stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
#end
This way you get namespacing, and you can call it by doing
[string stringByTrimmingWhitespace]
which is neater and more consistent with Cocoa.
Get rid of your starting and ending brackets. You've one pair too many.
Then get rid of "NSString stringWithString:string" and replace it with string. This was in itself erroneous because you didn't have it in brackets, but it's academic; you don't need to make a copy of the string anyway.
Edit:
It's a good idea, as your argument to your macro is in parentheses, to place it in parentheses wherever it occurs in your macro. This can avoid problems (if you specify a complex expression as an argument) as the preprocessor expands your macro.

Are strings defined as constants not NSStrings?

I have a constant defined as:
#define BEGIN_IMPORT_STRING #"Importing Hands!";
But I get an error when I try to concat with:
NSString *updateStr = [NSString stringWithFormat:#"%#%#", BEGIN_IMPORT_STRING, #" - Reading "];
This doesn't happen if I replace it with a string literal
NSString *updateStr = [NSString stringWithFormat:#"%#%#", #"foo", #" - Reading "];
Or a local string
NSString *temp = #"foo";
NSString *updateStr = [NSString stringWithFormat:#"%#%#", temp, #" - Reading "];
You need to remove the semicolon from your #define:
#define BEGIN_IMPORT_STRING #"Importing Hands!"
To the compiler, the resulting line looks like this:
NSString *updateStr = [NSString stringWithFormat:#"Importing Hands!";, #" - Reading "];
Replace
#define BEGIN_IMPORT_STRING #"Importing Hands!";
with
#define BEGIN_IMPORT_STRING #"Importing Hands!"
This is because compiler in your case replaces all occurrences of BEGIN_IMPORT_STRING with #"Importing Hands!";
Aside from the accepted answer (remove semicolon), note that:
#"Foo" is an NSString. You can even send it a message.
#define FOO #"Foo" is a preprocessor macro, not a constant. It's a typing shortcut.
Though macros aren't an uncommon way to avoid retyping the same string, they're an unfortunate holdover. Essentially, they're playing games that aren't necessary anymore.
For repeated strings, I prefer:
static NSString *const Foo = #"Foo;
The const portion of this definition ensures that the pointer is locked down, so that Foo can't be made to point to a different object.
The static portion restricts the scope to the file. If you want to access it from other files, remove the static and add the following declaration to your header file:
extern NSString *const Foo;
Should you be using
NSLocalizedString(#"Importing Hands!", #"Message shown when importing of hands starts");
?
I put it as an answer because this looks like something you would not want to have to go and redo through all your code.