() obsoleted?!

Z

Zeljko Vrba

-----BEGIN PGP SIGNED MESSAGE-----
Hash: RIPEMD160

Can somebody explain why the () function argument list with the semantics
"any number of arguments of any type" has been obsoleted in C99? Is there a
suitable replacements with the same semantics (something like: void f(...))?

This was a _very_ useful thing (although, rarely, but it was). It is used
in some parts of POSIX, e.g. the makecontext function (which is also now
obsoleted because of C99). With the suggestion "use threads" instead of
*context functions. Nonsense! For example, look at my coroutine library for
C: http://www.core-dump.com.hr/index.pl?node_id=422

It makes extensive use of these functions. And can somebody find an
_efficient_ way to translate this into threads?!

Bah, I could just rewrite it to use GNU Pth, it has functions similar
to *context, but with no () declarators.

I was _very_ dissapointed to see this feature removed in C99. In the next
std. they'll probably declare implicit void* conversion obsolete too.. Those
two things are among the 3 things[1] That made possible to use C as a
dynamic typeless language. Now one of them is gone.

[1] The 3rd being the struct address guarantee, i.e. the address of a struct
equals the address of its first member.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQFCR6ihFtofFpCIfhMRA1mIAKCH46kLdH7T8/DDCEVfOCPv8GdBugCfVDiJ
3RRmUh70zn+h3Fqx5gQX5Mw=
=02ho
-----END PGP SIGNATURE-----
 
M

Martijn

Can somebody explain why the () function argument list with the
semantics "any number of arguments of any type" has been obsoleted in
C99? Is there a suitable replacements with the same semantics
(something like: void f(...))?

Yes, you can use ellipses ( ... ) to indicate an unknown amount of arguments
(being zero or more), but this requires at least one known argument, so
something like this (prototype):

int f(int i, ...);

Then you use the va_ set of macros to extract the arguments. Note that some
types are converted to others and you need to have some way of knowing which
types of arguments to expect (and the compiler won't check this for you!)
You also need to include stdarg.h .

All further information is easily acquired using google.

Good luck,
 
Z

Zeljko Vrba

-----BEGIN PGP SIGNED MESSAGE-----
Hash: RIPEMD160

Then you use the va_ set of macros to extract the arguments. Note that some
Not exactly what I'm having in mind. I had the situation like this (when
writing a language interpreter).

void interpret(void)
{
void (*op)();

while(1) {
/* fetch opcode and initialize op based on it */
op(arg1, arg2, ...);
}
}

Then each of the called-by-op functions had their real definition like:

void op1(int);
void op2(int, int);
void op3(int, double);
etc.

the interpreter loop is always calling via the pointer with correct number and
types of arguments.

In this case I didn't have to mess around with va_ macros, and everything
was 'cleaner'... Now the () feature is obsoleted in C99 with nothing to
replace it in future version of the standard...


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQFCR7voFtofFpCIfhMRA28BAJ94me9BORWDHCv3JZJi1z966bbDcQCfdRBQ
9cTfIdh86oFWB5CFsi7Q8Ts=
=gqFm
-----END PGP SIGNATURE-----
 
R

Robert Bachmann

Zeljko said:
Can somebody explain why the () function argument list with the semantics
"any number of arguments of any type" has been obsoleted in C99? Is there a
suitable replacements with the same semantics (something like: void f(...))?

AFAIK void f(...); wasn't a valid declaration in C89 either.
See http://www.eskimo.com/~scs/C-faq/q15.9.html:

| Question 15.9
|
| My compiler isn't letting me declare a function
|
| int f(...)
| {
| }
| i.e. with no fixed arguments.
|
| Standard C requires at least one fixed argument,
| in part so that you can hand it to va_start.
|
| References: ANSI Sec. 3.5.4, Sec. 3.5.4.3, Sec. 4.8.1.1
| ISO Sec. 6.5.4, Sec. 6.5.4.3, Sec. 7.8.1.1
 
P

pjp

Can somebody explain why the () function argument list with the
semantics
"any number of arguments of any type" has been obsoleted in C99?

[pjp] No. I just looked at the C99 Standard and it appears to still be
there. IIRC, the only thing that went away in this area was the ability
to call a function that had not been declared.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 
A

Andrey Tarasevich

Zeljko said:
...
In this case I didn't have to mess around with va_ macros, and everything
was 'cleaner'... Now the () feature is obsoleted in C99 with nothing to
replace it in future version of the standard...
...

What exactly do you mean by this? C99 refers to non-prototype function
declarations as an "obsolescent feature". C89/90 refers to this feature
in exactly the same way. Nothing changed in this respect from C89/90 to
C99. Yet you are complaining about some change in C99. What change are
you talking about?
 
Z

Zeljko Vrba

-----BEGIN PGP SIGNED MESSAGE-----
Hash: RIPEMD160

[pjp] No. I just looked at the C99 Standard and it appears to still be
there. IIRC, the only thing that went away in this area was the ability
to call a function that had not been declared.
Look at the following link:
http://www.opengroup.org/onlinepubs/009695399/functions/makecontext.html

And quotes from the rationale:

"With the incorporation of the ISO/IEC 9899:1999 standard into this
specification it was found that the ISO C standard (Subclause 6.11.6)
specifies that the use of function declarators with empty parentheses is
an obsolescent feature."

"There is no way in the ISO C standard to specify a non-obsolescent function
prototype indicating that a function will be called with an arbitrary number
(including zero) of arguments of arbitrary types (including integers, pointers
to data, pointers to functions, and composite types)."

While it did not go away, it is marked obsoleted. That was my only original
claim. Which this paragraph expresses puts more succintly and clearly. And
the question, of course, remains: why it was obsoleted?


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQFCSSQ0FtofFpCIfhMRA2+GAJwMj0DwQRM9hNHugizHYO8H5v1EugCfSPOe
ET2BQuGErCWehdXwYpC3k/M=
=3wvW
-----END PGP SIGNATURE-----
 
K

Keith Thompson

Zeljko Vrba said:
[pjp] No. I just looked at the C99 Standard and it appears to still be
there. IIRC, the only thing that went away in this area was the ability
to call a function that had not been declared.
Look at the following link:
http://www.opengroup.org/onlinepubs/009695399/functions/makecontext.html

And quotes from the rationale:

"With the incorporation of the ISO/IEC 9899:1999 standard into this
specification it was found that the ISO C standard (Subclause 6.11.6)
specifies that the use of function declarators with empty parentheses is
an obsolescent feature."

"There is no way in the ISO C standard to specify a non-obsolescent
function prototype indicating that a function will be called with an
arbitrary number (including zero) of arguments of arbitrary types
(including integers, pointers to data, pointers to functions, and
composite types)."

While it did not go away, it is marked obsoleted. That was my only original
claim. Which this paragraph expresses puts more succintly and clearly. And
the question, of course, remains: why it was obsoleted?

"Obsolescent" does not been obsolete. It merely means that it could
(not necessarily that it will) become obsolete in a future standard.

C90 6.9.4 says:

The use of function declarators with empty parentheses (not
prototype-format parameter type declarators) is an obsolescent
feature.

C99 6.11.6 says exactly the same thing:

The use of function declarators with empty parentheses (not
prototype-format parameter type declarators) is an obsolescent
feature.

The feature is still fully supported by the C90 and C99 standards.
 
C

CBFalconer

Zeljko said:
.... snip ...

And quotes from the rationale:
.... snip ...

"There is no way in the ISO C standard to specify a non-obsolescent
function prototype indicating that a function will be called with
an arbitrary number (including zero) of arguments of arbitrary
types (including integers, pointers to data, pointers to functions,
and composite types)."

While it did not go away, it is marked obsoleted. That was my only
original claim. Which this paragraph expresses puts more succintly
and clearly. And the question, of course, remains: why it was
obsoleted?

Because it is a security risk and doesn't prevent writing any
correct code. You always have varargs and its insecurities
available. It should be obvious that the requirement for one known
parameter is not a bind, since such is necessary to transfer
information about any further parameters in all cases.
 
A

Andrey Tarasevich

Zeljko said:
Look at the following link:
http://www.opengroup.org/onlinepubs/009695399/functions/makecontext.html

And quotes from the rationale:

"With the incorporation of the ISO/IEC 9899:1999 standard into this
specification it was found that the ISO C standard (Subclause 6.11.6)
specifies that the use of function declarators with empty parentheses is
an obsolescent feature."

I don't know why ISO/IEC 9899:1999 is mentioned there. As it was said
before, this type of declaration was referred to as "obsolescent" in the
C89/90 standard as well. Nothing really changed in C99. Apparently,
authors of the above document didn't know this fact. Instead of taking a
minute to verify it they jumped to conclusion that this was a new
addition to C99.
That was my only original
claim. Which this paragraph expresses puts more succintly and clearly. And
the question, of course, remains: why it was obsoleted?

It probably become obsolescent "in transition" from K&R to C89/90 as
something that falls into the same family as K&R-style function
definitions (which are also obsolescent).
 
P

Peter Nilsson

CBFalconer said:
Because it is a security risk and doesn't prevent writing any
correct code. You always have varargs and its insecurities
available. It should be obvious that the requirement for one
known parameter is not a bind, since such is necessary to
transfer information about any further parameters in all cases.

Hmmm...

I wouldn't say in _all_ cases. One has to ask why function macros
can be declared identifier(...) in C99, but functions can't.

C++ allows (...) functions as a 'catch all'.

It's not beyond possibility that C programmers have occasionally
wished for such a facility to be available in C. Of course, C
doesn't have function overloading, so the need is considerably
less.

But that doesn't mean the requirement for one known parameter in
variadic functions is ("obviously") _never_ a bind. (IMO)
 
C

CBFalconer

Peter said:
.... snip ...

But that doesn't mean the requirement for one known parameter in
variadic functions is ("obviously") _never_ a bind. (IMO)

Because you have to be able to transmit something in order to
describe how many things are being transmitted. It's not a bind
because it is absolutely necessary.
 
B

Ben Pfaff

CBFalconer said:
Because you have to be able to transmit something in order to
describe how many things are being transmitted. It's not a bind
because it is absolutely necessary.

It is not absolutely necessary, e.g. I can pass a null-terminated
list of pointers.
 
P

Peter Nilsson

CBFalconer said:
Because you have to be able to transmit something in order to
describe how many things are being transmitted.

What if the function wants to ignore all the parameters and
do nothing?
It's not a bind because it is absolutely necessary.

Why?

[The discussion (snipped above) is why the standard says
it is necessary for functions. It isn't necessary for
function macros in C99.]
 
A

Arthur J. O'Dwyer

It is not absolutely necessary, e.g. I can pass a null-terminated
list of pointers.

Even a null-terminated list has to contain at least one element (namely,
a null pointer). I agree with CBFalconer in practice, but with the other
side in theory; certainly there's nothing intrinsically /wrong/ with

void foo(...) {
/* ignore all arguments and */ return;
}

or even [UNTESTED PSEUDO-C]

int bar_type;
int bar(...) {
va_list ap;
va_start(ap);
switch (bar_type) { /* set by the user before this call */
case 0: printf("%d", va_arg(ap, int)); break;
case 1: printf("%s", va_arg(ap, const char*)); break;
case 2: printf("%g", va_arg(ap, double)); break;
}
va_end(ap);
return bar_type;
}

The standard just arbitrarily disallows such constructions. I think
the restriction has absolutely no effect on /real-world/ programs, but
it is kind of weird and non-orthogonal, if you ask me.

my $.02,
-Arthur
 
C

CBFalconer

Ben said:
It is not absolutely necessary, e.g. I can pass a null-terminated
list of pointers.

In which case not only is the type of the first parameter known,
but the types of all of them.
 
P

Peter Nilsson

Arthur said:
...I agree with CBFalconer in practice, but with the other side
in theory; certainly there's nothing intrinsically /wrong/ with

void foo(...) {
/* ignore all arguments and */ return;
}

The difficulty lies with implementing it. C++ allows it, but C++ can
identify calls to such functions since it requires function
prototypes to be in scope.
or even [UNTESTED PSEUDO-C]

int bar_type;
int bar(...) {
va_list ap;
va_start(ap);
switch (bar_type) { /* set by the user before this call */
case 0: printf("%d", va_arg(ap, int)); break;
case 1: printf("%s", va_arg(ap, const char*)); break;
case 2: printf("%g", va_arg(ap, double)); break;
}
va_end(ap);
return bar_type;
}

My conjecture was not that the parameters should be deducable. Although
that would also be nice, I can see such a requirement causing headaches
for implementors. Certainly, having two forms of va_start() would be a
pain.
The standard just arbitrarily disallows such constructions.

Given my points above, I doubt it was _arbitrarily_ disallowed.

It would certainly be possible to implement within C on the
architectures I know of, especially if function prototypes for
named functions were mandatory, but the lack of application,
and immediate obvious benefit, is the most likely reason for its
absense from the language.
I think the restriction has absolutely no effect on /real-world/
programs,

Playing advocate again, that's easy to say since no program can use
the construct portably now. For instance, I could say that not
having a typeof() macro has absolutely no effect on /real-world/
programs, but if such programs had the option... they _would_ use
it. At least, I would. ;)
but it is kind of weird and non-orthogonal, if you ask me.

Yet it exists in C++, and seemingly not for any reasons over and above
what's already been discussed here with regards to C.

I'm won't claim that my discussion is worth more than that. ;)
 
A

Arthur J. O'Dwyer

...I agree with CBFalconer in practice, but with the other side
in theory; certainly there's nothing intrinsically /wrong/ with [...]
or even [UNTESTED PSEUDO-C]

int bar_type;
int bar(...) {
va_list ap;
va_start(ap);
[...]
Certainly, having two forms of va_start() would be a pain.

Hm. I said it was untested. :) I guess that's a very good reason
for not adding the capability at this point in C's development; there'd
need to be a new form of 'va_start'. (I just completely forgot about
the second parameter to 'va_start' when I was writing the above code.)
Yet it exists in C++, and seemingly not for any reasons over and above
what's already been discussed here with regards to C.

How does C++ handle 'int bar(...)', if the 'va_start' interface is
the same as in C? I assume you're not just talking about overloading
and default parameter values, but actually variadic functions using
the '...' syntax.

-Arthur
 
E

Eric Sosman

CBFalconer said:
In which case not only is the type of the first parameter known,
but the types of all of them.

#ifdef HYPOTHETICAL_C
void receive_from_true_love(...);
#else
void receive_from_true_love(const char *, ...);
#endif

receive_from_true_love(
"Partridge in a pear tree", 1,
"Turtledoves", 2,
"French hens", 3,
/* ... */
"Lords a-leaping", 12,
(char*)NULL);

The requirement for at least one fixed argument makes it
clumsy (but possible) to write functions like this. On the
other hand, it's not a great loss: Long argument lists of mixed
type are themselves clumsy, and the lack of type-checking in
the `...' portion postpones the detection of errors. There
are better ways to design the receive_from_true_love() function,
even if the process of writing calls to it becomes a little
less free-wheeling.

The Rationale offers some hints about the reasons for
requiring fixed arguments before the `...'. The va_start
description gives what seems to be the primary motivation:

"The parmN argument to va_start was intended to be
an aid to implementors writing the definition of a
conforming va_start macro entirely in C, even using
pre-C89 compilers (for example, by taking the address
of the parameter). [...]"

There's also a hint that the Committee may have considered
<stdarg.h> as an enhancement to <varargs.h>:

"The definitions of these macros in the Standard differ
from their forebears: they have been *extended* [emphasis
mine] to support argument lists that have a fixed set of
arguments preceding the variable list."

Finally, there's a potential benefit (not mentioned in the
Rationale, as far as I can see) to having a fixed prefix to
the argument list: The compiler knows the types of these
arguments and can check or convert them, which it could not
do if the entire argument list were `...'. In a recent thread,
a C beginner was attempting something along the lines of (IIRC):

char buff[100];
fprintf (buff, "%d", number);

.... and the compiler's knowledge about the first two arguments
to fprintf() allowed it to diagnose the error. In the Bad Old
Days it was all too common to encounter

fprintf ("Unable to open %s\n", filename);

.... and this sort of error could evade testing for quite a long
time.
 
C

Chris Torek

The difficulty lies with implementing [variable arguments with
no fixed argument before the first variable argument].
C++ allows it, but C++ can identify calls to such functions since
it requires function prototypes to be in scope.

C requires function prototypes to be in scope for all variadic
function calls too, so it would have been possible.
... Certainly, having two forms of va_start() would be a
pain.

Before the 1989 standard, there was only one form of va_start,
and it took only one argument:

int like_printf(va_alist)
va_dcl /* note: no semicolon here */
{
va_list ap;
char *fmt;

va_start(ap);
fmt = va_arg(ap, char *);
vprintf(fmt, ap);
va_end(ap);
}

This would have been 100% forwards-and-backwards compatible
by including these, either in <stdarg.h> or in the old <varargs.h>
header:

#define va_alist ...
#define va_dcl /* nothing */

which would have expanded to the C99 prototype:

int like_printf(...) { va_list ap; /* and code */ }

Instead, the X3J11 committee not only stole the new "..." syntax
from C++, they also changed va_start to take *two* arguments.
Given my points above, I doubt it was _arbitrarily_ disallowed.

I think it *was* arbitrary -- at least "95% arbitrary" anyway.

Note that any existing C compiler vendor had to change their
compiler to accept the new ", ..." token-sequence, in order
to handle the new prototypes:

int like_printf(const char *fmt, ...);

The "at least one fixed argument" requirement did, however, allow
them to make only the one single change to their compilers, instead
of TWO (*gasp*, horrors!), yes TWO whole changes. Suppose that,
instead of the "two arguments to va_start" change, the C89 folks
had left the existing <varargs.h> syntax alone, merely adding the
"..." ellipsis syntax for prototypes. Suppose further that they
were to declare that "the va_start macro sets up its va_list argument
to access the variable arguments". How would compiler-writers
implement this? Well, there are at least two obvious ways:

/* Method A */
#define va_start(ap) __builtin_va_start(ap)

/* Method B */
#define va_start(ap) ((ap) = &...)

Either one requires a second change to the compiler: With Method A,
the compiler needs to recognize __builtin_* functions (as gcc does).
With Method B, the compiler needs to allow unary "&" to be applied
to "..." in variable-arguments functions.

Neither of these is at all difficult in real, practical compilers.
In particular, Method B is trivial to implement in real compilers
that currently use some variation on:

#define va_start(last, ap) ((ap) = (char *)&last + sizeof last)

(A number of real compilers really do just this. Not only is it
easy -- just add "&..." as a production rule to your grammar, and
do the obvious thing in the code-generation step -- it eliminates
the problem that occurs when "last" is a narrow argument, so that
sizeof(last) needs adjusting.)

The at-least-one-fixed-argument rule would now be allowed, but
*not* *required*, because the va_start() macro would not need the
name of the last argument. This would also remove the opportunity
for the programmer to supply the wrong name, i.e., it would prevent
bugs.

Unfortunately, we are now stuck with the inferior (and incompatible
with K&R-style <varargs.h>) design, and the "at least one fixed
argument" rule. The flaw is minor, like so many of C's other little
flaws, but it is still a flaw.
 

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

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,165
Latest member
JavierBrak
Top