[comp.lang.c++] default argument discussion

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

jak@cs.brown.edu writes
>
>langley@bigbird.rtp.dg.com (Mark L Langley) suggests that the following
>should be legal:
>>   
>>      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 correspondance...
>
>Surely you can't be serious about this?  You are proposing dynamic
>scoping for C++.  C and C++ have always been lexically scoped, and the
>problems with dynamic scoping are so great that languages like Lisp,
>which used to be dynamically scoped, are moving away from it.

Yes I am serious, but no, I am not *proposing dynamic scoping*.  I am 
just proposing that you delay evaluating the arguments until you evaluate 
the function call.

Apparently I didn't make my point very clear; in retrospect I chose a poor
example.

Perhaps I am suggesting a slight mind shift.  Namely, the default argument 
is not (I suggest) a property of the function definition.  Rather it is a 
property of the caller.  I think this is a better way of looking at it for 
two reasons.  

First, since the caller is the one that provides the arguments, shouldn't 
he have some say (if he wants it) in the arguments he *doesn't* provide?

Second, I think that this is more compatible with the usual conventions of
name-shadowing.  Simply look at the declaration of the function as a
scoped name, and more local declarations may shadow it.  This imposes 
no new constraints on the user (he can just use the declaration from 
the include file unless he has something in mind).

>
>Consider:
>
>- How can you guarantee that any i and j will be in scope when bar is
>called?  You would have to look at the context of every call to bar to
>decide this.

If there is no i or j visible, then i and j are undeclared identifiers,
and this should be reported as an error.

>
>- How do you decide which i and j should be used?  You have to scan up
>the stack at run-time -- a very expensive proposition.
>
>- How can you do any reasoning on your program when you have no idea
>which i and j are going to be used?
>

No such mechanism is required.  You take the most local copies of i and 
j at call time, the same as in any other expression.

Now I wouldn't really expect someone to put
	foo(int x=i+j) {}
in the function definition -- this would probably be a misuse.  Rather, I 
would more expect some user of foo (a regular user at that) to supply some 
more appropriate value than the generic default.  Consider this

	// foo.h
	void foo(int operation_type=0);
		...
	// Delete.h
	#include foo.h
	enum MajorOptype { Noop, Insert=100, Delete=200 };
	void foo(int operation_type=Delete);

	// application.c
	#include Delete.h
	enum MinorOptype { Paragraph, Word, Line, Column };

	// big function bar with LOTS of calls to foo
	insert_from_deleted_buffer() {
	extern void foo(int operation_type=major_operation + minor_operation);
	const int major_operation=Insert;
	enum MinorOptype minor_operation;
	  <code that sets minor_operation>
	foo();
	}


>                                Jak
>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>ARPA/BITNET :  jak@cs.brown.edu                           Tel  : (401) 863 7664
>Snail       :  86 Benevolent St, Providence, 02906 RI.    Tel  : (401) 272 6149

Mark Langley
langley@dg-rtp.dg.com

 -- Creative dissent always encouraged --

cs132093@cslab7f.cs.brown.edu (Dan Bornstein) (03/10/90)

In article <1006@xyzzy.UUCP>, langley@bigbird.rtp.dg.com (Mark L
Langley) writes:
> [lots-o-stuff deleted]
> 
> Now I wouldn't really expect someone to put
> 	foo(int x=i+j) {}
> in the function definition -- this would probably be a misuse.  Rather, I 
> would more expect some user of foo (a regular user at that) to supply some 
> more appropriate value than the generic default.  Consider this
> 
> 	// foo.h
> 	void foo(int operation_type=0);
> 		...
> 	// Delete.h
> 	#include foo.h
> 	enum MajorOptype { Noop, Insert=100, Delete=200 };
> 	void foo(int operation_type=Delete);
> 
> 	// application.c
> 	#include Delete.h
> 	enum MinorOptype { Paragraph, Word, Line, Column };
> 
> 	// big function bar with LOTS of calls to foo
> 	insert_from_deleted_buffer() {
> 	extern void foo(int operation_type=major_operation + minor_operation);
> 	const int major_operation=Insert;
> 	enum MinorOptype minor_operation;
> 	  <code that sets minor_operation>
> 	foo();
> 	}

Whoah! This could lead to some strange bugs. Remember that many .h files have
an #ifdef NAME and #endif surrounding their bodies. So, take this situation:

// foo.h
#ifndef FOO
#define FOO
void foo (int op=0);
#endif

// bar.h
#ifndef BAR
#define BAR
#include "foo.h"
const Bar = 100;
void foo (int op=Bar);
inline void bar (void)
{
  // stuff
  foo ();
  // more stuff
}
#endif

// baz.h
#ifndef BAZ
#define BAZ
#include "foo.h"
inline void baz (void)
{
  // stuff
  foo (); // using foo()'s original default.
  // more stuff
}
#endif

Now, foon wants to use bar() and baz(), so we have: 

// foon.c
#include "bar.h"
#include "baz.h"

Now, the problem is that baz() will be defined incorrectly since after bar.h
is included, the #defines keep the old foo() declaration from being
encountered. This could be a real problem when bar and baz are not originally
part of the same project and therefore the original author(s) didn't realize
the ugly possibility.

In other words, baz() behaves differently depending on whether or not bar.h
has been included first. This seems to defeat modularity.

-dan

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

>langley@bigbird.rtp.dg.com (Mark L Langley) suggests that the following
>should be legal:
>   
>      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 correspondance...
>
>Mark Langley
>langley@dg-rtp.dg.com

This is a great example of why language design should be left to
people who know what they're doing.

C already has a facility for doing this.  It is called 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.

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

Jonathan Shapiro