printf with run-time format strings

R

Richard Bos

Grumble said:
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
 
G

Grumble

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
 
R

Richard Bos

Al Bowers said:
Grumble said:
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
 
L

lallous

Grumble said:
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.
 
A

Al Bowers

Grumble said:
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;
}
 
G

Grumble

Richard said:
Al Bowers said:
Grumble said:
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
 
J

Jack Klein

Richard said:
Al Bowers said:
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], ...
 
R

Richard Bos

Grumble said:
Richard said:
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
 
G

Grumble

Richard said:
Grumble said:
Richard said:
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));
 
R

Richard Bos

Grumble said:
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
 
G

Grumble

Richard said:
Grumble said:
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);
 
C

Chris Torek

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top