printf with run-time format strings

Discussion in 'C Programming' started by Richard Bos, Feb 10, 2004.

  1. Richard Bos

    Richard Bos Guest

    Grumble <> wrote:

    > I have the following structure:
    >
    > struct foo {
    > char *format; /* format string to be used with printf() */
    > int nparm; /* number of %d specifiers in the format string */
    > /* 0 <= nparm <= 4 */
    > };
    >
    > e.g.
    >
    > struct foo bar = { "foo %d %d bar %d\n", 3 };
    >
    > I can write:
    >
    > printf(bar.format, rand(), rand(), rand());


    Yes. Note that you don't know in which order the rand()s are called.
    With rand(), this is obviously not of great importance, but if you
    substitute a function which pops a value from a stack instead, you might
    be in for a surprise...

    > Assume I have a properly initialized array of struct foo:
    >
    > struct foo array[100]; ... /* initialize array */
    >
    > and I want to print every struct foo. Do I have to make a special case
    > for every possible value of nparm? As in:
    >
    > for (i=0; i < 100; ++i)
    > {
    > switch(array.parm)
    > {
    > case 0:
    > printf(array.format); break;
    > case 1:
    > printf(array.format, rand()); break;
    > ...
    > case 4:
    > printf(array.format, rand(), rand(), rand(), rand()); break;
    > }
    > }


    You do if you really need to use printf(). If you know in advance that
    the maximum number of parameters is limited, this isn't such a bad
    approach.

    > Isn't there a better way? If I think in terms of a stack (which I know
    > is a sin in c.l.c.)


    You know nothing of the sort. Why would using stacks be a sin? What you
    can't do in ISO C is use any kind of system, hardware or program stack,
    but there's nothing to prevent you from creating your own stack ADT.

    > I should be able to push however many parameters I
    > have onto the stack, push either the number of arguments or NULL (I'm
    > not sure how printf works) and then jump to the the printf code.


    Ah, well, _that_ is impossible. printf() doesn't know how to use your
    stack, and you can't interfere with its stack. I wouldn't even try if
    you could, either; much too risky.

    > I've never used variadic functions.


    (Yes, you have; printf() is one.)

    > Could they prove useful in this case?


    Not that I can see. <stdarg.h> is for writing functions that get
    variable numbers of arguments passed to them, not for passing variable
    numbers of arguments to other functions. It would have been useful had
    that been possible, but alas, it isn't.

    Richard
    Richard Bos, Feb 10, 2004
    #1
    1. Advertising

  2. Richard Bos

    Grumble Guest

    Hello,

    I have the following structure:

    struct foo {
    char *format; /* format string to be used with printf() */
    int nparm; /* number of %d specifiers in the format string */
    /* 0 <= nparm <= 4 */
    };

    e.g.

    struct foo bar = { "foo %d %d bar %d\n", 3 };

    I can write:

    printf(bar.format, rand(), rand(), rand());

    Assume I have a properly initialized array of struct foo:

    struct foo array[100]; ... /* initialize array */

    and I want to print every struct foo. Do I have to make a special case
    for every possible value of nparm? As in:

    for (i=0; i < 100; ++i)
    {
    switch(array.parm)
    {
    case 0:
    printf(array.format); break;
    case 1:
    printf(array.format, rand()); break;
    ...
    case 4:
    printf(array.format, rand(), rand(), rand(), rand()); break;
    }
    }

    Isn't there a better way? If I think in terms of a stack (which I know
    is a sin in c.l.c.) I should be able to push however many parameters I
    have onto the stack, push either the number of arguments or NULL (I'm
    not sure how printf works) and then jump to the the printf code.

    I've never used variadic functions. Could they prove useful in this case?

    Regards,

    Grumble
    Grumble, Feb 10, 2004
    #2
    1. Advertising

  3. Richard Bos

    Richard Bos Guest

    Al Bowers <> wrote:

    > Grumble wrote:
    > > for (i=0; i < 100; ++i)
    > > {
    > > switch(array.parm)
    > > {
    > > case 0:
    > > printf(array.format); break;
    > > case 1:
    > > printf(array.format, rand()); break;
    > > ...
    > > case 4:
    > > printf(array.format, rand(), rand(), rand(), rand()); break;
    > > }
    > > }

    >
    > I am not sure if it is a better way but you can make a macro of
    > the last printf arguments. If there are more arguments than there
    > are specifiers, the remaining arguments are evaluated but otherwise
    > ignored.
    >
    > #define ARGS rand(),rand(),rand(),rand() /* max of 4 args */


    Of course! Should've thought of that... You don't even need to make a
    macro of it; putting them all into the function call works just as well.
    OTOH, it only works as desired if the arguments do not have side effects
    or if (as with rand()) its side effects are not likely to be important
    anyway.

    > Then you printf would look this this:
    > printf(array.format,ARGS);


    Or simply

    printf(array.format, rand(), rand(), rand(), rand());

    with a comment explaining that printf() will ignore the superfluous
    parameters, if necessary.

    Richard
    Richard Bos, Feb 10, 2004
    #3
  4. Richard Bos

    lallous Guest

    "Grumble" <> wrote in message
    news:c0ap12$h1e$...
    > Hello,
    >
    > I have the following structure:
    >
    > struct foo {
    > char *format; /* format string to be used with printf() */
    > int nparm; /* number of %d specifiers in the format string */
    > /* 0 <= nparm <= 4 */
    > };
    >
    > e.g.
    >
    > struct foo bar = { "foo %d %d bar %d\n", 3 };
    >
    > I can write:
    >
    > printf(bar.format, rand(), rand(), rand());
    >
    > Assume I have a properly initialized array of struct foo:
    >
    > struct foo array[100]; ... /* initialize array */
    >
    > and I want to print every struct foo. Do I have to make a special case
    > for every possible value of nparm? As in:
    >
    > for (i=0; i < 100; ++i)
    > {
    > switch(array.parm)
    > {
    > case 0:
    > printf(array.format); break;
    > case 1:
    > printf(array.format, rand()); break;
    > ...
    > case 4:
    > printf(array.format, rand(), rand(), rand(), rand()); break;
    > }
    > }
    >
    > Isn't there a better way? If I think in terms of a stack (which I know
    > is a sin in c.l.c.) I should be able to push however many parameters I
    > have onto the stack, push either the number of arguments or NULL (I'm
    > not sure how printf works) and then jump to the the printf code.
    >
    > I've never used variadic functions. Could they prove useful in this case?
    >
    > Regards,
    >
    > Grumble
    >

    Well as you said, you can write your own function that behaves like printf()
    and takes its parameter list in the form of a linked list or any other mean
    that can be fed during runtime.

    If you still want to use printf(), and on Intel x86, you can push your own
    parameters to the stack in a for loop.
    By observing the assembly output of a normal printf() call you can
    understand how to pass extra parameters in runtime as if they were added by
    the compiler.

    --
    Elias
    lallous, Feb 10, 2004
    #4
  5. Richard Bos

    Al Bowers Guest

    Grumble wrote:
    > Hello,
    >
    > I have the following structure:
    >
    > struct foo {
    > char *format; /* format string to be used with printf() */
    > int nparm; /* number of %d specifiers in the format string */
    > /* 0 <= nparm <= 4 */
    > };
    >
    > e.g.
    >
    > struct foo bar = { "foo %d %d bar %d\n", 3 };
    >
    > I can write:
    >
    > printf(bar.format, rand(), rand(), rand());
    >
    > Assume I have a properly initialized array of struct foo:
    >
    > struct foo array[100]; ... /* initialize array */
    >
    > and I want to print every struct foo. Do I have to make a special case
    > for every possible value of nparm? As in:
    >
    > for (i=0; i < 100; ++i)
    > {
    > switch(array.parm)
    > {
    > case 0:
    > printf(array.format); break;
    > case 1:
    > printf(array.format, rand()); break;
    > ...
    > case 4:
    > printf(array.format, rand(), rand(), rand(), rand()); break;
    > }
    > }
    >
    > Isn't there a better way? If I think in terms of a stack (which I know
    > is a sin in c.l.c.) I should be able to push however many parameters I
    > have onto the stack, push either the number of arguments or NULL (I'm
    > not sure how printf works) and then jump to the the printf code.
    >


    I am not sure if it is a better way but you can make a macro of
    the last printf arguments. If there are more arguments than there
    are specifiers, the remaining arguments are evaluated but otherwise
    ignored.

    #define ARGS rand(),rand(),rand(),rand() /* max of 4 args */

    Then you printf would look this this:
    printf(array.format,ARGS);

    Example:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>

    #define ARGS rand(),rand(),rand(),rand()

    struct foo {
    char *format; /* format string to be used with printf() */
    unsigned nparm; /* number of %d specifiers in the format string */
    /* 0 <= nparm <= 4 */
    };

    struct foo addFoo(const char *format);

    int main(void)
    {
    struct foo myfoo = addFoo("this is %d and this is %d");
    if(myfoo.format) printf(myfoo.format,ARGS);
    free(myfoo.format);
    return 0;
    }

    struct foo addFoo(const char *format)
    { /* TODO Add code to make sure "%d" */
    struct foo tmp = {NULL};
    const char *cs;
    unsigned cnt;
    size_t len = strlen(format);

    for(cnt = 0,cs = format; (cs = strchr(cs,'%')); cs+=1,cnt++) ;
    if(cnt <= 4 && len) tmp.format = malloc(len+2);
    if(tmp.format)
    {
    strcpy(tmp.format,format);
    tmp.nparm = cnt;
    if('\n' != tmp.format[strlen(tmp.format)-1])
    strcat(tmp.format,"\n");
    }
    return tmp;
    }




    --
    Al Bowers
    Tampa, Fl USA
    mailto: (remove the x to send email)
    http://www.geocities.com/abowers822/
    Al Bowers, Feb 10, 2004
    #5
  6. Richard Bos

    Grumble Guest

    Richard Bos wrote:

    > Al Bowers <> wrote:
    >
    >>Grumble wrote:
    >>
    >>>for (i=0; i < 100; ++i)
    >>>{
    >>> switch(array.parm)
    >>> {
    >>> case 0:
    >>> printf(array.format); break;
    >>> case 1:
    >>> printf(array.format, rand()); break;
    >>> ...
    >>> case 4:
    >>> printf(array.format, rand(), rand(), rand(), rand()); break;
    >>> }
    >>>}

    >>
    >>I am not sure if it is a better way but you can make a macro of
    >>the last printf arguments. If there are more arguments than there
    >>are specifiers, the remaining arguments are evaluated but otherwise
    >>ignored.
    >>
    >>#define ARGS rand(),rand(),rand(),rand() /* max of 4 args */

    >
    >
    > Of course! Should've thought of that... You don't even need to make a
    > macro of it; putting them all into the function call works just as well.
    > OTOH, it only works as desired if the arguments do not have side effects
    > or if (as with rand()) its side effects are not likely to be important
    > anyway.
    >
    >
    >>Then you printf would look this this:
    >>printf(array.format,ARGS);

    >
    >
    > Or simply
    >
    > printf(array.format, rand(), rand(), rand(), rand());
    >
    > with a comment explaining that printf() will ignore the superfluous
    > parameters, if necessary.


    What if I wanted to print values from an array instead of calling rand()?

    printf(array.format, v[0], v[1], v[2], v[3]);

    If v was defined as int v[3]; then I suppose the above would yield UB?

    What if I had 4 variables: int v1, v2, v3, v4;

    printf(array.format, v1, v2, v3, v4);

    If v4 was uninitialized but also not used by printf, would the above
    yield UB?

    Regards,

    Grumble
    Grumble, Feb 10, 2004
    #6
  7. Richard Bos

    Jack Klein Guest

    On Tue, 10 Feb 2004 17:54:05 +0100, Grumble <> wrote
    in comp.lang.c:

    > Richard Bos wrote:
    >
    > > Al Bowers <> wrote:
    > >
    > >>Grumble wrote:
    > >>
    > >>>for (i=0; i < 100; ++i)
    > >>>{
    > >>> switch(array.parm)
    > >>> {
    > >>> case 0:
    > >>> printf(array.format); break;
    > >>> case 1:
    > >>> printf(array.format, rand()); break;
    > >>> ...
    > >>> case 4:
    > >>> printf(array.format, rand(), rand(), rand(), rand()); break;
    > >>> }
    > >>>}
    > >>
    > >>I am not sure if it is a better way but you can make a macro of
    > >>the last printf arguments. If there are more arguments than there
    > >>are specifiers, the remaining arguments are evaluated but otherwise
    > >>ignored.
    > >>
    > >>#define ARGS rand(),rand(),rand(),rand() /* max of 4 args */

    > >
    > >
    > > Of course! Should've thought of that... You don't even need to make a
    > > macro of it; putting them all into the function call works just as well.
    > > OTOH, it only works as desired if the arguments do not have side effects
    > > or if (as with rand()) its side effects are not likely to be important
    > > anyway.
    > >
    > >
    > >>Then you printf would look this this:
    > >>printf(array.format,ARGS);

    > >
    > >
    > > Or simply
    > >
    > > printf(array.format, rand(), rand(), rand(), rand());
    > >
    > > with a comment explaining that printf() will ignore the superfluous
    > > parameters, if necessary.

    >
    > What if I wanted to print values from an array instead of calling rand()?
    >
    > printf(array.format, v[0], v[1], v[2], v[3]);
    >
    > If v was defined as int v[3]; then I suppose the above would yield UB?


    You are correct, sir.

    > What if I had 4 variables: int v1, v2, v3, v4;
    >
    > printf(array.format, v1, v2, v3, v4);
    >
    > If v4 was uninitialized but also not used by printf, would the above
    > yield UB?


    Yes it would, in theory. The act of passing v4 by value requires the
    compiler to generate code to read an int value from that object, and
    reading an uninitialized value is UB. There is one possible int value
    that could be a trap representation, neglecting padding bits which
    frankly need to be neglected.

    But you could do this:

    int int_args [3] = { 0 }; /* in a function, initializes every time */

    And in some cases:

    memcpy(&int_args[0], &v[0], 3 * sizeof *int_args);

    (& and [0] added for example clarity), then:

    printf(fmt, int_args[0], int_args[1], ...

    > Regards,
    >
    > Grumble


    --
    Jack Klein
    Home: http://JK-Technology.Com
    FAQs for
    comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
    comp.lang.c++ http://www.parashift.com/c -faq-lite/
    alt.comp.lang.learn.c-c++
    http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html
    Jack Klein, Feb 11, 2004
    #7
  8. Richard Bos

    Richard Bos Guest

    Grumble <> wrote:

    > Richard Bos wrote:
    >
    > > Or simply
    > >
    > > printf(array.format, rand(), rand(), rand(), rand());
    > >
    > > with a comment explaining that printf() will ignore the superfluous
    > > parameters, if necessary.

    >
    > What if I wanted to print values from an array instead of calling rand()?
    >
    > printf(array.format, v[0], v[1], v[2], v[3]);
    >
    > If v was defined as int v[3]; then I suppose the above would yield UB?
    >
    > What if I had 4 variables: int v1, v2, v3, v4;
    >
    > printf(array.format, v1, v2, v3, v4);
    >
    > If v4 was uninitialized but also not used by printf, would the above
    > yield UB?


    Yes and yes, but in the latter case this can be avoided by simply
    initialising the objects to some dummy value, and in the former case you
    may be able to declare the array larger than strictly necessary, or to
    copy it into a temporary array of the right size.

    Richard
    Richard Bos, Feb 11, 2004
    #8
  9. Richard Bos

    Grumble Guest

    Richard Bos wrote:

    > Grumble wrote:
    >
    >> Richard Bos wrote:
    >>
    >>> Or simply
    >>>
    >>> printf(array.format, rand(), rand(), rand(), rand());
    >>>
    >>> with a comment explaining that printf() will ignore the
    >>> superfluous parameters, if necessary.

    >>
    >> What if I wanted to print values from an array instead of calling
    >> rand()?
    >>
    >> printf(array.format, v[0], v[1], v[2], v[3]);
    >>
    >> If v was defined as int v[3]; then I suppose the above would
    >> yield UB?
    >>
    >> What if I had 4 variables: int v1, v2, v3, v4;
    >>
    >> printf(array.format, v1, v2, v3, v4);
    >>
    >> If v4 was uninitialized but also not used by printf, would the
    >> above yield UB?

    >
    > Yes and yes, but in the latter case this can be avoided by simply
    > initialising the objects to some dummy value, and in the former
    > case you may be able to declare the array larger than strictly
    > necessary, or to copy it into a temporary array of the right size.


    Thanks to all for your precious help!

    Bonus question :)

    Assume I want to make the array size a compile-time constant:

    #define MAXPARM 8 /* for example */

    Is there a way to write a clever macro that would expand to v[0],
    v[1], ..., v[MAXPARM-1] whatever the value of MAXPARM? (If it helps,
    you can assume MAXPARM will never be greater than 100.)

    Then I could write:

    int v[MAXPARM] = {0};
    /* initialize v[0], ..., v[k] where k<MAXPARM */
    printf(format, SMART_MACRO(v));

    --
    Regards,

    Grumble
    Grumble, Feb 11, 2004
    #9
  10. Richard Bos

    Richard Bos Guest

    Grumble <> wrote:

    > Bonus question :)
    >
    > Assume I want to make the array size a compile-time constant:
    >
    > #define MAXPARM 8 /* for example */
    >
    > Is there a way to write a clever macro that would expand to v[0],
    > v[1], ..., v[MAXPARM-1] whatever the value of MAXPARM? (If it helps,
    > you can assume MAXPARM will never be greater than 100.)
    >
    > Then I could write:
    >
    > int v[MAXPARM] = {0};
    > /* initialize v[0], ..., v[k] where k<MAXPARM */
    > printf(format, SMART_MACRO(v));


    Erm... well, it's clunky, but you could try using the concatenation
    macro operator, like this:

    #define MAXPARM 8

    #define SMART_MACRO_1(a) a[1]
    #define SMART_MACRO_2(a) a[1], a[2]
    #define SMART_MACRO_3(a) a[1], a[2], a[3]
    :
    :
    #define SMART_MACRO_100(a) a[1], a[2], a[3], ..., a[100]
    #define SMART_MACRO(a) SMART_MACRO ## MAXPARM(a)

    Note that I may have the details of the ## macro wrong; I'm never quite
    sure around # and ## macros. If so, I'm sure some other c.l.c-er will be
    more than happy to correct me.

    Oh, and if you would like a way to get all those SMART_MACRO_n
    definitions into your program without wearing your fingers to the bone,
    Richard Heathfield will probably be along any minute now to show you how
    to write a C program that writes a C program for you :)

    Richard
    Richard Bos, Feb 11, 2004
    #10
  11. Richard Bos

    Grumble Guest

    Richard Bos wrote:

    > Grumble wrote:
    >
    >>Bonus question :)
    >>
    >>Assume I want to make the array size a compile-time constant:
    >>
    >> #define MAXPARM 8 /* for example */
    >>
    >>Is there a way to write a clever macro that would expand to v[0],
    >>v[1], ..., v[MAXPARM-1] whatever the value of MAXPARM? (If it helps,
    >>you can assume MAXPARM will never be greater than 100.)
    >>
    >>Then I could write:
    >>
    >> int v[MAXPARM] = {0};
    >> /* initialize v[0], ..., v[k] where k<MAXPARM */
    >> printf(format, SMART_MACRO(v));

    >
    >
    > Erm... well, it's clunky, but you could try using the concatenation
    > macro operator, like this:
    >
    > #define MAXPARM 8
    >
    > #define SMART_MACRO_1(a) a[1]
    > #define SMART_MACRO_2(a) a[1], a[2]
    > #define SMART_MACRO_3(a) a[1], a[2], a[3]
    > :
    > :
    > #define SMART_MACRO_100(a) a[1], a[2], a[3], ..., a[100]
    > #define SMART_MACRO(a) SMART_MACRO ## MAXPARM(a)
    >
    > Note that I may have the details of the ## macro wrong; I'm never quite
    > sure around # and ## macros. If so, I'm sure some other c.l.c-er will be
    > more than happy to correct me.


    Couldn't vprintf() come in handy here? (I have never used it.)

    Can I build the va_list at run-time?

    Then I could call vprintf(format, ap);

    --
    Regards,

    Grumble
    Grumble, Feb 13, 2004
    #11
  12. Richard Bos

    Chris Torek Guest

    In article <news:c0iv6u$n0m$>
    Grumble <> writes:
    >Couldn't vprintf() come in handy here? (I have never used it.)
    >Can I build the va_list at run-time?
    >Then I could call vprintf(format, ap);


    This is, in fact, a FAQ -- 15.13 to be precise.

    Unfortunately, the less-frequent answer to the F.A.Question
    is "no, or at least, not portably."

    See 15.5 and 15.12 for why vprintf() exists and when to use it.
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
    Chris Torek, Feb 13, 2004
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Adrian Neumeyer

    variables in format strings -- printf

    Adrian Neumeyer, Sep 16, 2003, in forum: C Programming
    Replies:
    9
    Views:
    380
    Kevin D. Quitt
    Sep 22, 2003
  2. Summu82
    Replies:
    5
    Views:
    853
    Richard Heathfield
    Jun 7, 2006
  3. dank
    Replies:
    5
    Views:
    1,467
    Simon Biber
    Jun 30, 2006
  4. Pierre Yves
    Replies:
    2
    Views:
    475
    Pierre Yves
    Jan 10, 2008
  5. ruud
    Replies:
    3
    Views:
    75
    A. Sinan Unur
    Feb 23, 2005
Loading...

Share This Page