reading <stdarg.h>'s va_list twice

T

tonybalinski

I'd like to be able to scan a va_list twice in a v... function, but
can't see how to do it. For example

char *strdupcat(const char *first, ...)
{
char *result, pos;
va_list arg;
size_t len;
const char *next;

va_start(arg, first);
next = first;
while (next) {
len += strlen(next);
next = va_arg(arg, const char *);
}
va_end(arg);

result = pos = malloc(len + 1);

va_start(arg, first);
next = first;
while (next) {
strcpy(pos, next);
pos += strlen(pos);
next = va_arg(arg, const char *);
}
va_end(arg);
*pos = 0;

return result;
}

This should be fine (it's a close paraphrase, but I haven't tried it).
It concatenates string arguments on the argument until one which is
NULL is reached. Now I want to have a function (analogous to vprintf
for printf) that does the same thing called from another variadic
function - how do I handle the rewinding done by va_end/va_start in a
portable way, given that I won't have "first" again? In other words,
how can I write (portably) vstrdupcat in:

char *choseThenConcat(int which, const char *first, ...)
{
char *res;
/* do something with which, then */
va_list args;
va_start(args, first);
res = vstrdupcat(first, args);
va_end();
/* do stuff with res */
return ...something-or-other...
}
 
B

Ben Bacarisse

I'd like to be able to scan a va_list twice in a v... function, but
can't see how to do it.
how do I handle the rewinding done by va_end/va_start in a
portable way, given that I won't have "first" again? In other words,
how can I write (portably) vstrdupcat in:

char *choseThenConcat(int which, const char *first, ...)
{
char *res;
/* do something with which, then */
va_list args;
va_start(args, first);
res = vstrdupcat(first, args);
va_end();
/* do stuff with res */
return ...something-or-other...
}

va_copy
 
B

Ben Bacarisse

Where would you put the va_copy?

Inside a function that takes a va_list you can do this:

void example(va_list alist)
{
va_list copy_of_alist;
va_copy(copy_of_alist, alist);
/* process original alist */
/* process copy_of_alist */
va_end(alist);
va_end(copy_of_alist);
}
And for pre-C99? I want this to work with C89, if at all possible.

Ah. In that case I break you concatenation function into two. One to
whatever the first pass does (no doubt count the strings and their
lengths) and another to do the concatenation.

In fact, I'd be inclined do this anyway.
 
T

tonybalinski

Ben said:
Ah. In that case I break you concatenation function into two. One to
whatever the first pass does (no doubt count the strings and their
lengths) and another to do the concatenation.

In fact, I'd be inclined do this anyway.
I was afraid you'd say that... but thanks anyway!
 
B

Ben Pfaff

Ben Bacarisse said:
[va_copy]
And for pre-C99? I want this to work with C89, if at all possible.

Ah. In that case I break you concatenation function into two.

If you are willing to give up strict portability, then you may
find that the following (or similar; I have not carefully
proofread it) is good enough:

#include <stdarg.h>
#ifndef va_copy
# ifdef __va_copy
# define va_copy(a, b) __va_copy(a, b)
# else
# define va_copy(a, b) ((a) = (b))
# endif
#endif
In that case I break you concatenation function into two. One to
whatever the first pass does (no doubt count the strings and their
lengths) and another to do the concatenation.

Of course, that will definitely work.
 
P

Peter Nilsson

Ben Bacarisse said:
...
void example(va_list alist)
{
    va_list copy_of_alist;
    va_copy(copy_of_alist, alist);
    /* process original alist */
    /* process copy_of_alist */
    va_end(alist);

No, va_end should be applied to alist in the function
that initialised alist with va_start or va_copy.
    va_end(copy_of_alist);

}


Ah.  In that case I break you concatenation function
into two.  One to whatever the first pass does (no
doubt count the strings and their lengths) and another
to do the concatenation.

Or pass two va_lists.
 
B

Ben Bacarisse

Peter Nilsson said:
No, va_end should be applied to alist in the function
that initialised alist with va_start or va_copy.

Yes, thanks. I don't know how that got in there.
Or pass two va_lists.

I am probably too old to say it but I am thinking "sweet!" :) (and
also "why didn't I think of that?").
 
P

Peter Nilsson

I'd like to be able to scan a va_list twice in a v...
function, but can't see how to do it. For example

  char *strdupcat(const char *first, ...)
  {

You don't have to scan it twice...

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

static
char *vzjoin(size_t z, const char *head, va_list ap)
{
char *r;

if (head == 0)
{
r = malloc(z + 1);
r[z] = 0;
}
else
{
size_t hz = strlen(head);
char *tail = va_arg(ap, char *);
r = vzjoin(z + hz, tail, ap);
memcpy(r + z, head, hz);
}

return r;
}

char *vjoin(const char *head, va_list ap)
{
return vzjoin(0, head, ap);
}

char *join(const char *head, ...)
{
char *r;
va_list ap;

va_start(ap, head);
r = vjoin(head, ap);
va_end(ap);

return r;
}
 
T

Tim Rentsch

Ben Pfaff said:
Ben Bacarisse said:
[va_copy]
And for pre-C99? I want this to work with C89, if at all possible.

Ah. In that case I break you concatenation function into two.

If you are willing to give up strict portability, then you may
find that the following (or similar; I have not carefully
proofread it) is good enough:

#include <stdarg.h>
#ifndef va_copy
# ifdef __va_copy
# define va_copy(a, b) __va_copy(a, b)
# else
# define va_copy(a, b) ((a) = (b))
# endif
#endif

It's probably better to use memcpy() rather than assignment,
in case the type involved is an array type (and __va_copy
isn't available, even though it "hopefully will be").
 

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,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top