468,771 Members | 1,459 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,771 developers. It's quick & easy.

printf with run-time format strings

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

Nov 14 '05 #1
11 5641
Grumble <in*****@kma.eu.org> 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[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;
}
}
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
Nov 14 '05 #2
"Grumble" <in*****@kma.eu.org> wrote in message
news:c0**********@news-rocq.inria.fr...
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

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
Nov 14 '05 #3


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

Nov 14 '05 #4
Al Bowers <xa******@rapidsys.com> wrote:
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 */


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[i].format, rand(), rand(), rand(), rand());

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

Richard
Nov 14 '05 #5
Richard Bos wrote:
Al Bowers <xa******@rapidsys.com> wrote:
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 */

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[i].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[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

Nov 14 '05 #6
On Tue, 10 Feb 2004 17:54:05 +0100, Grumble <in*****@kma.eu.org> wrote
in comp.lang.c:
Richard Bos wrote:
Al Bowers <xa******@rapidsys.com> wrote:
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 */

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[i].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[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?


You are correct, sir.
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?
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/~a...FAQ-acllc.html
Nov 14 '05 #7
Grumble <in*****@kma.eu.org> wrote:
Richard Bos wrote:
Or simply

printf(array[i].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[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?


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
Nov 14 '05 #8
Richard Bos wrote:
Grumble wrote:
Richard Bos wrote:
Or simply

printf(array[i].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[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?


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

Nov 14 '05 #9
Grumble <in*****@kma.eu.org> 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
Nov 14 '05 #10
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

Nov 14 '05 #11
In article <news:c0**********@news-rocq.inria.fr>
Grumble <in*****@kma.eu.org> 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 (4039.22'N, 11150.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.
Nov 14 '05 #12

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

3 posts views Thread by Chris | last post: by
29 posts views Thread by whatluo | last post: by
10 posts views Thread by volunteers | last post: by
14 posts views Thread by Turner, GS \(Geoff\) | last post: by
2 posts views Thread by Jude | last post: by
19 posts views Thread by v4vijayakumar | last post: by
19 posts views Thread by RedDevilDan | last post: by
1 post views Thread by CARIGAR | last post: by
reply views Thread by zhoujie | last post: by
1 post views Thread by Marin | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.