1

I am trying to write a wrapper around vfprintf function but with the requirement that I want to add a prefix to the format specifier and then pass the new format specifier to vfprintf.

Now I don't know how to do this, but I have captured my intention in the following code.

#include <stdio.h>
#include <stdarg.h>

void err(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, "foo: error:" format, args);
    va_end(args);
}

int main()
{
    err("%s: %d\n", "Transaction failed with error code", 42);
    return 0;
}

You can see in the above code, that I want to prefix "foo: error" to the format specifier and then pass it to vprintf. This code, of course, would lead to compile time error because this code is not valid. It only captures my intention of what I am trying to achieve.

lone@debian:~/lab/c$ gcc -std=c89 -Wall -Wextra -pedantic vfprintf-wrapper.c 
vfprintf-wrapper.c: In function ‘err’:
vfprintf-wrapper.c:8:36: error: expected ‘)’ before ‘format’
     vfprintf(stderr, "foo: error:" format, args);
                                    ^
vfprintf-wrapper.c:8:5: error: too few arguments to function ‘vfprintf’
     vfprintf(stderr, "foo: error:" format, args);
     ^

Can you help me to write this code correctly?

Lone Learner
  • 18,088
  • 20
  • 102
  • 200
  • 2
    Looks like a needlessly complicated thing to do. Why not just print the error prefix in a separate statement inside the wrapper, and then call vprintf as is? – n. m. could be an AI Aug 10 '15 at 03:28
  • @n.m. That is a perfectly valid way to do things, however, I want to learn if what I am asking is possible and how to do it. – Lone Learner Aug 10 '15 at 03:35
  • 1
    Of course, it is possible. Just create a new format string ( malloc and a couple of strcpys would do it). But it is likely not the most efficient solution. – rici Aug 10 '15 at 03:39
  • 2
    What you can't do is add more arguments to the varargs list. – rici Aug 10 '15 at 03:40
  • `prog` is a mystery variable not accounted for in `err("%s: %d\n", "Transaction failed with error code", 42)` What is ir? What is code trying to do with it? – chux - Reinstate Monica Aug 10 '15 at 04:00
  • Sorry, the `prog` was present there by mistake. I have removed it now. The `err` is an example call to demonstrate how I want to call the wrapper function. – Lone Learner Aug 10 '15 at 04:06
  • one way is to declare a buffer that is the length of the current format string+the length of the part you want to prepend. clear that buffer to all '\0'. Then strcat the prepend value then strcat the original format string. Use a pointer to the new format string in the call to vfprintf() – user3629249 Aug 11 '15 at 04:50

1 Answers1

4

Your pseudocode vfprintf(stderr, "foo: error:" format, args); should be:

fprintf(stderr, "foo: error:");
vfprintf(stderr, format, args);

You seem to be indicating that you want to avoid the "extra" fprintf call. If so then you could do this:

char *fail = malloc(sizeof "foo: error:" + strlen(format));
if ( !fail )
    exit(EXIT_FAILURE);
strcpy(fail, "foo: error:");
strcat(fail, format);

vfprintf(stderr, fail, args);

free(fail);

although that would be a waste of time and resource.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • It seems a pity to write `if (!fail) exit(EXIT_FAILURE);` — if you didn't fail, exit with failure. Maybe a better choice of variable name would be `msg_format`, or `msgfmt` or something like that. – Jonathan Leffler Aug 10 '15 at 04:13
  • Oh, you inherited the `prog` name from the older version of the question. I think it is just an interloper, or should be interpolated into the format as part of the string: `char *msgfmt = malloc(sizeof(": error: ") + strlen(prog) + strlen(format); sprintf(msgfmt, "%s: error: %s", prog, format); vfprintf(stderr, msgfmt, args);` (ignoring the necessary error check after `malloc()`). – Jonathan Leffler Aug 10 '15 at 04:21
  • @JonathanLeffler oops,I read that off his error messages and didn't match it up to the actual code (which it doesn't match as you say). Yeah, `fail` is a dumb name for a pointer variable, that was intentional..:) – M.M Aug 10 '15 at 04:46
  • The second solution is more robust against threads, but I'd love to avoid using `malloc`. Any good ideas? – syockit Aug 30 '16 at 08:11
  • @syockit yeah, use the first one. Nothing wrong with two calls to fprintf. If you are multithreaded and want to avoid interleaving from different threads writing to `stderr` simultaneously then you should use a mutex to surround writes to `stderr`. – M.M Aug 30 '16 at 08:15
  • @syockit: if the caller is passing a string literal as the "format" arg (which is typical), then you can use a macro to expand `LOG("format", args...)` to `printf("prefix" "format", args...)`. The case where there are no args gets a little harder to do cross-platform, though. See [this post](https://stackoverflow.com/questions/5588855/standard-alternative-to-gccs-va-args-trick) – jwd Jan 22 '20 at 06:29