[comp.lang.c++] default arg handling

langley@bigbird.rtp.dg.com (Mark L Langley) (03/10/90)

Jonathon Shapiro (shap@delrey.sgi.com) bristles
 >
 >>langley@bigbird.rtp.dg.com (Mark L Langley) suggests...
 >>   
 >>      void bar(int x=i+j) {
 >>      printf("%d\n", x);
 >>      }
 >>   
 >>      main() {
 >>      int i=20, j=30;
 >>       bar();
 >>       }
 >>   ...I intend to propose this as a change in ANSI C++, and have already
 >>   proposed it to AT&T in private correspondence...
 >
 >This is a great example of why language design should be left to
 >people who know what they're doing.

I'll just let that part ride.  

The problem however is that the dangerous aspects of this are legal now, 
depending on how declarations and definitions are broken across files.

 >
 >C already has a facility for doing this.  It is called a macro.

One problem in general with macros is that they aren't C and hence
obey their own, more casual, scoping rules.  

But could you say a little more about how using macros helps default
argument handling?  Isn't one win of default arguments to avoid using 
a macro?

 >
 >The problem with what you suggest is that the function definition site
 >needs to know lots and lots of things about ALL function use sites.
 >It also would be altogether impossible to implement this across files
 >today.
 >
 >Functions should be isolated - they should not care who calls them,
 >except to the extent that they specify requirements in the form of
 >arguments.  The above proposal violates this any number of ways.
 >

How does it violate modularity?  And I don't understand what you are
getting at with ``sites.''

Now, as I clarified (I hope) in a later posting that you may not have 
seen, I am just suggesting that the declaration of a default argument
to a function call, like the declaration of any other scoped object, 
be shadowed by declarations at a more local scope.

My suggestion would not involve any more work than is done now.  For 
example consider what happens now if you have two declarations of a 
function, in different parts of a system that have mutually exclusive 
declarations, but call the same function at run time.

I assume of course that you realize that an application (now) can be 
broken across .c and .h files such that an intended default can be 
canceled out by an errant declaration.

   // foo.c - definition of foo
   extern "C" { extern printf(...); };
   void foo(int x=1024) { printf("%d\n", x); }

   // main.c - usage of foo
   void foo(int x=100);
   main() { foo(); }

Namely, since the actual default argument is supplied staticaly at call 
time the language processor detects no conflict.

I just think redeclaring the default argument is legitimate; and should 
not be canceled by the occurrence of two declarations of foo being 
processed in the same compilation.  Admittedly the above is the down
side of what I am proposing.  But things are no safer now, and there
are useful aspects of redefining a default on purpose.

 >Try proposing it to the BASIC committee.  They love this sort of
 >stuff.
 >
 >Jonathan Shapiro
 >

Mark Langley
langley@dg-rtp.dg.com

shap@delrey.sgi.com (Jonathan Shapiro) (03/11/90)

Okay, Mark. I'll tey for a serious answer this time. I think your
proposal, as I understand it, is a bad idea.

In article <1116@xyzzy.UUCP> langley@bigbird.rtp.dg.com (Mark L Langley) writes:
>Jonathon Shapiro (shap@delrey.sgi.com) bristles

I like the way you put that - I don't recall seeing the word "bristle"
used in this way on the net before.  It fits.  On the other hand, I
would rather I hadn't "bristled in the first place".

> >>langley@bigbird.rtp.dg.com (Mark L Langley) suggests...
> >>   
> >>      void bar(int x=i+j) {
> >>      printf("%d\n", x);
> >>      }
> >>   
> >>      main() {
> >>      int i=20, j=30;
> >>       bar();
> >>       }
> >>   ...I intend to propose this as a change in ANSI C++, and have already
> >>   proposed it to AT&T in private correspondence...
> >
> >The problem with what you suggest is that the function definition site
> >needs to know lots and lots of things about ALL function use sites.
> >It also would be altogether impossible to implement this across files
> >today.
> >
> >Functions should be isolated - they should not care who calls them,
> >except to the extent that they specify requirements in the form of
> >arguments.  The above proposal violates this any number of ways.
> >
>
>Now, as I clarified (I hope) in a later posting that you may not have 
>seen, I am just suggesting that the declaration of a default argument
>to a function call, like the declaration of any other scoped object, 
>be shadowed by declarations at a more local scope.

Well, yes and no.  If we take this proposal on face value, it would
seem to imply that one wishes to be able to say:

	extern void bar(int x=i+j);

After all, the mechanism should work across files.

The problem with your proposed interpretation is that it is contrary
to other interpretations in the language, and that the types of the
'i' and 'j' cannot be known at the time the declaration is seen.  This
is what another poster probably meant when they complained that you
wanted dynamic scoping, which is a LISP feature.  You could think of
dynamic scoping, for this discussion, as being call-by-name.

In some cases, in the absence of the type, the expression is very
difficult to parse, which imposes a practical consideration.

Most important, however, is the fact that this runs contrary to the C
convention that names must be defined (at least implicitly - even in
older C's) before they are used.  This declaration somehow promises
that there will be an i and a j at the call site.  The question then
arises whether "there must be and i and a j at the call site" should
be considered part of the "type" of the function.  That is, does the
declaration as written mean that we shouldn't allow you to call 'bar'
unless the call site has an i and a j?  Surely the implementation of
bar doesn't care!

Also, What should your proposal do when it sees:

	int i = 0;
	int j = 1;
	extern void bar(int x = i + j);

	main()
	{
		int j = 2;
		
		bar();
	}

Should we understand the j to mean the j that was in scope at the
declaration (in a lexically scoped language, this would be the
"expected" interpretation), or the 'j' in scope at the call site?
[Since you seemed to express confusion about 'site', I mean the place
where the function is called].  I take it that you wish the second.  I
suggest that the this interpretation opens a rats nest that even the
LISP community is abandoning in newer dialects (consider Scheme, which
is strictly lexically scoped).  The former is interesting, and let me
suggest that the following example might want to be considered
analogous:

	int a = 5;
	int b = a;

Note that in C/C++ this is not legal, even though the compiler can
compile-time evaluate a according to the following rationale:

	1. All declarations are "run" before any code
	2. Therefore, 'a's value is known.
	3. Therefore b = 5.

But then what about

	extern int a;
	int b = a;

Even a smart linker would have a hard time ensuring that there were no
circular dependencies.  Why should the *file* impact the syntactic
correctness?

>
>I assume of course that you realize that an application (now) can be 
>broken across .c and .h files such that an intended default can be 
>canceled out by an errant declaration.
>
>   // foo.c - definition of foo
>   extern "C" { extern printf(...); };
>   void foo(int x=1024) { printf("%d\n", x); }
>
>   // main.c - usage of foo
>   void foo(int x=100);
>   main() { foo(); }
>

This is a bug, not a feature - it means that the name mangling in the
compiler isn't strong enough.  It has always been my opinion that the
determination of what should be the default value logically belongs
with the definition of the function.  Placing it in the declaration is
an unfortunate artifact imposed on us by header files.

In fact, at one point C++ allowed the declaration and the definition
both to specify a default value, but it never required them to agree.
I believe that it should be legal to specify the default in both
places, so long as they agree.

Does this make my concerns more clear?

Jonathan Shapiro
Silicon Graphics

patch@hpldola.HP.COM (Pat Chkoreff) (03/13/90)

/ hpldola:comp.lang.c++ / shap@delrey.sgi.com (Jonathan Shapiro) /  4:13 pm  Mar 10, 1990 /

> Also, What should your proposal do when it sees:
> 
> 	int i = 0;
> 	int j = 1;
> 	extern void bar(int x = i + j);
> 
> 	main()
> 	{
> 		int j = 2;
> 		
> 		bar();
> 	}

As Mark Langley has pointed out, the supplying of default arguments should
be thought of as the responsibility of the caller, not part of the function
definition.  So perhaps the example above should be equivalent to the
following, which is valid and comprehensible (albeit reprehensible):

	int i = 0;
	int j = 1;
	extern void bar(int);

	void bar()
	{
		return bar(i+j);
	}

	main()
	{
		int j = 2;
		bar();
	}

Of course, the definition of bar() can be inline'd and/or static'ed, and the
"return" is not strictly needed in the body, but that's irrelevant.

Perhaps if the last formal argument of a function has a default value, then
the compiler should always generate a dummy (inline) function without that
argument which simply calls the other function with the appropriate default
value.  Then the meaning of C++ would be as well-founded as that of C++
without default arguments.

BTW, this is the standard way of modeling default arguments in languages
which do not support them but do support overloading (esp. Prolog).  My
apologies if this proposal has already been discussed.


Patrick Chkoreff   719-590-5983
{hpfcla,hplabs}!hpldola!patch

shap@delrey.sgi.com (Jonathan Shapiro) (03/15/90)

In article <11430020@hpldola.HP.COM> patch@hpldola.HP.COM (Pat Chkoreff) writes:
>/ hpldola:comp.lang.c++ / shap@delrey.sgi.com (Jonathan Shapiro) /  4:13 pm  Mar 10, 1990 /
>
>As Mark Langley has pointed out, the supplying of default arguments should
>be thought of as the responsibility of the caller, not part of the function
>definition.

De facto, C++ says otherwise.  If what you say were true, the
following would be legal:

	extern foo(int = 5);

	...

	extern foo(int = 10);

in the same file.  Note that C++ disallows this.

Jon

patch@hpldola.HP.COM (Pat Chkoreff) (03/17/90)

/ hpldola:comp.lang.c++ / shap@delrey.sgi.com (Jonathan Shapiro) / 12:52 pm  Mar 14, 1990 /
>>In article <11430020@hpldola.HP.COM> patch@hpldola.HP.COM (Pat Chkoreff) writes:
>/ hpldola:comp.lang.c++ / shap@delrey.sgi.com (Jonathan Shapiro) /  4:13 pm  Mar 10, 1990 /
>
>> As Mark Langley has pointed out, the supplying of default arguments should
>> be thought of as the responsibility of the caller, not part of the function
>> definition.
>
> De facto, C++ says otherwise.  If what you say were true, the
> following would be legal:
>
>	extern foo(int = 5);
>
>	...
>
>	extern foo(int = 10);
>
> in the same file.  Note that C++ disallows this.
>
> Jon

According to Mark Langley's policy, the example above should NOT be legal,
because the caller's responsibility is ambiguous.  The implementation of his
policy which I proposed would in fact correctly reject the example.  Here's
why.  The compiler would generate the following:

	extern int foo(int);           // extern foo(int = 5);
	static int foo()
	{
		return foo(5);
	}

	// ...

	extern int foo(int);           // extern foo(int = 10);
	static int foo()               // This is line "10".
	{
		return foo(10);
	}

This itself is not legal, because foo() is declared twice:

	CC: "t.c", line 10: error 1034: two definitions of foo()

I still do not see a problem with either Mark Langley's policy or my
implementation of it.


Patrick Chkoreff   719-590-5983
{hpfcla,hplabs}!hpldola!patch