[comp.lang.c] Function prototypes versus open

gnu@hoptoad.uucp (John Gilmore) (05/29/87)

I think I've found a problem with the C standard. 	(greek chorus:
It says that functions with variable numbers of		 "so what else
arguments cannot be called unless a function		  is new?")
prototype is in scope.

The standard Unix open() call can take either two or three parameters,
but you are not required to #include any header files to use it.  Now,
if you are using the third operand, you will be including <sys/file.h>
to get O_CREAT, but if not, it seems to be perfectly acceptable to use
a small integer constant as the second operand.

It seems that if a compiler really believed the restriction in the
draft standard, it would not be able to correctly compile many Unix
programs which haven't heard of three operand open() or <sys/file.h>.
-- 
Copyright 1987 John Gilmore; you may redistribute only if your recipients may.
(This is an effort to bend Stargate to work with Usenet, not against it.)
{sun,ptsfa,lll-crg,ihnp4,ucbvax}!hoptoad!gnu	       gnu@ingres.berkeley.edu

jss@hector.UUCP (05/29/87)

In article <2210@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes:
>I think I've found a problem with the C standard. 	(greek chorus:
>It says that functions with variable numbers of	"so what else
>arguments cannot be called unless a function		  is new?")
>prototype is in scope.
>
>It seems that if a compiler really believed the restriction in the
>draft standard, it would not be able to correctly compile many Unix
>programs which haven't heard of three operand open() or <sys/file.h>.

It is not a restriction on the compiler (or compiler writer), it is a
requirement on the (application) programmer.   The intention, as I
read it, is to allow a C compilation system to use a different
calling sequence for functions with known numbers of arguments and
those (like printf) with variable numbers of arguments.  Whether this
is desirable will depend on pragmatic considerations, such as the
overheads associated with permitting variable numbers of arguments. I
doubt that any UNIX compiler will be written that varies its calling
sequences in this way, but I could be wrong.

At least the ANSI standard requires compilation systems to have some
way to deal with functions that are called with varying numbers of
arguments.  This was not required by K&R. (Although a C compilation
system that didn't support "printf" would probably not have many
users.)

Jerry Schwarz

brett@wjvax.UUCP (Brett Galloway) (05/31/87)

In article <2619@ulysses.homer.nj.att.com> jss@hector (Jerry Schwarz) writes:
>In article <2210@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes:
>>I think I've found a problem with the C standard. 	(greek chorus:
>>It says that functions with variable numbers of	"so what else
>>arguments cannot be called unless a function		  is new?")
>>prototype is in scope.
>It is not a restriction on the compiler (or compiler writer), it is a
>requirement on the (application) programmer.   The intention, as I
>read it, is to allow a C compilation system to use a different
>calling sequence for functions with known numbers of arguments and
>those (like printf) with variable numbers of arguments.

How about functions called with function pointers?  I am not that familiar
with the details of function prototyping in the standard, but will it be
possible to invoke functions with variable arguments with function pointers?
If not, that will be quite annoying (and will break an application that I
have written (under UNIX C).

How about functions called with function pointers?  It seems to me that the
only way separate calling sequences for non-variable argument and variable
argument functions could work would be to disallow function pointers to the
latter type.  In that case, this seems a serious deficiency of the requirement.
At minimum, it breaks an application that I have written under UNIX C (bsd),
which otherwise adheres to the varargs(3) specifications.
-- 
-------------
Brett Galloway
{pesnta,twg,ios,qubix,turtlevax,tymix,vecpyr,certes,isi}!wjvax!brett

guy%gorodish@Sun.COM (Guy Harris) (06/01/87)

> How about functions called with function pointers?  I am not that familiar
> with the details of function prototyping in the standard, but will it be
> possible to invoke functions with variable arguments with function pointers?

From the October 1, 1986 draft:

	3.5.3.3 Function declarators (including prototypes)

	...

	   Here are two more intricate examples:

		int (*apfi[3])(int *x, int *y);

	declares an array "apfi" of three pointers to functions returning
	"int".  Each of these functions has two parameters that are
	pointers to "int"....

which implies that the type of a function pointer, just like the type
of a function, includes the types of its arguments as well as the
type of its result.  As such, you can declare

	int (*pintvarargs)(int i, ...);

which is a pointer to a function whose first argument is an "int" and
which may have 0 or more optional arguments after that.  A function
pointed to by "pintvarargs" can be called with a variable-length list
of arguments, as long as that list has at least one argument and the
first argument is of a type that can be coerced to "int".  The
declaration

	int (*pintarg)(int i);

declares a pointer to a function whose *only* argument is an "int".
A function pointed to by "pintarg" may only be called with one
argument, which must be of a type that can be coerced to "int".

The deprecated old-style declaration

	int (*pwhoknows)();

is, I infer, equivalent to

	int (*pwhoknows)(...);

which declares a pointer to a function which may have 0 or more
optional arguments.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

gwyn@brl-smoke.ARPA (Doug Gwyn ) (06/02/87)

In article <2210@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes:
>The standard Unix open() call can take either two or three parameters,
>but you are not required to #include any header files to use it.  Now,
>if you are using the third operand, you will be including <sys/file.h>
>to get O_CREAT, but if not, it seems to be perfectly acceptable to use
>a small integer constant as the second operand.

I don't understand the point you're trying to make.  open() is not an
ANSI C function at all.  POSIX requires that you #include three headers
(which does seem like two too many; <stat.h> should not be required) for
portable invocation of open().  (<sys/file.h> is NOT one of these.)
Non-POSIX UNIX-like systems are free to do whatever they please.

The only way for an application programmer to correctly, portably code
his own IMPLEMENTATION of open() in ANSI C would be to use the <stdarg.h>
mechanism; however, implementors of ANSI C can implement it any way they
choose -- for most existing systems the implementation would be unchanged.
In particular, for most existing systems, code that invokes open() without
including <fcntl.h> would continue to work, but portability cannot be
assured (which is simply a fact of life).

bright@dataio.UUCP (06/02/87)

In article <931@wjvax.wjvax.UUCP> brett@wjvax.UUCP (Brett Galloway) writes:
-How about functions called with function pointers?  I am not that familiar
-with the details of function prototyping in the standard, but will it be
-possible to invoke functions with variable arguments with function pointers?
-If not, that will be quite annoying (and will break an application that I
-have written (under UNIX C).

The secret is in the declaration:

int (*fp1)(int);	/* pointer to function with fixed # of arguments */

int (*fp1)(char *,...);	/* pointer to function with variable arguments that
			   returns an int	*/

int (*fp)();		/* obsolete	*/

gnu@hoptoad.UUCP (06/04/87)

Several people didn't understand what I was saying, so let me try again.
This is strictly an ANSI C issue; there is no interaction with POSIX.

Let's suppose that someone writes a compiler such that if a function is
not declared with (...) then it cannot be called with a variable number
of arguments.  In other words, if the function is not declared at all,
it can't take a varying number of arguments.  If the function is
declared the old way, e.g. "int open();" it also will not work.  You
have to declare it as e.g. "int open(...);" for this compiler to pass
the arguments properly.  This is a valid way to write a compiler,
according to ANSI C.

This might occur in a machine with overlapping register windows, such
as a Pyramid or various RISC machines.  Typically, arguments would be
passed in registers, but for calls to vararg functions, they would get
pushed on a stack, or an argument count passed in secretly, or some
such.

If such a compiler was ever used to compile a Unix program, trouble
would arise.  Unix programs traditionally don't #include anything
when they are going to use the open() call, so there is no include file
that could declare "int open(...);" for them.  If the user calls open()
with two arguments, without having declared the "open" function in any
way, the compiler must assume that it is a two-argument function,
since all variable-argument functions MUST be declared with "...".
Sez so right in the standard.

Remember, the compiler has to know which kind of function it is --
fixed-args or variable-args, since it implements parameter passing
differently for the two kinds.  When it passes these two arguments to
the open() library routine, the arguments will be in the wrong place,
because the open() library routine will have to be written to assume
variable arguments, since indeed open() can be called with a variable
number of arguments.  In fact, another module of the same source
program could call open() with three arguments.

This means that such a compiler could not be compatibly used on a Unix
system, even though it meets all the requirements of the ANSI C standard,
and indeed BECAUSE it ENFORCES some of the requirements of the ANSI C
standard.

I can see a few possible ways around this but they all involve making
the varargs interface pass arguments compatibly with the non-varargs
interface, or massive kludgery (recognize the string "open"; bring in
different libraries at link time; append the argument count to the 
names of fixed-arg functions; and similar rot).  If implementations
are going to be forced to pass arguments compatibly, the standard might
as well not restrict users to declaring all their varargs functions, and
should not encourage implementors to require it.

How do the Pyramid and MIPS compilers handle open() calls now?
-- 
Copyright 1987 John Gilmore; you may redistribute only if your recipients may.
(This is an effort to bend Stargate to work with Usenet, not against it.)
{sun,ptsfa,lll-crg,ihnp4,ucbvax}!hoptoad!gnu	       gnu@ingres.berkeley.edu

gwyn@brl-smoke.UUCP (06/04/87)

In article <2242@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes:
>This means that such a compiler could not be compatibly used on a Unix
>system, even though it meets all the requirements of the ANSI C standard,

I think a more precise statement would be:
	Much existing code from UNIX systems would have to be changed
	(at least to the extent of #including a header that declares
	the open() function properly) in order to successfully work
	under a C implementation that (for perhaps good and sufficient
	reason) uses a different function linkage for a fixed versus
	a variable number of arguments.

It is important to note that MOST UNIX C implementations will not have
this problem.

This is just a fact of life for some machine architectures.

It may also be worth noting that much UNIX software is atrociously
written and wouldn't port well without considerable effort anyway.
Several people have been working on improving this situation; they
ought to insert #include <fcntl.h> as required while they're at it.

michael@stb.UUCP (06/05/87)

In article <20119@sun.uucp> guy%gorodish@Sun.COM (Guy Harris) writes:
>> How about functions called with function pointers?  I am not that familiar
>> with the details of function prototyping in the standard, but will it be
>> possible to invoke functions with variable arguments with function pointers?
>
>From the October 1, 1986 draft:
>
>	3.5.3.3 Function declarators (including prototypes)
>
>	...
>
>	   Here are two more intricate examples:
>
>		int (*apfi[3])(int *x, int *y);
>
>	declares an array "apfi" of three pointers to functions returning
>	"int".  Each of these functions has two parameters that are
>	pointers to "int"....
>
>which implies that the type of a function pointer, just like the type
>of a function, includes the types of its arguments as well as the
>type of its result.  As such, you can declare
>
>	int (*pintvarargs)(int i, ...);
>
>which is a pointer to a function whose first argument is an "int" and
>which may have 0 or more optional arguments after that.  A function
>pointed to by "pintvarargs" can be called with a variable-length list
>of arguments, as long as that list has at least one argument and the
>first argument is of a type that can be coerced to "int".  The
>declaration
>
>	int (*pintarg)(int i);
>
>declares a pointer to a function whose *only* argument is an "int".
>A function pointed to by "pintarg" may only be called with one
>argument, which must be of a type that can be coerced to "int".
>
>The deprecated old-style declaration
>
>	int (*pwhoknows)();
>
>is, I infer, equivalent to
>
>	int (*pwhoknows)(...);
>
>which declares a pointer to a function which may have 0 or more
>optional arguments.
>	Guy Harris
>	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
>	guy@sun.com


Wait a sec. Would you please explain how the following can be done:

I have a dispatcher routine, that takes a pointer to a function, some args,
does some munching on the args, and calls the pointed to routine with 3,
4, or 5 args. Each routine called is expecting a known, fixed # of args,
yet the pointer used is with variable args. So, according to the above,
I have to declare the function pointer as variable args, which activates
one calling convention, yet the routines are fixed args, and expecting
another calling convention.
-- 
: Michael Gersten		seismo!scgvaxd!stb!michael
: The above is the result of being educated at a school that discriminates
: against roosters.

karl@haddock.UUCP (Karl Heuer) (06/06/87)

In article <2242@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes:
>Let's suppose that someone writes a compiler such that [varargs functions
>must be declared with ellipses].  If such a compiler was ever used to compile
>a Unix program, [with no #include and with open() not explicitly declared,]
>trouble would arise.

Well, an ANSI compiler is not even obliged to provide an open() function, so
there's no problem.  :-)

Instead of answering your question, I'd just like to mention that I think the
addition of a third argument to open() was a really dumb move by AT&T.  This
causes open() to be weakly variadic without need, and creates nonportable code
since neither BSD nor Research UNIX has implemented this, to the best of my
knowledge.

Now, I'll grant that it's useful to have some of the features of creat() and
open() available in one system call.  I only disagree with the implementation.
I would've made a new system call, cropen(path, mode|flags); powerful enough
to supersede both creat() and open() (which could then be moved into section 3
and/or marked as deprecated).

Other systems could have a SysV emulation library that defines cropen(3) in
terms of open(2) and creat(2), where possible.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
(Note "mode|flags" implies the Read/Write/RdWr option would be higher bits.
Alternately, they could be separate arguments.)

mash@mips.UUCP (06/06/87)

In article <2242@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes:
.....
>This might occur in a machine with overlapping register windows, such
>as a Pyramid or various RISC machines.  Typically, arguments would be
>passed in registers, but for calls to vararg functions, they would get
>pushed on a stack, or an argument count passed in secretly, or some
>such...

>I can see a few possible ways around this but they all involve making
>the varargs interface pass arguments compatibly with the non-varargs....

>How do the Pyramid and MIPS compilers handle open() calls now?

MIPS does the following:
callee:
	the code is the same, regardless of whether the callee is
	vararg'd or not:
	0-4 arguments are placed in registers
	args 5-? are placed onto the stack [not pushed, the stack doesn't
		move until the actual function call, if at all]
	space is left in the stack for args 1-4.
	(non-long args complexify all this, but that's the idea)

caller:
	usually gets the args from the registers, or the stack, as needed
	saves any of args 1-4 into space reserved on the stack,
	if compiler "suspects" & operator being applied to the arguments,
	even indirectly, as in  x = &arg2;  z = *--x;
	varargs declaration makes sure that compiler suspects the right things!

Thus, normal access to the variables is thru the registers,
and the first 4 args are only saved into memory if they need to be,
or if the compiler has reason to suspect they need to be.
Statistically, the arguments almost NEVER get saved unless they really need to.
-- 
-john mashey	DISCLAIMER: <generic disclaimer, I speak for me only, etc>
UUCP: 	{decvax,ucbvax,ihnp4}!decwrl!mips!mash, DDD:  	408-720-1700, x253
USPS: 	MIPS Computer Systems, 930 E. Arques, Sunnyvale, CA 94086

guy@gorodish.UUCP (06/06/87)

> Wait a sec. Would you please explain how the following can be done:
> 
> I have a dispatcher routine, that takes a pointer to a function, some args,
> does some munching on the args, and calls the pointed to routine with 3,
> 4, or 5 args. Each routine called is expecting a known, fixed # of args,
> yet the pointer used is with variable args. So, according to the above,
> I have to declare the function pointer as variable args, which activates
> one calling convention, yet the routines are fixed args, and expecting
> another calling convention.

Trivial.  Have the argument to the dispatcher, instead of being a
pointer to a function with a variable number of arguments, be a union
of a pointer to a function with three arguments, a pointer to a
function with four arguments, and a pointer to a function with five
arguments.  Since you have three separate calls, use the three
different members of the union in the three calls.  End of problem.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

guy@gorodish.UUCP (06/06/87)

> Instead of answering your question, I'd just like to mention that I think the
> addition of a third argument to open() was a really dumb move by AT&T.  This
> causes open() to be weakly variadic without need, and creates nonportable
> code since neither BSD nor Research UNIX has implemented this, to the best
> of my knowledge.

Nope.  4.2BSD implemented the three-argument "open".
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

karl@haddock.UUCP (06/09/87)

In article <20539@sun.uucp> guy%gorodish@Sun.COM (Guy Harris) writes:
>>... neither BSD Research has implemented this, to the best of my knowledge.
>Nope.  4.2BSD implemented the three-argument "open".

Damn.  They didn't have enough bad ideas of their own, they had to copy
AT&T's? :-)  (Sorry about the misinformation; I should have RTFM.)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

dhesi@bsu-cs.UUCP (06/10/87)

Re:  Much discussion about how open() can break C code on an ANSI-
compliant system if a function prototype is omitted.

The original discussion arose because somebody said that AT&T's open()
system call could take either 2 or 3 arguments, causing possible
problems on systems that implemented function calls with a known
argument count differently from function calls with a variable argument
count.  Somebody suggested that 4.2BSD code would break too.

It should be noted that the 4.2BSD manual page for open(2), dated 2
July 1983, and the 4.3BSD manual page for open(2), dated May 14 1986,
both clearly show that open() has three, and exactly three,
parameters.  The value of the third parameter is simply ignored in most
cases, which is quite different from saying that the presence of the
parameter is optional.

AT&T's manual page for open(2), as supplied with Microport System V/AT, 
does show the third argument as being optional.
-- 
Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

guy@gorodish.UUCP (06/10/87)

> It should be noted that the 4.2BSD manual page for open(2), dated 2
> July 1983, and the 4.3BSD manual page for open(2), dated May 14 1986,
> both clearly show that open() has three, and exactly three,
> parameters.  The value of the third parameter is simply ignored in most
> cases, which is quite different from saying that the presence of the
> parameter is optional.

It should also be noted that plenty of 4.[23]BSD code uses "open" as
if the third argument were options, so the fact that the manual does
not explicitly indicate the third parameter as optional probably does
not mean that one is obliged to provide it.  Plenty of 4.[23]BSD code
will break on systems that don't support "open" with the third
argument optional.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

stevesu@copper.UUCP (06/14/87)

In article <514@haddock.UUCP>, karl@haddock.UUCP (Karl Heuer) writes:
> I think the addition of a third argument to open() was a really dumb
> move by AT&T.  This causes open() to be weakly variadic without need,
> and creates nonportable code since neither BSD nor Research UNIX has
> implemented this, to the best of my knowledge.

I really like the new, optional third argument to open().  It is
one of the few cases I know of in the Unix world in which a truly
new feature was added to an existing call in a backwards-compatible
way.  I first came across it in 4.xBSD (which does have it, by
the way) and was astonished that Berkeley had done something that
clever, since backwards-incompatibility is usually their battle
cry.  (I now know that it was implemented first at AT&T, and
emulated in BSD.)

Of course, code using it is less portable, but then so is code
using any new feature.  I only use a three-argument open when I
need its extra semantics, otherwise I use old-fashioned creat()
and/or two-argument open().

I will grant that functions with variable numbers of arguments
are a problem.  However, backwards incompatibility is a bigger
problem.  I insist that extensions be made in a backwards
compatible way, to avoid breaking existing code.  If a case can
be made for adding functionality to an existing call (rather than
adding a new call) and if the new functionality requires new
arguments, then varargs is the only way to go.  The optional
third argument to open is specified correctly, in that its
presence is signaled by a previous argument (the O_CREAT bit in
the second argument).  There is no need for the callee to
discover the number of arguments in the stack frame, which can be
done reliably on the VAX but on few other architectures.

Implementors of architectures with register or other non-stack
argument passing mechanisms will simply have to solve the varargs
problem (as long as the language provides sufficient information,
as a function prototype does.)  You need varargs for printf(),
which I think everybody agrees is an appropriate and useful
application of varargs, so why not use it for open() as well,
even if the case for the optional argument is a little less
strong?

                                           Steve Summit
                                           stevesu@copper.tek.com

mouse@mcgill-vision.UUCP (der Mouse) (06/23/87)

In article <20538@sun.uucp>, guy%gorodish@Sun.COM (Guy Harris) writes:
>> Wait a sec. Would you please explain how the following can be done:

>> [dispatcher which calls a fxn passed by a pointer as an argument,
>>  with 3, 4, or 5 args.  Each fxn expects known arg pattern.]

> Trivial.  Have the argument to the dispatcher, instead of being a
> pointer to a function with a variable number of arguments, be a union
> of a pointer to a function with three arguments, a pointer to a
> function with four arguments, and a pointer to a function with five 
> arguments.

Except that this makes it effectively impossible to use the dispatcher.
Consider the call to the dispatcher that would have been written (under
the old K&R paradigm) as

int fxn();
...
dispatcher(....,fxn);

How does on write the corresponding thing?  (Please forgive any syntax
errors, I don't have a copy of the draft - and won't until I can get
one machine-readable!)

int fxn(int x; int y; double z);
...
dispatcher(....,fxn);

Oops.  The argument to the dispatcher is not of type
int (*)(int,int,double)
but of type
union{int(*)(int,int,double);int(*)(int,int,double,double);}
instead.  Ugly.

					der Mouse

				(mouse@mcgill-vision.uucp)

mouse@mcgill-vision.UUCP (der Mouse) (06/23/87)

In article <1125@copper.TEK.COM>, stevesu@copper.TEK.COM (Steve Summit) writes:
> In article <514@haddock.UUCP>, karl@haddock.UUCP (Karl Heuer) writes:
>> I think the addition of a third argument to open() was a really dumb
>> move by AT&T.  [...] since neither BSD nor Research UNIX has
>> implemented this, to the best of my knowledge.

As both stevesu and someone else (Chris Torek I think?) point out, BSD
4.2 and 4.3 both have three-argument open().

> I really like the new, optional third argument to open().  It is one
> of the few cases I know of in the Unix world in which a truly new
> feature was added to an existing call in a backwards-compatible way.
> I first came across it in 4.xBSD (which does have it, by the way) and
> was astonished that Berkeley had done something that clever, since
> backwards-incompatibility is usually their battle cry.  (I now know
> that it was implemented first at AT&T, and emulated in BSD.)

Well, I don't know about the AT&T implementation, but the BSD open()
most emphatically does not have an optional third argument!  It has
three, exactly three, arguments.  It just happens that on most BSD
machines, you can get away with passing fewer arguments that the
routine wants provided it ignores the extra ones.  This means that you
can get away with pretending it's optional, but that doesn't make it
optional at all.  Wait for the day you have to port to a machine whose
compiler treats optional arguments differently - it'll be the day this
burns you.

					der Mouse

				(mouse@mcgill-vision.uucp)

guy%gorodish@Sun.COM (Guy Harris) (06/26/87)

> Well, I don't know about the AT&T implementation, but the BSD open()
> most emphatically does not have an optional third argument!  It has
> three, exactly three, arguments.  It just happens that on most BSD
> machines, you can get away with passing fewer arguments that the
> routine wants provided it ignores the extra ones.

The BSD implementation very closely resembles the System III/System V
implementation, so the argument is as optional in one as it is in the
other.

> This means that you can get away with pretending it's optional, but that
> doesn't make it optional at all.  Wait for the day you have to port to a
> machine whose compiler treats optional arguments differently - it'll be
> the day this burns you.

Actually, it'll be the day it burns the person who ports UNIX to that
machine; "open()" used to take two arguments, and there are plenty of
programs left around that weren't changed when "open" was changed.
This is, I presume, why Karl Heuer complained about the 3-argument
"open"; introducing it technically rendered *all* programs that used
a two-argument "open" invalid!
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

guy%gorodish@Sun.COM (Guy Harris) (06/26/87)

> Except that this makes it effectively impossible to use the dispatcher.
> Consider the call to the dispatcher that would have been written (under
> the old K&R paradigm) as
> 
> int fxn();
> ...
> dispatcher(....,fxn);
> 
> How does on write the corresponding thing?

/*
 * This would presumably appear in an #include file.
 */
typedef union {
	int	(*three_argument)(int, int, double);
	int	(*four_argument)(int, int, double, double);
	int	(*five_argument)(int, int, double, double, double);
} function_pointer_passed_to_dispatcher;

int fxn(int x; int y; double z);

extern return_type_of_dispatcher dispatcher(various_argument_types,
    function_pointer_passed_to_dispatcher funcp);

function_pointer_passed_to_dispatcher temporary;

temporary.three_argument = fxn;

dispatcher(..., temporary);

Yes, this may be ugly, but it's type-correct, and may even be necessary
in some implementations.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

pdg@ihdev.ATT.COM (Joe Isuzu) (06/29/87)

In article <821@mcgill-vision.UUCP> mouse@mcgill-vision.UUCP (der Mouse) writes:
>Well, I don't know about the AT&T implementation, but the BSD open()
>most emphatically does not have an optional third argument!  It has
>three, exactly three, arguments. 

I'll just add this for completeness.

According to SVID, the open call is:

int open(path,oflag,mode)

Three arguments, none optional.
Now everybody go back and change all of the code you have
ever written. :-}

-- 

Paul Guthrie				"Another day, another Jaguar"
ihnp4!ihdev!pdg				    -- Pat Sajak

stuart@bms-at.UUCP (Stuart D. Gathman) (06/30/87)

In article <817@mcgill-vision.UUCP>, mouse@mcgill-vision.UUCP (der Mouse) writes:
> In article <20538@sun.uucp>, guy%gorodish@Sun.COM (Guy Harris) writes:

> > Trivial.  Have the argument to the dispatcher, instead of being a
> > pointer to a function with a variable number of arguments, be a union
		. . .

> Consider the call to the dispatcher that would have been written (under
> the old K&R paradigm) as

> int fxn();
> ...
> dispatcher(....,fxn);

> How does on write the corresponding thing?  (Please forgive any syntax

> int fxn(int x; int y; double z);
> ...
> dispatcher(....,fxn);

*** Solution A

typedef union {
  int (*type3)(int,int,double);
  int (*type4)(int,int,double,double);
} disp_fxn;
...
dispatcher(....,(disp_fxn)fxn);

I know, this is not allowed, but I wish it was!  Perhaps it should be:

dispatcher(....,((disp_fxn)fxn).type3);
	or
dispatcher(....,(disp_fxn.type3)fxn);

Would this be portable?  Uglier, but I know it would work is:

*** Solution B

{ disp_fxn temp; dispatcher(....,temp.type3=fxn); }

We use indirect functions all over the  place.  We  not  been  using
function  prototypes since some of the compilers we support don't
have them.  As a result, there are many bugs resulting  from  the
wrong  parm  type  being  passed  to  a function called through a
table.  It would be nice to have a way to lint indirect  function
calls without the mess of 'B'.

Worse yet, are dynamic indirect function calls.  This is where the
function pointers are themselves stored in a table!  This is impossible
to lint, since lint cannot guarrantee what kind of pointer will actually
reside in a function pointer variable at run time.  At this point, I
start leaning towards O-O!
-- 
Stuart D. Gathman	<stuart@bms-at.uucp>
			<..!seismo!dgis!bms-at!stuart>

karl@haddock.UUCP (Karl Heuer) (06/30/87)

In article <22196@sun.uucp> guy%gorodish@Sun.COM (Guy Harris) writes:
["temporary" is a union of different flavors of functions]
>temporary.three_argument = fxn;
>dispatcher(..., temporary);

This makes a good case for the cast-into-union feature on my wishlist.

>Yes, this may be ugly, but it's type-correct, and may even be necessary
>in some implementations.

Yeah.  Of course people won't use it if they "know" it's overkill on their
vax. :-(  Fortunately, the situation in question doesn't come up very often.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

shopiro@alice.UUCP (07/01/87)

I'd just like to mention that in C++, the prototype for open() would be

	int	open(const char* path, int flags, int mode=0);

This declares a function with three arguments, that can optionally
be called with only two arguments.  In that case, the compiler adds
the third argument using the default value given in the declaration.
Thus old code coexists with new, and type checking is not defeated.
-- 
		Jonathan Shopiro
		AT&T Bell Laboratories, Murray Hill, NJ  07974
		research!shopiro   (201) 582-4179

mikes@apple.UUCP (Mike Shannon) (07/03/87)

In article <7046@alice.UUCP> shopiro@alice.UUCP writes:
>I'd just like to mention that in C++, the prototype for open() would be
>
>	int	open(const char* path, int flags, int mode=0);
			^
			|
	The above "const" brings up an interesting question:
	if I do:

{
char *pathname = "some.file";
int i;
	i = open(pathname, O_RDONLY) .....
}

	is the above declaration really OK? since the first param isn't CONST?
-- 
			Michael Shannon {apple!mikes}

am@cl.cam.ac.uk (Alan Mycroft) (07/03/87)

In article <817@mcgill-vision.UUCP> mouse@mcgill-vision.UUCP (der Mouse) writes:
(Discussion re despatcher and use of unions of function types.)
>Except that this makes it effectively impossible to use the dispatcher.
extern dispatcher(...., union funtypes fnarg);
>int fxn(int x; int y; double z);
>...
>dispatcher(....,fxn);
>
>Oops.  The argument to the dispatcher is not of type
>int (*)(int,int,double)
>but of type
>union{int(*)(int,int,double);int(*)(int,int,double,double);}
>instead.  Ugly.
Ugly indeed.  The problem is that C does not include all the operations
one would expect on union types.  If you ask (say) a category theorist
(and can understand the answer) he will mutter about co-products and
say something more precise than:
   if A and B are types(objects) then so is (sometimes) union(A,B).
   This comes along with some functions (partial or sometimes undefined in the
   case of the out functions).
     in1: A -> union(A,B)         in2: B -> union(A,B)
     out1: union(A,B) -> A        out2: union(A,B) -> B.
Now we see the problem: C in its hackerish wisdom only provides the 'out'
functions via the (e).member construct.
The other functions one has to hack using temporaries (see below).
Now one should sympathise with those who wish casts of objects
into a union type containing that object -- they only want a
very natural concept.

In C we could say
union intorptr { int a; char *b;};
extern f(union intorptr);
g() { f((union intorptr)3);}       NOT IN ANSI.

However, this omission leads to the following as the 'best' allowed
form for g:
g() { union intorptr temp; temp.a = 3; f(temp); }
I fail to see that this is more readable (note we have forged an 'in' function
using an 'out' function and assignment).
Lest I get flamed, I will not point out the even nastier version using
casts between differing pointers which does not work for functions due
to the ANSI code/data pointer disinction.

Motto: once in a while listen to these crazy pure mathematicians - they
can't spent all day contemplating their navels and fail to notice
anything significant.

While we're on about this, similar reasoning may be used to contemplate
why C makes it so hard to extract more than 1 result from a struct-returning
function like 'ldiv'.

bts@sas.UUCP (Brian T. Schellenberger) (07/05/87)

In article <817@mcgill-vision.UUCP>, mouse@mcgill-vision.UUCP (der Mouse) writes:
> In article <20538@sun.uucp>, guy%gorodish@Sun.COM (Guy Harris) writes:
> >> Wait a sec. Would you please explain how the following can be done:
> >> [dispatcher which calls a fxn passed by a pointer as an argument,
> >>  with 3, 4, or 5 args.  Each fxn expects known arg pattern.]
> 
> <then he suggests that the following problem occurs:
> 
> int fxn(int x; int y; double z);
> ...
> dispatcher(....,fxn);
> 
> Oops.  The argument to the dispatcher is not of type
> int (*)(int,int,double)
> but of type
> union{int(*)(int,int,double);int(*)(int,int,double,double);}
> instead.  Ugly.

Why not just use typedefs and casts to good effect; eg,

typedef union{ int (*)(int,int,double); int (*)(int,int,double,double) }
	dispatch_fptr;

	dispatcher( ...., (dispatch_fptr)fxn );

A thousand forgivenesses if I am mistaken and ANSI in fact prohibits the
obvious (I should wait 'til Monday to post this, so I can check my copy
of the standard to be sure, but . . .).  A cast seems the obvious solution.
I *would* suggest that if ANSI disallows this, it should be fixed.

							--Brian.
							...!rti!mcnc!sas!bts