A code doesn't work but the equivalent code works - objective-c

#define CLog( s, ... ) NSLog( #"%#", [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#define PO(x) CLog(##x ": %#", x)
Then I do:
NSString * hello =[NSString stringWithFormat:#"%#, %#",theCatalogData.id,#(theCatalogData.images.count)];
PO(hello);
Works
Of course, a shortened version of this is simply:
PO([NSString stringWithFormat:#"%#, %#",theCatalogData.id,#(theCatalogData.images.count)]);
That doesn't work. I wonder why.
No emergency. No problem. The longer equivalent works anyway. I just want to understand how the compiler parses the macro to see why things don't work.

Since the pre-processor does not really understand syntax in itself, macro parameters containing commas will cause problems.
In the second case, since the parameter contains two commas outside of quotes, the compiler thinks the macro is getting 3 parameters instead of one, and since the macro takes only one parameter, the compiler will complain.
A simplified test case similar to your second case;
#define TEST(a,b,c) a
TEST([d e:#"%#, %#", f, g])
will expand to;
[d e:#"%#, %#"
which shows that the a parameter only contains all characters up to the first un-quoted comma.
In your case, you define the TEST macro to take one parameter and since the pre-processor considers it 3, compilation will fail.

Related

Ignore comma inside #define macro call

I created a special console log function macro. It works successfully except when there's a comma in the parameter, even if it's part of another expression, i.e. not another argument. I think it's due to the fact that macros are expanded at the pre-processing stage, so the semantic analysis hasn't occurred yet to understand that the comma is not another argument. Here is what I mean:
#define FANCY_LOG(message) [MyLogger logDebug:message withClassAndMethodName: __PRETTY_FUNCTION__ lineNumber: __LINE__];
+(BOOL)logDebug:(NSString *)message withClassAndMethodName:(const char *)name lineNumber:(int)lineNumber;
These work:
FANCY_LOG(#"Hello world");
FANCY_LOG([NSString stringWithFormat:#"Hello!"]);
This does not work:
FANCY_LOG([NSString stringWithFormat:#"Hello %#!", planet]);
Although the comma obviously is part of the NSString expression, the macro interprets it as another argument, I get the following error:
Too many arguments provided to function-like macro invocation
Here's what I have tried unsuccessfully (and variants of these):
#define FANCY_LOG(...) [MyLogger logDebug:##__VA_ARGS___ withClassAndMethodName: __PRETTY_FUNCTION__ lineNumber: __LINE__];
#define FANCY_LOG(message) [MyLogger logDebug:#message withClassAndMethodName: __PRETTY_FUNCTION__ lineNumber: __LINE__];
You are doing that wrong. First of all there are lots of great ready solutions so you do not have reinvent the wheel (don't remember for sure but I think CocoaLumberjack is best).
And your logger can look like this (I've got rusty with Objective C):
+(void) function:(char *)methodName
inLine:(int)line
logs:(NSString *)format, ...;
...
#define FANCY_LOG(...) [MyLogger function: __PRETTY_FUNCTION__ \
inLine: __LINE__ \
logs: __VA_ARGS__]
// then usage:
FANCY_LOG(#"Hello %#!", planet);

Parsing file with percent signs (%) in Objective-C

I'm writing a parser for fortune files. Fortune is a small app on *nix platforms that just prints out a random "fortune". The fortune files are straight text, with each fortune being separated by a percent sign on its own line. For example:
A little suffering is good for the soul.
-- Kirk, "The Corbomite Maneuver", stardate 1514.0
%
A man either lives life as it happens to him, meets it head-on and
licks it, or he turns his back on it and starts to wither away.
-- Dr. Boyce, "The Menagerie" ("The Cage"), star date unknown
%
What I've found is that when parsing the file, stringWithContentsOfFile returns a string with the % signs in place. For example:
#"A little suffering is good for the soul.\n\t\t-- Kirk, \"The Corbomite Maneuver\", stardate 1514.0\n%\nA man either lives life as it happens to him, meets it head-on and\nlicks it, or he turns his back on it and starts to wither away.\n\t\t-- Dr. Boyce, \"The Menagerie\" (\"The Cage\"), stardate unknown\n%"
However, when I call componentsSeparatedByCharactersInSet on the file contents, everything is parsed as a string, with the exception of the percent signs, which are NSTaggedPointerString. When I print out the lines, the percent signs are gone.
Is this because the percent sign is a format specifier for strings? I would think in that case that the initial content pull would escape those.
Here's the code:
NSFileManager *fileManager;
fileManager = [NSFileManager defaultManager];
NSStringEncoding stringEncoding;
// NSString *fileContents = [NSString stringWithContentsOfFile:fileName encoding:NSASCIIStringEncoding error:nil];
NSString *fileContents = [NSString stringWithContentsOfFile:fileName usedEncoding:&stringEncoding error:nil];
NSArray *fileLines = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
The used encoding ends up being UTF-8. You can see I have also tried specifying plain ASCII, but it yields the same results.
So the question is, how do I retain the percent signs? Or, may I should use it as the separator character and then parse each of the subsequent results individually.
You are calling NSLog() but passing the line strings as the format string. Something like:
NSLog(lineString);
Therefore, any percent characters in the line strings are interpreted as format specifiers. You should (almost) never pass strings that come from outside sources — i.e. strings which are not hard-coded in your code — as format strings to any function (NSLog(), printf(), +[NSString stringWithFormat:], etc.). It's not safe and you'll sometimes get unexpected results like you've seen.
You should always log a single string like this:
NSLog(#"%#", lineString);
That is, you need to pass a hard-coded format string and use the foreign string as data for that to format.
NSTaggedPointerString is just subclass of NSString. You can use anywhere as NSString.
But in your string
#"A little suffering is good for the soul.\n\t\t-- Kirk, \"The Corbomite Maneuver\", stardate 1514.0\n%\nA man either lives life as it happens to him, meets it head-on and\nlicks it, or he turns his back on it and starts to wither away.\n\t\t-- Dr. Boyce, \"The Menagerie\" (\"The Cage\"), stardate unknown\n%"
sign % is not percent sign. in Objective-C percent sign is declared as double of % mark
#"%%"

Outputting Formatted NSString

UIAlertView *message = [[UIAlertView alloc] initWithTitle:[[LanguageManager sharedLanguageManager] get:#"Notice"]
message:[NSString stringWithFormat:[[LanguageManager sharedLanguageManager] get:#"Notice_Text"]]
delegate:nil
cancelButtonTitle:[[LanguageManager sharedLanguageManager] get:#"Close"]
otherButtonTitles:nil];
Hi, let me explain my codes above. Basically it calls up an UIAlertView with data read from a .plist via my LanguageManager singleton class. The LanguageManager get function basically returns a NSString*. I know I should use the NSLocalizedString class but I had been using this class for a while now, so I had decided to stick to it.
My problem lies with the "message:" parameter. The string I am trying to read contains formatting characters like \n but it does not output correctly and appears as \n instead of a line break when printed. I also get the "Format string is not a string literal" warning. Other parts of the app using similar method to return a string which contains %d or %f works correctly though, just the '\n' character not working.
Does anyone have any idea how I may overcome this?
"\n" is not a "formatting character": the compiler translates it to the appropiate code; the string NEVER contains the "\" and "n" characters.
Thus, if you string comes from a source that is NOT compiled by a (Objective-)C(++) compiler, "\n" will be just the two characters. Nothing will turn them into a newline, unless you do it yourself with something like
NewString=[MyString stringByReplacingOccurrencesOfString:#"\\n" withString:#"\n"];
Note the two different strings: in the first case, "\" prevents the compiler from doing the \n -> newline conversion, while the second string will be an actual newline.
The warning about a non-literal format string is somewhat pointless; I've yet to find a good way to get rid of that one (for now, I just disable it entirely, using -Wno-format-nonliteral on clang++ >= 4.0).

Question about #define statement in Objective-C project

I'm not experienced in C, so I'm not comfortable with this statement in this C / objective-C project.
#define CBLog(...) NSLog(#"%s [%ld]: %#", basename(__FILE__), __LINE__, [NSString stringWithFormat:__VA_ARGS__])
Questions:
the 3 dots (...) are used to indicate CBLog() is a method with parameters ? What do they mean ?
%ld stands for line format ? what's the d in %ld for ?
FILE , LINE and VA_ARGS are default replacement tokens for the C debugger ?
thanks
The ... means the macro accepts any number of arguments.
%ld is a string formatter meaning 'long decimal', where decimal really means integer.
__FILE__ expands to the current file name
__LINE__ expands to the current line number
__VA_ARGS__ expands to the arguments passed to the macro.
The debugger has nothing to do with it. All of this is the preprocessor, except %ld which is string formatting.
... means that any number of arguments can be given.
%ld means signed long, though it's a bit strange as I've never seen signed line numbers.
__FILE__ is the current source file's filename. __LINE__ is the current line number. __VA_ARGS__ are the arguments given to the macro.

Spanish characters replaced with weird string when 'stringWithFormat' is used?

NSString *myString = [NSString stringWithFormat:#"%#",BernabÈu];
NSLog(#"%#", myString);
Above statement prints:
Bernab\u00c8u
Here 'BernabÈu' is Spanish character string.
Why is the "\u00c8u" appended? How to get rid of it?
The because the '\u00c8' is the unicode representation of the E. I don't have the code handy, but you will have to look into using Locale's I think to get it to print with the correct character. But don't worry. Java still understands that this is an E.
(don't have the correct 'E' handy either :-)