To my understanding this is legal C :
int foo ();
It's a K&R-style variadic functions, while their use is discouraged,
they're still legal C.
If I, in D, declare a variadic function with C linkage that doesn't
take, at least, one regular parameter the compiler will complain.
extern (C) int foo (...);
Error: variadic functions with non-D linkage must have at least one
parameter
Does that mean I can't use a function like this from D?
I'm trying to figure out what my tool, DStep, should do when it
encounters a function like this.
https://github.com/jacob-carlborg/dstep
--
/Jacob Carlborg

First I wanted to know if my interpretation is correct and then I was
trying to figure out how my tool should behave if it encounters a
function like this.

After a bit of googling and a test with my local MSVC9 I think old-style
variadics look like this:
#include <varargs.h>
#include <stdio.h>
void foo(va_alist)
va_dcl
{
va_list p;
va_start(p);
vprintf("%d %d %d\n", p);
}
void main()
{
foo(1, 2, 3);
}
(the above runs and outputs "1 2 3" on the console)
The same syntax is/was supported by GNU C, see:
http://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_chapter/libc_34.html#SEC676
I believe, if you see an "old-style" function declaration in a header file
like:
int foo ();
that you can't actually assume anything about it's parameters, it may have
/any/ number of parameters, and may or may not be variadic.
e.g. see: http://msdn.microsoft.com/en-us/library/efx873ys.aspx
The "declaration" syntax shown there for a function with 2 arguments is
simply:
double old_style();
In short, I don't think you can produce a valid .di/d file for old-style
declarations automatically, it will require a manual step where the user
enters the function type-arguments.
However, if you see an old-style:
int foo(void);
you can be sure it has 0 parameters and can happily produce:
extern (C) int foo();
So, I think you should assume no old-style function declarations but have
a command line option/mode which assumes all functions declarations are
old-style. In this mode you would output something like..
extern (C) <return-type> <function-name>(/*TODO: add args here*/);
and require the user manually find and define these.
I can't think of any other way you could do it.
R
p.s. I found this copy of the C spec online:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
section 6.7.5.3 appears to deal with function declarations.
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Some follow up information, for interests sake.
1. va_dcl is defined in varargs.h as..
#define va_dcl va_list va_alist;
So, the old-style variable args function definiton can be written:
void foo(va_alist)
va_list va_alist;
{
...
}
2. va_start in defined in varargs.h as..
#define va_start(ap) ap = (va_list)&va_alist
So, it assumes a parameter called va_alist exists, and it simply takes the
address of it. A new style variadic uses a definition like this..
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) +
_INTSIZEOF(v) )
which takes the address of 'v' (which is the argument prior to the ...)
then skips past it, obtaining the address of the first ... argument.
So, in effect they are doing the same thing, taking the address of the
first variable argument. In which case, I can only assume if we can call
new-style variadics from D, we can call old-style ones as well - provided
any preceding arguments to the va_alist argument are defined correctly,
.e.g.
given this old-style function declaration:
void foo();
which actually happens to match this old-style variadic function
definition:
void foo(a,b,va_alist)
int a;
int b;
va_list va_alist;
{
}
we could produce the following:
extern (C) void foo(/*TODO: add args here*/);
and, once the user filled in the args
extern (C) void foo(int a, int b, ...);
it should be callable from D.. in fact I just did a test (albeit with a
slightly different test case..)
[testdll.c]
#include <varargs.h>
#include <stdio.h>
__declspec( dllexport )
void foo(va_alist)
va_list va_alist;
{
va_list p;
va_start(p);
vprintf("%d %d %d\n", p);
}
(compiled with MSVC to dll and lib, lib converted with coffimplib to OMF
format)
[test.d]
extern (C) void foo(int a, ...);
int main(string[] args)
{
foo(1, 3, 5);
return 0;
}
compile with dmd test.d testdll.lib, run test.exe, outputs "1 3 5" :)
But, this highlights one issue I was unaware of. The D extern (C)
declaration needs at least 1 parameter before the ..., having just:
extern (C) void foo(...);
is illegal.
In my specific case I could add "int a" as the first parameter, and all
was well. Each case will be different, and it's conceivable there is a C
old-style variadic which takes /any/ type of first parameter, which could
be a problem, however the key issue is the size of the argument. If all
types which could be passed are 32 bits big, then "int a" is sufficient to
get it working in all cases.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

In my specific case I could add "int a" as the first parameter, and all
was well. Each case will be different, and it's conceivable there is a
C old-style variadic which takes /any/ type of first parameter, which
could be a problem, however the key issue is the size of the argument.
If all types which could be passed are 32 bits big, then "int a" is
sufficient to get it working in all cases.

In my specific case I could add "int a" as the first parameter, and all
was well. Each case will be different, and it's conceivable there is a
C old-style variadic which takes /any/ type of first parameter, which
could be a problem, however the key issue is the size of the argument.
If all types which could be passed are 32 bits big, then "int a" is
sufficient to get it working in all cases.

It could just as well be no parameters.

I believe old-style no parameter function declarations MUST have "void"
i.e.
int foo(void);
They cannot read:
int foo();
The latter MUST have parameters, we just can't tell what they are.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Ahh, I've been looking at the ANSI C spec and assuming that is what you're
basing things off, K&R C was pre-ANSI C and may have different rules. I
think you should probably aim to be ANSI C compliant and above, and ignore
K&R.
Looking at the ANSI C spec again, section 6.7.5.3, item 10 says:
"The special case of an unnamed parameter of type void as the only item in
the list specifies that the function has no parameters."
So, "void" indicates no parameters..
Item 14 is also applicable and says:
"An identifier list declares only the identifiers of the parameters of the
function. An empty list in a function declarator that is part of a
definition of that function specifies that the function has no parameters.
The empty list in a function declarator that is not part of a definition
of that function specifies that no information about the number or types
of the parameters is supplied." 124)
The latter part of that is applicable to declarations in header files (the
former is for definitions in c files); "The empty list in a function
declarator that is /not part of a definition of that function/ specifies
that /no information about the number or types of the parameters is
supplied/."
So, a function like:
int foo();
in a header "specifies that no information about the number or types of
the parameters is supplied".
However footnote 124) says see 6.1.6, and 6.1.6 says:
6.11.6 Function declarators
The use of function declarators with empty parentheses (not
prototype-format parameter type declarators) is an obsolescent feature.
So, coming full circle, it seems like I'm right after all .. I think.
"void" is required to indicate no parameters and () is obsolete in ANSI C.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Ahh, I've been looking at the ANSI C spec and assuming that is what
you're basing things off, K&R C was pre-ANSI C and may have different
rules. I think you should probably aim to be ANSI C compliant and
above, and ignore K&R.

Looking at the ANSI C spec again, section 6.7.5.3, item 10 says:
"The special case of an unnamed parameter of type void as the only item
in the list specifies that the function has no parameters."
So, "void" indicates no parameters..
Item 14 is also applicable and says:
"An identifier list declares only the identifiers of the parameters of
the function. An empty list in a function declarator that is part of a
definition of that function specifies that the function has no
parameters. The empty list in a function declarator that is not part of
a definition of that function specifies that no information about the
number or types of the parameters is supplied." 124)
The latter part of that is applicable to declarations in header files
(the former is for definitions in c files); "The empty list in a
function declarator that is /not part of a definition of that function/
specifies that /no information about the number or types of the
parameters is supplied/."
So, a function like:
int foo();
in a header "specifies that no information about the number or types of
the parameters is supplied".
However footnote 124) says see 6.1.6, and 6.1.6 says:
6.11.6 Function declarators
The use of function declarators with empty parentheses (not
prototype-format parameter type declarators) is an obsolescent feature.
So, coming full circle, it seems like I'm right after all .. I think.
"void" is required to indicate no parameters and () is obsolete in ANSI C.

Ahh, I've been looking at the ANSI C spec and assuming that is what
you're basing things off, K&R C was pre-ANSI C and may have different
rules. I think you should probably aim to be ANSI C compliant and
above, and ignore K&R.

The full quote:
"In the above example, a prototype is used in a function declaration for
ANSI compliant implementations, while an obsolescent non-prototype
declaration is used otherwise. Those are still ANSI-compliant as of C99
and C90, but their use is discouraged."
1) "a prototype is used in a function declaration for ANSI compliant
implementations"
implies an ANSI compliant compiler /requires/ the full prototype.
2) "obsolescent non-prototype declaration is used otherwise"
implies non-prototype forms are /obsolete/
3) "Those are still"
what is being referred to by the word "those" in that sentence, it's not
immediately clear to me. It could mean the non-prototype (as you've
assumed) but it might also mean the entire construct (using "#if
__STDC__").
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

In any case. It think the practical solution to this problem is to assume
you're working with modern code, and have a command line option to make it
assume K&R or old-style declarations.
In the old-style mode you would have to assume a function with an empty ()
could have any number of parameters and the best you can do is produce:
extern (C) <ret> <name>(/* fill in the blanks please*/);
R

In any case. It think the practical solution to this problem is to
assume you're working with modern code, and have a command line option
to make it assume K&R or old-style declarations.
In the old-style mode you would have to assume a function with an empty
() could have any number of parameters and the best you can do is produce:
extern (C) <ret> <name>(/* fill in the blanks please*/);
R

K&R C did not allow you to declare arguments at all, according to the wiki:
"Since K&R function declarations did not include any information about
function arguments, function parameter type checks were not performed,
although some compilers would issue a warning message if a local function
was called with the wrong number of arguments, or if multiple calls to an
external function used different numbers or types of arguments. Separate
tools such as Unix's lint utility were developed that (among other things)
could check for consistency of function use across multiple source files."
So all K&R function declarations were <name>() with no parameters.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

K&R C did not allow you to declare arguments at all, according
to the wiki:
"Since K&R function declarations did not include any
information about function arguments, function parameter type
checks were not performed, although some compilers would issue
a warning message if a local function was called with the wrong
number of arguments, or if multiple calls to an external
function used different numbers or types of arguments. Separate
tools such as Unix's lint utility were developed that (among
other things) could check for consistency of function use
across multiple source files."
So all K&R function declarations were <name>() with no
parameters.
R

K&R was more than that.
I guess most old timers here will agree with me that it was
not much more than glorified assembler in what concerns typing.
Much of the typing C has today, was actually brought in as part
of the
ANSI C standardization process.
--
Paulo

I guess most old timers here will agree with me that it was
not much more than glorified assembler in what concerns typing.

[...]
I guess you predate me. ;-) When I started learning C, it was already in
the ANSI syntax, though there were enough K&R style code floating around
that I've at least _seen_ K&R syntax. I'm guessing nowadays most people
don't even know what K&R syntax is.
T
--
If I were two-faced, would I be wearing this one? -- Abraham Lincoln

Definitely - I live in a very technologically-savvy dorm in college, and
when I encountered it for the first time nobody there had any idea what
it was.
...we did later find an old book in our library that had it, though.
-Matt
On 07/17/2012 02:39 PM, H. S. Teoh wrote:

I guess most old timers here will agree with me that it was
not much more than glorified assembler in what concerns typing.

[...]
I guess you predate me. ;-) When I started learning C, it was already in
the ANSI syntax, though there were enough K&R style code floating around
that I've at least _seen_ K&R syntax. I'm guessing nowadays most people
don't even know what K&R syntax is.
T

I guess you predate me. ;-) When I started learning C, it was
already in
the ANSI syntax, though there were enough K&R style code
floating around
that I've at least _seen_ K&R syntax. I'm guessing nowadays
most people
don't even know what K&R syntax is.
T

I started coding in C around 1993 with Turbo C 2.0, but was
exposed
to K&R code from different sources, specially when we needed to
make use of a System V based system for some OS classes.
Plus as a language geek, I had the pleasure to have access a lots
of books and papers since the late 70's in our university library.
That is a reason why I tend to have a good background in what
languages introduced which paradigm.
Maybe it is the historian in me. :)
--
Paulo

Clarification.
The /definition/ is as you have above, the /declaration/ is not. The
declaration is what goes in the header file, and in K&R (and ANSI C for
that matter) looks like:
int main();
parameters are not required for the declaration, only the definition.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Clarification.
The /definition/ is as you have above, the /declaration/ is not.
The declaration is what goes in the header file, and in K&R (and
ANSI C for that matter) looks like:
int main();
parameters are not required for the declaration, only the definition.

[...]
You are right.
And also, under K&R syntax, the 'int' can be omitted from before main. I
*think* the entire argc line can be omitted as well (undeclared
variables default to int, IIRC). 'Tis a strange world they used to live
in. :-)
T
--
Almost all proofs have bugs, but almost all theorems are true. -- Paul Pedersen

After a bit of googling and a test with my local MSVC9 I think old-style
variadics look like this:
#include <varargs.h>
#include <stdio.h>
void foo(va_alist)
va_dcl
{
va_list p;
va_start(p);
vprintf("%d %d %d\n", p);
}
void main()
{
foo(1, 2, 3);
}
(the above runs and outputs "1 2 3" on the console)
The same syntax is/was supported by GNU C, see:
http://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_chapter/libc_34.html#SEC676
I believe, if you see an "old-style" function declaration in a header
file like:
int foo ();
that you can't actually assume anything about it's parameters, it may
have /any/ number of parameters, and may or may not be variadic.

Clang seems to interpret it as a variadic function. Then if that is
correct or not I don't know.
--
/Jacob Carlborg

After a bit of googling and a test with my local MSVC9 I think old-style
variadics look like this:
#include <varargs.h>
#include <stdio.h>
void foo(va_alist)
va_dcl
{
va_list p;
va_start(p);
vprintf("%d %d %d\n", p);
}
void main()
{
foo(1, 2, 3);
}
(the above runs and outputs "1 2 3" on the console)
The same syntax is/was supported by GNU C, see:
http://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_chapter/libc_34.html#SEC676
I believe, if you see an "old-style" function declaration in a header
file like:
int foo ();
that you can't actually assume anything about it's parameters, it may
have /any/ number of parameters, and may or may not be variadic.

Clang seems to interpret it as a variadic function. Then if that is
correct or not I don't know.

All my googling for "old style" "variadic" etc returned the use of
va_alist and va_dcl so I can't see where/why Clang would do what it's
doing.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Is Clang open source, can we see the code for that function? Perhaps it's
a bug.. ANSI C may have made () without "void" obsolete, but no compiler
I've ever used has actually enforced that - or perhaps C++ made old-style
function definition/declarations obsolete and allowed () back again.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Is Clang open source, can we see the code for that function?
Perhaps it's a bug.. ANSI C may have made () without "void"
obsolete, but no compiler I've ever used has actually enforced
that - or perhaps C++ made old-style function
definition/declarations obsolete and allowed () back again.
R

In C++, a function with no parameters () is a synonym for (void).
--
Paulo

Is Clang open source, can we see the code for that function? Perhaps
it's a bug.. ANSI C may have made () without "void" obsolete, but no
compiler I've ever used has actually enforced that - or perhaps C++
made old-style function definition/declarations obsolete and allowed ()
back again.
R

In C++, a function with no parameters () is a synonym for (void).

That's what I've been assuming all this time, but I wasn't sure if I was
technically (according to the spec) right, so I didn't want to say.. :)
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Is Clang open source, can we see the code for that function? Perhaps
it's a bug.. ANSI C may have made () without "void" obsolete, but no
compiler I've ever used has actually enforced that - or perhaps C++ made
old-style function definition/declarations obsolete and allowed () back
again.

Is Clang open source, can we see the code for that function? Perhaps
it's a bug.. ANSI C may have made () without "void" obsolete, but no
compiler I've ever used has actually enforced that - or perhaps C++ made
old-style function definition/declarations obsolete and allowed () back
again.

It certainly seems intentional:
if (T->getAs<FunctionNoProtoType>())
return 1;
I wonder if it's a case of "we can't be certain, so lets assume it is" or
similar.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

To my understanding this is legal C :
int foo ();
It's a K&R-style variadic functions, while their use is discouraged,
they're still legal C.
If I, in D, declare a variadic function with C linkage that doesn't
take, at least, one regular parameter the compiler will complain.
extern (C) int foo (...);

To my understanding this is legal C :
int foo ();
It's a K&R-style variadic functions, while their use is discouraged, they're
still legal C.

Variadic functions, in order to work in C, need at least one parameter so that
varargs can work.
int foo();
declares a function with an unspecified parameter list, not a variadic one. It
is specified by a definition somewhere:
int foo(a,b)
int a;
int b;
{ ... }
somewhere.
If Dstep encounters the first declaration form, your options are to:
1. reject it (a perfectly reasonable approach)
2. treat it as:
int foo(void);
I suggest option 2, which is what C++ does.

Variadic functions, in order to work in C, need at least one parameter
so that varargs can work.
int foo();
declares a function with an unspecified parameter list, not a variadic
one. It is specified by a definition somewhere:
int foo(a,b)
int a;
int b;
{ ... }
somewhere.

I think I understand now.

If Dstep encounters the first declaration form, your options are to:
1. reject it (a perfectly reasonable approach)
2. treat it as:
int foo(void);
I suggest option 2, which is what C++ does.

Sounds reasonable. I will also provide a flag specifying how this should
be handled.
I actually found library that uses this style of declaration. It's used
in several places in libruby. For example:
void rb_define_virtual_variable(const
char*,VALUE(*)(ANYARGS),void(*)(ANYARGS));
ANYARGS is defined as:
#ifdef __cplusplus
#define ANYARGS ...
#else
#define ANYARGS
#endif
Does that mean that this C++ declaration:
void foo (...);
Is the same as this C declaration?
void foo ();
I'm wondering if the intention is to cast these function pointers to
their correct signature before calling them.
--
/Jacob Carlborg

That /requires/ an argument in the definition (AFAIKS), e.g.
execl(va_alist) <- va_alist is the argument name
va_dcl <- this is #define va_dcl va_list va_alist;
{
}
so, it's no different to any other old-style function, e.g.
foo(a)
int a;
{
}
Now.. the declaration may be a different story. These would be valid
declarations for the above:
void execl(); <- no parameters necessary in a declaration
void foo(); <- no parameters necessary in a declaration
It may be that GCC allows a declaration of:
void execl(...);
But I wasn't ware that was valid ANSI C, perhaps it's a GCC/clang
feature? Can anyone find docs on it?
If we /assume/ the above declaration always refers to a function with a
definition of:
execl(va_alist)
va_dcl
{
}
Then we assume the first argument is /always/ going to be a 32 bit value
(int, or 32 bit pointer) then we could speculatively convert to this D
extern (C) void execl(int first, ...);
But, if either assumption is false this would probably crash when used.
R
--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Note that it only works when compiling as C++. Perhaps it works like this:
If C++ is interpreting this:
void foo ();
As:
void foo (void);
Perhaps it will interpret:
void foo (...);
As C would interpret this:
void foo ();
--
/Jacob Carlborg