2

My environment: Xcode5, iOS, Objective-C/Objective-C++ mix.

I am trying to figure out what causes the next problem. I am writing my own logging function:

int _me_log(const char *fmt, ...) {

va_list args;
va_start(args, fmt);

char *c = va_arg(args, char *);

char *message = NULL;

printf(fmt, args);

int n = asprintf(&message, fmt, args);

if (n != -1 && message != NULL) {
 //do something with 'message' like writing to file, etc.
 UPDATE:
//we need to handle memory created for 'message' storage.
free(message); 
}
va_end(args);

return n;

}

Then I call it like this:

_me_log("socket %s did open", "Socket: 0x1fd1c880");

And instead of correct output socket Socket: 0x1fd1c880 did open I get some gibberish like this socket \\323\331/ did open in this line printf(fmt, args);.

If I call it this way printf("%s", c); I get correct results.

I have googled several implementations (this or this ) of logging functions and functions which pass variable parameters and it seems that I do everything correctly.

Could you please suggest me what I'm doing wrong?

Community
  • 1
  • 1
user1264176
  • 1,118
  • 1
  • 9
  • 26

2 Answers2

2

You've got the right idea to use va_list here, but if you work with va_list you should use vasprintf instead of asprintf:

int _me_log(const char *fmt, ...)
{
    va_list args;
    char *message = NULL;
    int n;

    va_start(args, fmt);
    n = vasprintf(&message, fmt, args);

    if (n != -1 && message != NULL) {
        // ... use message ...
    }
    free(message);
    va_end(args);

    return n;
}

For every routine of the printf family, there is a variant that takes a va_list instead of the variadic argument ... and whose name is prefixed with the letter v, for example:

int printf(const char *format, ...);
int vprintf(const char *format, va_list ap);

These routines exist so you can write you own (non-macro) wrapper for xprintf.

M Oehm
  • 28,726
  • 3
  • 31
  • 42
  • Thanks, I have already added a comment to previous answer. I am accepting you answer because it provides direct answer to my question and code in it. trojanfoe's answer is also correct. – user1264176 Nov 12 '13 at 14:07
  • @trojanfoe if you mean leaking 'message' then of course I handle that. I omitted it for code brevity. – user1264176 Nov 12 '13 at 14:12
  • Updated question with freeing of 'message' memory. Just in case someone copy/pastes it. – user1264176 Nov 12 '13 at 14:17
  • OK that makes more sense, however the call to `free()` which is now in this answer is in the wrong place. – trojanfoe Nov 12 '13 at 14:20
  • @trojanfoe: Of course the message buffer must be freed. User1264176 beat me to editing the answer. Thanks for correcting that. – M Oehm Nov 12 '13 at 14:22
  • @trojanfoe: It's only in the wrong place if `vasprintf` guarantees that the buffer is `NULL` if and only if -1 is returned. It is legal (if unnecessary) or `free(NULL)`. – M Oehm Nov 12 '13 at 14:30
  • And what will `free(NULL)` do? – trojanfoe Nov 12 '13 at 14:32
  • @trojanfoe: It won't do anything. It's probably so you don't have to check for `NULL` in client code. But I agree with you in principle: Had the condition just been `if (message) ...` it would have been cleaner to free the buffer at the end of the `if` block. – M Oehm Nov 12 '13 at 14:40
  • Ah OK - on some systems that will blow up - not under OSX/iOS I guess. – trojanfoe Nov 12 '13 at 14:48
1

Seems like a very complicated implementation. Try:

int _me_log(const char *fmt, ...) {
    int ret = 0;
    va_list va;
    va_start(va, fmt);
    ret = vprintf(fmt, va);
    va_end(va);

    putc('\n', stdout);
    return ret;
}

But, of course, that is no different from printf(), except for forcing a newline.

trojanfoe
  • 120,358
  • 21
  • 212
  • 242
  • Now I see my mistake. You can't call printf with va_list as a parameter like this "printf(fmt, va);". You have to call vprintf() instead. Thanks. – user1264176 Nov 12 '13 at 14:05