Connecting Tech Pros Worldwide Help | Site Map

printf with run-time format strings

Grumble
Guest
 
Posts: n/a
#1: Nov 14 '05
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[i].parm)
{
case 0:
printf(array[i].format); break;
case 1:
printf(array[i].format, rand()); break;
...
case 4:
printf(array[i].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

Richard Bos
Guest
 
Posts: n/a
#2: Nov 14 '05

re: printf with run-time format strings


Grumble <invalid@kma.eu.org> wrote:
[color=blue]
> 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());[/color]

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...
[color=blue]
> 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[i].parm)
> {
> case 0:
> printf(array[i].format); break;
> case 1:
> printf(array[i].format, rand()); break;
> ...
> case 4:
> printf(array[i].format, rand(), rand(), rand(), rand()); break;
> }
> }[/color]

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.
[color=blue]
> Isn't there a better way? If I think in terms of a stack (which I know
> is a sin in c.l.c.)[/color]

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.
[color=blue]
> 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.[/color]

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.
[color=blue]
> I've never used variadic functions.[/color]

(Yes, you have; printf() is one.)
[color=blue]
> Could they prove useful in this case?[/color]

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
lallous
Guest
 
Posts: n/a
#3: Nov 14 '05

re: printf with run-time format strings


"Grumble" <invalid@kma.eu.org> wrote in message
news:c0ap12$h1e$1@news-rocq.inria.fr...[color=blue]
> 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[i].parm)
> {
> case 0:
> printf(array[i].format); break;
> case 1:
> printf(array[i].format, rand()); break;
> ...
> case 4:
> printf(array[i].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
>[/color]
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


Al Bowers
Guest
 
Posts: n/a
#4: Nov 14 '05

re: printf with run-time format strings




Grumble wrote:[color=blue]
> 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[i].parm)
> {
> case 0:
> printf(array[i].format); break;
> case 1:
> printf(array[i].format, rand()); break;
> ...
> case 4:
> printf(array[i].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.
>[/color]

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: xabowers@myrapidsys.com (remove the x to send email)
http://www.geocities.com/abowers822/

Richard Bos
Guest
 
Posts: n/a
#5: Nov 14 '05

re: printf with run-time format strings


Al Bowers <xabowers@rapidsys.com> wrote:
[color=blue]
> Grumble wrote:[color=green]
> > for (i=0; i < 100; ++i)
> > {
> > switch(array[i].parm)
> > {
> > case 0:
> > printf(array[i].format); break;
> > case 1:
> > printf(array[i].format, rand()); break;
> > ...
> > case 4:
> > printf(array[i].format, rand(), rand(), rand(), rand()); break;
> > }
> > }[/color]
>
> 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 */[/color]

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.
[color=blue]
> Then you printf would look this this:
> printf(array.format,ARGS);[/color]

Or simply

printf(array[i].format, rand(), rand(), rand(), rand());

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

Richard
Grumble
Guest
 
Posts: n/a
#6: Nov 14 '05

re: printf with run-time format strings


Richard Bos wrote:
[color=blue]
> Al Bowers <xabowers@rapidsys.com> wrote:
>[color=green]
>>Grumble wrote:
>>[color=darkred]
>>>for (i=0; i < 100; ++i)
>>>{
>>> switch(array[i].parm)
>>> {
>>> case 0:
>>> printf(array[i].format); break;
>>> case 1:
>>> printf(array[i].format, rand()); break;
>>> ...
>>> case 4:
>>> printf(array[i].format, rand(), rand(), rand(), rand()); break;
>>> }
>>>}[/color]
>>
>>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 */[/color]
>
>
> 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.
>
>[color=green]
>>Then you printf would look this this:
>>printf(array.format,ARGS);[/color]
>
>
> Or simply
>
> printf(array[i].format, rand(), rand(), rand(), rand());
>
> with a comment explaining that printf() will ignore the superfluous
> parameters, if necessary.[/color]

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

printf(array[i].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[i].format, v1, v2, v3, v4);

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

Regards,

Grumble

Jack Klein
Guest
 
Posts: n/a
#7: Nov 14 '05

re: printf with run-time format strings


On Tue, 10 Feb 2004 17:54:05 +0100, Grumble <invalid@kma.eu.org> wrote
in comp.lang.c:
[color=blue]
> Richard Bos wrote:
>[color=green]
> > Al Bowers <xabowers@rapidsys.com> wrote:
> >[color=darkred]
> >>Grumble wrote:
> >>
> >>>for (i=0; i < 100; ++i)
> >>>{
> >>> switch(array[i].parm)
> >>> {
> >>> case 0:
> >>> printf(array[i].format); break;
> >>> case 1:
> >>> printf(array[i].format, rand()); break;
> >>> ...
> >>> case 4:
> >>> printf(array[i].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 */[/color]
> >
> >
> > 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.
> >
> >[color=darkred]
> >>Then you printf would look this this:
> >>printf(array.format,ARGS);[/color]
> >
> >
> > Or simply
> >
> > printf(array[i].format, rand(), rand(), rand(), rand());
> >
> > with a comment explaining that printf() will ignore the superfluous
> > parameters, if necessary.[/color]
>
> What if I wanted to print values from an array instead of calling rand()?
>
> printf(array[i].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?[/color]

You are correct, sir.
[color=blue]
> What if I had 4 variables: int v1, v2, v3, v4;
>
> printf(array[i].format, v1, v2, v3, v4);
>
> If v4 was uninitialized but also not used by printf, would the above
> yield UB?[/color]

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], ...
[color=blue]
> Regards,
>
> Grumble[/color]

--
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/~a...FAQ-acllc.html
Richard Bos
Guest
 
Posts: n/a
#8: Nov 14 '05

re: printf with run-time format strings


Grumble <invalid@kma.eu.org> wrote:
[color=blue]
> Richard Bos wrote:
>[color=green]
> > Or simply
> >
> > printf(array[i].format, rand(), rand(), rand(), rand());
> >
> > with a comment explaining that printf() will ignore the superfluous
> > parameters, if necessary.[/color]
>
> What if I wanted to print values from an array instead of calling rand()?
>
> printf(array[i].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[i].format, v1, v2, v3, v4);
>
> If v4 was uninitialized but also not used by printf, would the above
> yield UB?[/color]

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
Grumble
Guest
 
Posts: n/a
#9: Nov 14 '05

re: printf with run-time format strings


Richard Bos wrote:
[color=blue]
> Grumble wrote:
>[color=green]
>> Richard Bos wrote:
>>[color=darkred]
>>> Or simply
>>>
>>> printf(array[i].format, rand(), rand(), rand(), rand());
>>>
>>> with a comment explaining that printf() will ignore the
>>> superfluous parameters, if necessary.[/color]
>>
>> What if I wanted to print values from an array instead of calling
>> rand()?
>>
>> printf(array[i].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[i].format, v1, v2, v3, v4);
>>
>> If v4 was uninitialized but also not used by printf, would the
>> above yield UB?[/color]
>
> 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.[/color]

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

Richard Bos
Guest
 
Posts: n/a
#10: Nov 14 '05

re: printf with run-time format strings


Grumble <invalid@kma.eu.org> wrote:
[color=blue]
> 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));[/color]

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
Grumble
Guest
 
Posts: n/a
#11: Nov 14 '05

re: printf with run-time format strings


Richard Bos wrote:
[color=blue]
> Grumble wrote:
>[color=green]
>>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));[/color]
>
>
> 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.[/color]

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

Chris Torek
Guest
 
Posts: n/a
#12: Nov 14 '05

re: printf with run-time format strings


In article <news:c0iv6u$n0m$1@news-rocq.inria.fr>
Grumble <invalid@kma.eu.org> writes:[color=blue]
>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);[/color]

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.
Closed Thread