tags:

views:

220

answers:

6

I have zillions of my_printf() function calls in a huge program. I now want to convert them all so that the function takes a new integer argument (call it x) without having to edit the zillions of calls. If my_printf only ever took exactly one string argument then I could do something like this:

    #define my_printf(str) _my_printf(x,str)

    void _my_printf(int x,char *str) // changed from my_printf(char *str)
    {
        // stuff
    }

But as my_printf takes a variable number of arguments I'm not sure how to do it. Can it be done?

EDIT: for those wondering why I should want to do such a thing, here's a related example:

#if BELT_AND_BRACES_DIAGNOSTIC_MODE
    #define function(x) _function(__FILE__,__LINE__,x)
#else // speed critical optimised mode
    #define function(x) _function(x)
#endif

#if BELT_AND_BRACES_DIAGNOSTIC_MODE
    void _function(char *file,int line,int x)
#else
    void _function(int x)
#endif
{
    // stuff
    #if BELT_AND_BRACES_DIAGNOSTIC_MODE
    if (something_went_wrong)
    {
        printf("Cock up in function when called from %s line %d\n",file,line);
    }
    #endif
}
+8  A: 

The best thing is of course to bite the bullet and edit the code. Otherwise you're creating a "mystery", that needs to be solved by all future maintainers of the code. Even if that's only you, this is exactly the kind of clever trick that you will forget all about. It sucks to come back, and be puzzled by strange pointless-seeming macros.

That said, if you're using a GNU toolchain, you can perhaps look into using varargs macros.

unwind
+1 to biting the bullet. With today's editors with refactoring support, etc., you should be laughing. Heck, my programming editor 15 years ago could quite easily do a global search and replace on files specified by wildcard across an entire directory tree, with and without my previewing the results first. (And if you're thinking macro, then you can just do it blind.)
T.J. Crowder
See my edit to the original question
Mick
+1. Look on it as a learning opportunity to really get to grips with what a modern editor can do (or if you've been around long enough, what Vim or Emacs can do -- and they can do things that Visual Studio is only just catching up with...).
AAT
+5  A: 

If the code can be compiled as C99 code, you can define a variadic macro

#define my_printf(str, args...) _my_printf(x, str, ##__VA_ARGS__)

The preprocessor will replace the arguments ... and the GNU preprocessor will remove the trailing comma in case the macro is invoked only with the str argument.

philippe
that's GNU, not C99 - the C99 version would read `define my_printf(str, ...) _my_printf(x, __VA_ARGS__)`; keep in mind that you need to provide at leat one argument
Christoph
also, you and me both forgot to pass on `str`; I think the best approach would actually be to drop the explicit `str` entirely and just use `define my_printf(...) _my_printf(x, __VA_ARGS__)` so you can call it without the function's varargs even in the C99 version
Christoph
The problem is now solved. I'm using dev-studio 2008 and the __VA_ARGS__ thing worked a treat - but I'm not sure which answer to mark as correct because none of them are really completely spot on. unwind's answer implies I need to be using GNU and phillipe's answer has errors too.
Mick
@Mick: I added an answer with some details about the problem with empty argument lists and trailing commas: http://stackoverflow.com/questions/1491749/define-and-functions-with-variable-length-arguments/1492234#1492234
Christoph
Strangely enough gcc compiled the ##args form event with std=c99 ; I thought this removed the GNU extensions (onlt tetsed with gcc v3.4.3). Thanks, I fixed it.
philippe
@philippe: use `-std=c99 -pedantic` to get the warnings
Christoph
+1 thanks a lot for following up!
philippe
A: 

If my_printf already takes a variable number of arguments, I'm not sure why you need to wrap 'one more argument' in a macro... Just insert the new calls with the extra argument and be done with it; the old calls should still work as expected.

ezpz
+2  A: 

Not with standard C89 macros, you can't. However you can get the same effect using functions, by breaking out the main part of your my_printf function into a vmy_printf function, analagous to the standard vprintf:

#include <stdarg.h>

int vmy_printf(int x, const char *format, va_list ap)
{
    /* main body of my_printf goes here, taking its varargs from ap */
}

/* new_my_printf(), for callers who know about the x parameter */
int new_my_printf(int x, const char *format, ...)
{
    int n;
    va_list ap;

    va_start(ap, format);
    n = vmy_printf(x, format, ap);
    va_end(ap);

    return n;
}

/* my_printf(), for the old callers who don't know about x */
int my_printf(const char *format, ...)
{
    int n;
    va_list ap;

    va_start(ap, format);
    n = vmy_printf(DEFAULT_X, format, ap);
    va_end(ap);

    return n;
}

(This kind of thing is why those v... versions of all the standard varargs functions exist.)

caf
+1  A: 

You may use C99 variadic macros:

#define my_printf(...) _my_printf(x, __VA_ARGS__)

As Microsoft's implementation suppresse trailing commas, the str argument can be added explicitly

#define my_printf(str, ...) _my_printf(x, str, __VA_ARGS__)

but this would lead to a syntax error in standard C when invoked without variadic arguments

my_printf("foo")

or an empty argument list

my_printf("foo",)

Therefore, I'd go with the first version.

Christoph
A: 

A simple solution to this problem is...

#define my_printf(x) printf x

(note the missing braces)

To call it, use:

my_printf((any number of arguments))

(note the double braces)

dmityugov