'va_start' used in function with fixed args - objective-c

I have Logger.h with following code:
#import <Foundation/Foundation.h>
#import <asl.h>
#define LogDebug(format, ...){ \
Logger(ASL_LEVEL_DEBUG, format,##__VA_ARGS__); }
#define Logger(LEVEL,format, ...) \
LogLocal(LEVEL,format,##__VA_ARGS__);
#define LogLocal(LEVEL, format, ...) \
va_list arg_list; \
va_start(arg_list, format); \
// ...\
va_end(arg_list); \
I call log from Obj-C as:
LogDebug(#"Name is called with flag: %#", collectName ? #"YES" : #"NO");
However I get an error:
'va_start' used in function with fixed args
How to get rid of this problem?
EDIT 1:
I tried to call also: AFLogLocal(LEVEL,format,...); - same error
EDIT 2:
if I'll remove asl_log and replace with NSLog - it will work:
#define LogLocal(LEVEL,format, ...) \
NSLog((#"XXX: %s " format), __PRETTY_FUNCTION__, ##__VA_ARGS__);
EDIT 3
credits to #Amin Negm-Awad, replaced LogLocal with function in .m file as:
void LogLocal(int level, NSString *format, ...){
va_list arg_list;
va_start(arg_list, format);
va_end(arg_list);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
asl_add_log_file(NULL, STDERR_FILENO);
asl_log(NULL, NULL, (level), "XXXX: %s", [formattedString UTF8String]);
}
works as expected.

You use macros that are expanded. But they do not create a scope or a call on expansion. It's simple text replacement.
// Somewhere in a function
void f(void)
{
…
LogDebug(#"Name is called with flag: %#", collectName ? #"YES" : #"NO");
…
}
This will expand at the end to something like this:
// Somewhere in a function
void f(void)
{
…
// LogDebug(#"Name is called with flag: %#", collectName ? #"YES" : #"NO");
va_list arg_list;
va_start(arg_list, #"Name is called with flag: %#");
// ...
va_end(arg_list);
…
}
Since f() takes no vargs, there are no vargs.
It might help to make LogLocal() a function, not a macro:
void LogLocal( int level, NSString *format, ... );
Implementation:
void LogLocal( int level, NSString *format, ... )
{
va_list arg_list;
va_start(arg_list, format);
// ...
va_end(arg_list);
}
To your Edit 2:
Yes, this works, because you do not touch the arg list, but pass the args to a function taking vargs. But you do not need that trick. Instead make LogLocal() a function and it will be called similar to NSLog() without any error.

Related

'VA_ARGS', an invalid preprocessing token in macros Obj-C

I use following technique to manage my logs. I print logs to asl_log and before, regards to flag [DebugManager shared] isDebugging I want to send log line to other class (method addLogEvent)
#if !defined(TheLog)
#define TheLog(fmt, ...) { \
if ([[DebugManager shared] isDebugging]) \
addLogEvent(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__); \
}
#endif
#define __AF_MAKE_LOG_FUNCTION(LEVEL, NAME) \
inline void NAME(NSString *format, ...)\
{ \
TheLog(__PRETTY_FUNCTION__,format,##__VA_ARGS__);\
va_list arg_list; \
va_start(arg_list, format); \
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; \
asl_add_log_file(NULL, STDERR_FILENO); \
asl_log(NULL, NULL, (LEVEL), "PREFIX: %s", [formattedString UTF8String]); \
va_end(arg_list); \
}
// Something has failed.
__AF_MAKE_LOG_FUNCTION(ASL_LEVEL_ERR, AFLogError)
// Something is amiss and might fail if not corrected.
__AF_MAKE_LOG_FUNCTION(ASL_LEVEL_WARNING, AFLogWarning)
// The lowest priority for user log
__AF_MAKE_LOG_FUNCTION(ASL_LEVEL_INFO, AFLogDebug)
I map log level with __AF_MAKE_LOG_FUNCTION(LEVEL, NAME) and I need to call TheLog(__PRETTY_FUNCTION__,format,##__VA_ARGS__);\ from inline void NAME(NSString *format, ...)
I get an error:
Pasting formed ',__VA_ARGS__', an invalid preprocessing token
How can I fetch ,__VA_ARGS__ and __PRETTY_FUNCTION__?
This line:
TheLog(__PRETTY_FUNCTION__,format,##__VA_ARGS__);\
is part of the definition of this macro:
#define __AF_MAKE_LOG_FUNCTION(LEVEL, NAME) \
Note that that macro does not take a variable argument list. Therefore, there's no __VA_ARGS__ defined within its definition.
The fact that the function being defined by an instantiation of __AF_MAKE_LOG_FUNCTION — the inline void NAME() — takes a variable argument list isn't relevant. If that function wants to pass the variable argument list along to another function, it needs to do it using the stdarg functionality, as it does for -[NSString initWithFormat:arguments:], but that doesn't work for your TheLog macro, because it's not designed to accept a va_list.
You can't do what you're attempting. Your TheLog macro is incompatible with how you're trying to use it. You would need to design an alternative version, such as:
#define TheLogv(fmt, args) { \
if ([[DebugManager shared] isDebugging]) \
addLogEventv(__PRETTY_FUNCTION__,fmt,args); \
}
Note that this would, in turn, require the existence of a function addLogEventv() which accepts a va_list instead of an actual variable argument list. Within the body of the function being defined by __AF_MAKE_LOG_FUNCTION, you'd have to start and end the list twice, once around each time you pass it to another function, because each function will "consume" it:
#define __AF_MAKE_LOG_FUNCTION(LEVEL, NAME) \
inline void NAME(NSString *format, ...)\
{ \
va_list arg_list; \
va_start(arg_list, format); \
TheLogv(__PRETTY_FUNCTION__,format,arg_list);\
va_end(arg_list); \
va_start(arg_list, format); \
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; \
asl_add_log_file(NULL, STDERR_FILENO); \
asl_log(NULL, NULL, (LEVEL), "PREFIX: %s", [formattedString UTF8String]); \
va_end(arg_list); \
}
You could also change your TheLog() macro to take an NSString* and simply pass in the formattedString that's already being created.

__VA_ARGS__ Macro expansion

I'm trying to get my macro to work like NSLog() which accepts variable arguments. Code below causes parse issues.
What is the correct way to define this?
#define TF_CHECKPOINT(f, ...) \
do { \
NSString *s = [[NSString alloc] initWithFormat:f arguments:__VA_ARGS__] autorelease]; \
[TestFlight passCheckpoint:[NSString stringWithFormat:#"%#: %#", [self class], s]]; \
} while (0)
You forgot the opening bracket for the autorelease message.
Moreover -[NSString initWithFormat:arguments:] expects a va_list argument, whereas __VA_ARGS__ is replaced by all the passed arguments. Here, you need to use -[NSString initWithFormat:] or +[NSString stringWithFormat:].
Finally, you may prefix __VA_ARGS__ with ##. By doing so, the preceding comma is deleted when there is no argument.
Try this:
#define TF_CHECKPOINT(f, ...) \
do { \
NSString *s = [NSString stringWithFormat:(f), ##__VA_ARGS__]; \
[TestFlight passCheckpoint:[NSString stringWithFormat:#"%#: %#", [self class], s]]; \
} while (0)

c function declaration in xcode / to replace NSLog with something that behaves differently in debug and release

It is a rather silly question! but in the following function how do you use the remaining arguments passed in:
void NSLog(NSString *format, ...)
{
//here I can use "format" but how can I use the remaining arguments?
}
It is hard to find the answer to this question because I cant search for "..." ?!
By the way that is how NSLog works but I put it here just as an example my question has nothing to do with NSLog.
Use a variable argument list:
void NSLog(NSString *format, ...)
{
va_list ap;
va_start(ap, format);
while( (value = va_arg(args, NSString *) ){
// Do something with value. This is assuming they are all strings
}
// or pass entire list to another function
NSLog_VA( format, ap );
va_end(ap);
}
void NSLog_VA( NSString * format, va_list args )
{
// do something with va_list here
}
Edit:
Since you want a debug only log:
#ifdef DEBUG
#define DebugOnly_Log(format, args...) NSLog(format, ##args)
#else
#define DebugOnly_Log(format, args...) // defined to nothing
#endif
Have a look at stdarg.h
It might look a bit like -
void NSLog(NSString *format, ...)
{
va_list args;
va_start(args,format);
vprintf([format cString], args);
va_end(args);
}
That's called a variadic function, and you can access its arguments by using the macros in stdarg.h.
OK, thanks all, I followed the direction you gave me and this is the solution:
As I understand it, there is no general/portable solution to the problem of receiving variable number of arguments and passing them to another function that accepts variable number of arguments (as mentioned her http://c-faq.com/varargs/handoff.html ).
But I wanted to implement an NSLog alternative (I call it AliLog ) which during debugging just behaves like NSLog but for the release version does nothing (or does something other than writing to consol).
here is my solution:
void AliLog(NSString *format, ...)
{
#ifdef DEBUG
va_list args;
va_start(args, format);
NSLogv(format, args);
va_end(args);
#else
// do nothing! or something that makes sense in a release version
#endif
}
The magic is in NSLogv here.

How to use va_args to pass arguments on (variadic parameters, ellipsis)

I can't get my head around the syntax for multiple arguments in Objective-C. I have seen this question, but the answer hasn't helped me (yet).
Here is my code (actually I will want to eventually pass to NSString stringWithFormat, but getting an NSLog to work would be good enough for now):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self log:#"blah blah %d", 32];
}
- (void)log:(NSString *)text, ... {
va_list args;
va_start(args, text);
NSLog(text, args);
}
The argument (or some argument) comes through, but it's got some weird value (output is blah blah 1606412704). How should I pass the values that come in via ...?
There's a variant of NSLog that accepts a va_list called NSLogv:
- (void) log:(NSString *)text, ... {
va_list args;
va_start(args, text);
NSLogv(text, args);
va_end(args);
}
The only way to forward the actual ... (not the va_list) is to use a macro. For example:
#define MyLog(f, ...) { \
NSLog(f, ##__VA_ARGS__); \
[someObject doSomething:f, ##__VA_ARGS__]; \
}
However, this should be used very sparingly, since macros can make code really obfuscated.
You could use -[NSString initWithFormat:arguments:]:
- (void)log:(NSString *)text, ...
{
va_list args;
va_start(args, text);
NSString *log_msg = [[[NSString alloc] initWithFormat:text arguments:args] autorelease];
NSLog(#"%#", log_msg);
}

What are some unique uses of NSLog you have used?

Do you have any unique or special uses of NSLog you use for debugging?
I like to use this format for debugging.
NSLog( #"<%p %#:(%d)> %#", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
Of course, you'll want to wrap this in your own method or function for ease of use. I use the preprocessor and also only enable it for my own use and special builds I send out to beta testers. This is copied from my answer to this question, by the way.
#define DEBUG_MODE
#ifdef DEBUG_MODE
#define DebugLog( s, ... ) NSLog( #"<%p %#:(%d)> %#", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define DebugLog( s, ... )
#endif
This makes DebugLog act just like NSLog, but displays the filename and line-number where it was called:
2009-05-23 17:23:40.920 myproject[92523:10b] <AppCon.m:(8)> My debug message...
I use some macros with NSLog to quickly debug the contents of cocoa's NSPoint, NSSize, and NSRect structs:
#define LogPoint(POINT) CMLog(#"%s: (%0.0f, %0.0f)",\
#POINT, POINT.x, POINT.y)
#define LogSize(SIZE) CMLog(#"%s: %0.0f x %0.0f",\
#SIZE, SIZE.width, SIZE.height)
#define LogRect(RECT) CMLog(#"%s: (%0.0f, %0.0f) %0.0f x %0.0f",\
#RECT, RECT.origin.x, RECT.origin.y,\
RECT.size.width, RECT.size.height)
These can be used as follows:
LogPoint(somePoint);
LogSize(someSize);
LogRect(someRect);
And they produce the following output:
somePoint: (100, 200)
someSize: 12 x 440
someRect: (120, 240) 326 x 74
NSLog(#"%s", __func__);
prints the current method signature or function name.
This is not an NSLog feature, however it’s very handy for use with NSLog: you can use %# placeholder for objects, and their descriptions will be displayed:
NSLog (#"The object is %#", someKindOfObjectWhichYouWantToDisplay);
This way you can quikly see what object you get returned, for example. This works by sending “description” selector an object, which can be, of course, implemented in your own objects.
Here's one that lets you indent some portions of your debugging logs to make things more readable:
// MyDebugStuff.h:
void MyLog_Indent();
void MyLog_Outdent();
void MyLog(NSString * format, ...);
// MyDebugStuff.m:
int logIndentLevel = 0;
void MyLog_Indent() { logIndentLevel++; }
void MyLog_Outdent() { if (logIndentLevel > 0) { logIndentLevel--; } }
void MyLog(NSString * format, ...)
{
va_list args;
va_start(args, format);
NSString * indentString = [[NSString stringWithString:#""]
stringByPaddingToLength:(2*LogIndentLevel)
withString:#" "
startingAtIndex:0];
NSLogv([NSString stringWithFormat:#"%#%#", indentString, format], args);
va_end(args);
}
This can be used like so:
MyLog(#"Hello, world");
MyLog_Indent();
MyLog(#"Step 1");
MyLog(#"Step 2");
MyLog_Indent();
MyLog(#"Step 2a");
MyLog(#"Step 2b");
MyLog_Outdent();
MyLog(#"Step 3");
MyLog_Outdent();
MyLog(#"Goodbye, cruel world!");
And will produce:
Hello, world
Step 1
Step 2
Step 2a
Step 2b
Step 3
Goodbye, cruel world!