[comp.std.c++] the most dissatisfying part of c++

limsoon@saul.cis.upenn.edu (Limsoon Wong) (07/31/90)

the program below is a contrived one.   it cannot  be compiled.
the reason is a type fault. however, it exhibits a very central
characteristic of update.    something should be  done to allow
this kinds of programs.	   			    ---limsoon.



#include <stream.h>
class a { 
  public: 
    a& test() { 
	// do some modification to the state of this object,
	// then ...
	return *this;
};
class b : public a { 
  public:
    int b;
};
main() { 
  b B; 
  B = B.test();
  // type violation despite the fact that 
  // `B' and `B.test()' are the same object.
}

lijewski@batcomputer.tn.cornell.edu (Mike Lijewski) (08/01/90)

In article <27634@netnews.upenn.edu> limsoon@saul.cis.upenn.edu.UUCP (Limsoon Wong) writes:
 
>the program below is a contrived one.   it cannot  be compiled.
>the reason is a type fault. however, it exhibits a very central
>characteristic of update.    something should be  done to allow
>this kinds of programs.	   			    ---limsoon.

I disagree completely that such programs should be allowed.  Since test()
returns a `a&', basically what you are asking is that there be a implicit
conversion from a base class to a derived class.  No way should this be
allowed.  If you really want the below program to work, one possibility
would be to add the following constructor definitions to the class
declaration of b:

	 b() {};
	 b(a) {};
 

-- 
Mike Lijewski  (H)607/277-0394 (W)607/254-8686
Cornell National Supercomputer Facility
ARPA: mjlx@eagle.cnsf.cornell.edu  BITNET: mjlx@cornellf.bitnet
SMAIL:  1122 Ellis Hollow Rd. Ithaca, NY  14850

schemers@vela.acs.oakland.edu (Roland Schemers III) (08/01/90)

In article <27634@netnews.upenn.edu> limsoon@saul.cis.upenn.edu.UUCP (Limsoon Wong) writes:
>
>the program below is a contrived one.   it cannot  be compiled.
>the reason is a type fault. however, it exhibits a very central
>characteristic of update.    something should be  done to allow
>this kinds of programs.	   			    ---limsoon.
>
>#include <stream.h>
>class a { 
>  public: 
>    a& test() { 
>	// do some modification to the state of this object,
>	// then ...
>	return *this;
>};
>class b : public a { 
>  public:
>    int b;
>};
>main() { 
>  b B; 
>  B = B.test();
>  // type violation despite the fact that 
>  // `B' and `B.test()' are the same object.
>}

I for one do not see where 'B' and 'B.test()' are the same object. The
function B.test() clearly returns a reference to an 'a' object. What
you could do is:

class b : public a { 
  public:
    int b;
    b &test() { a::test(); return *this; }
};

(Forgive me if the syntax is not correct)

I understand your point of view though, and I think it would be nice
if there was some construct in C++ to do what you want. I know some of
my classes could use it. Maybe something like:

class a { 
  public: 
    virtual& test() { 
	// do some modification to the state of this object,
	// then ...
	return *this;
};

In otherwords, make the return type 'virtual' like functions. 
In this case, the virtual means the type of the class. So when class
'b' is derived from class 'a', all virtual return types in class 'a'
will now return type 'b'.

Of course this we be very ugly to implement, and is sort of like
parametric return types. 

Actually, would templates solve this problem?

Roland
-- 
Roland J. Schemers III                              Systems Programmer 
schemers@vela.acs.oakland.edu (Ultrix)              Oakland University 
schemers@argo.acs.oakland.edu (VMS)                 Rochester, MI 48309-4401
"Get off your LEF and do something!"                (313)-370-4323

hopper@ux.acs.umn.edu (hopper) (08/01/90)

In article <10599@batcomputer.tn.cornell.edu> lijewski@tcgould.tn.cornell.edu (Mike Lijewski) writes:
>In article <27634@netnews.upenn.edu> limsoon@saul.cis.upenn.edu.UUCP (Limsoon Wong) writes:
> 
>>the program below is a contrived one.   it cannot  be compiled.
>>the reason is a type fault. however, it exhibits a very central
>>characteristic of update.    something should be  done to allow
>>this kinds of programs.	   			    ---limsoon.
>
>I disagree completely that such programs should be allowed.  Since test()
>returns a `a&', basically what you are asking is that there be a implicit
>conversion from a base class to a derived class.  No way should this be
>allowed.  If you really want the below program to work, one possibility
>would be to add the following constructor definitions to the class
>declaration of b:
>
>	 b() {};
>	 b(a) {};

	I propose that there should be a generic class declaration for a
method that returns the same type as the class which it is part of.

Example:

class A {
   me &func(...); // This function modifies a.
   // .
   // .
   // .  (More stuff)
};

class B : A {
   // .
   // .
   // .  (Even more junk)
};

main()
{
   B b;

   b = b.func();
}

	Of course calling it me isn't neccesarily a good idea, but there
still should be one.

(I believe this is what Mike Lijewski had in mind all along, but I'm not
sure.)

Have fun,
UUCP: rutgers!umn-cs!ux.acs.umn.edu!hopper   (Eric Hopper)
     __                    /)                       /**********************/
    / ')                  //                         * I went insane to   *
   /  / ______  ____  o  //  __.  __  o ____. . _    * preserve my sanity *
  (__/ / / / <_/ / <_<__//__(_/|_/ (_<_(_) (_/_/_)_  * for later.         *
Internet:              />                            * -- Ford Prefect    *
hopper@ux.acs.umn.edu </  #include <disclaimer.h>   /**********************/

rae@gpu.utcs.toronto.edu (Reid Ellis) (08/01/90)

schemers@vela.acs.oakland.edu (Roland Schemers III) writes:
|I understand your point of view though, and I think it would be nice
|if there was some construct in C++ to do what you want. I know some of
|my classes could use it. Maybe something like:
|
|class a { 
|  public: 
|    virtual& test() { 
|	// do some modification to the state of this object,
|	// then ...
|	return *this;
|	}
|};
|
|In otherwords, make the return type 'virtual' like functions. 

This is nice in that it uses an already key word.  However, this
would be too easily mixed up with people just forgetting to put
type names in their functions.  Rather than just

	virtual &method();

how about

	virtual class& method();

instead? "class" is already a keyword and it even sounds like what
it is -- a reference to the current "class".  But you're right,
this sounds like the syntax proposed for templates, so maybe they
will solve this problem without this changed syntax.

					Reid
--
Reid Ellis  264 Broadway Avenue, Toronto ON, M4P 1V9               Canada
rae@gpu.utcs.toronto.edu || rae%alias@csri.toronto.edu || +1 416 487 1383

bobatk@microsoft.UUCP (Bob ATKINSON) (08/02/90)

Limsoon Wong writes:
>
>the program below is a contrived one.   it cannot  be compiled.
>the reason is a type fault. however, it exhibits a very central
>characteristic of update.    something should be  done to allow
>this kinds of programs.	   			    ---limsoon.
>
>
>
>#include <stream.h>
>class a { 
>  public: 
>    a& test() { 
>	// do some modification to the state of this object,
>	// then ...
>	return *this;
>};
>class b : public a { 
>  public:
>    int b;
>};
>main() { 
>  b B; 
>  B = B.test();
>  // type violation despite the fact that 
>  // `B' and `B.test()' are the same object.
>}


This is a not-infrequent idiom.  Another example of its use is
creating a "copy" method for collections (what's the return type?)
What one would like to write is

	class Collection {
	public:
		virtual ??? Copy();
	};

which will then be overridden in derived classes to do the 
appropriate copy operation.  Replacing the question marks with 
"Collection*" is not acceptable, for then clients would loose
static type information, as in


	class Collection {
	public:
		virtual Collection* Copy();
	};

	class Set : public Collection {
	public:
		virtual Collection* Copy();	// override for implementation
	}

	//...

	Set* pset, pset2 = ...;

	pset = pset2->Copy();	// illegal!


An approach to solving this problem involves using _non-virtual_ 
member functions.


	class Collection {
	protected:
		virtual Collection* _Copy();	// The real copy function
	public:
		Collection* Copy() { return _Copy(); }

	};

	class Set : public Collection {
		virtual Collection *_Copy();	// override implemenation
	public:
		Set* Copy() { return (Set *)_Copy(); }
	}

	//...

	Set* pset, pset2 = ...;

	pset = pset2->Copy();	// legal now


In the context of the example Limsoon presented, we would write:

	class a {
	protected:
		virtual void	_test()	{ /* do the real work here }
	public:
			a&	test()  { _test(); return *this; }
	};

	class b : public a {
	public:
			b&	test()  { _test(); return *this; }
	};

	main()
		{
		a A;
		b B;
		B = B.test();	// legal
		}


Yes, this is tedious for the class writer, but it makes client's job
a lot easier.  I personally agree, though, that it would be 
worthwhile investigating making the following constructions legal:

	class Collection {
	public:
		virtual	Collection *	Copy();
	};

	class Set : public Collection {
	public:
		virtual Set *		Copy();	   // illegal today
	};


At present, this gives an illegal virtual return type error.

The change to the existing rules is to allow overidings of virtual
functions to return a type to which a standard conversion may be
applied to obtain the inherited return type.  ("standard conversion"
may be too general; perhaps only derived-to-base standard conversions
should be applicable).  The appropriate member function signature would 
be chosen according to the static type of the pointer through which
it is invoked.


	Bob Atkinson
	Microsoft	

[Disclaimer: I am but one solitary opinionated C++ programmer]

brownpc@sunne.crd.ge.com (Paul C Brown) (08/02/90)

In article <27634@netnews.upenn.edu>, limsoon@saul.cis.upenn.edu
(Limsoon Wong) writes:
|>
|>the program below is a contrived one.   it cannot  be compiled.
|>the reason is a type fault. however, it exhibits a very central
|>characteristic of update.    something should be  done to allow
|>this kinds of programs.	   			    ---limsoon.
|>
|>
|>
|>#include <stream.h>
|>class a { 
|>  public: 
|>    a& test() { 
|>	// do some modification to the state of this object,
|>	// then ...
|>	return *this;
|>};
|>class b : public a { 
|>  public:
|>    int b;
|>};
|>main() { 
|>  b B; 
|>  B = B.test();
|>  // type violation despite the fact that 
|>  // `B' and `B.test()' are the same object.
|>}
 
Your proposed program is not "type" correct. The method "test" returns
an object
of type a, not of type b. Objects of type a do not exhibit all of the
properties of type b, and therefore are not valid values for variables
of type b. The 
following version of main is type correct:

main() {
  b B;
  a A;
  A = B.test();
};

To accomplish what you were trying to do would require a second method on 
class b, also named test, that returns an object of type b. If the original
method on class a does, indeed, simply modify the referenced instance, then
the new method could be as simple as:

b::test() {
  a temp;
  temp = this.test();
  return *this;
};

As you have observed, strong typing does not give you everything for
free!                         
Paul C. Brown				brownpc@crd.ge.com
GE Corporate Research & Development
Schenectady, New York

boissier@irisa.fr (franck boissiere) (08/02/90)

From article <56236@microsoft.UUCP>, by bobatk@microsoft.UUCP (Bob ATKINSON):
> 
> Yes, this is tedious for the class writer, but it makes client's job
> a lot easier.  I personally agree, though, that it would be 
> worthwhile investigating making the following constructions legal:
> 
> 	class Collection {
> 	public:
> 		virtual	Collection *	Copy();
> 	};
> 
> 	class Set : public Collection {
> 	public:
> 		virtual Set *		Copy();	   // illegal today
> 	};
> 
> 
> At present, this gives an illegal virtual return type error.
> 
> The change to the existing rules is to allow overidings of virtual
> functions to return a type to which a standard conversion may be
> applied to obtain the inherited return type.  ("standard conversion"
> may be too general; perhaps only derived-to-base standard conversions
> should be applicable).  The appropriate member function signature would 
> be chosen according to the static type of the pointer through which
> it is invoked.

As you said this is a tedious task for the programmer and I would propose
a construct such as:

       class Collection {
       public:
               virtual WHATEVER_CONCSTRUCT_MEANING_THE_CLASS_OF_RECEIVER *    Copy();
       };

With such an approach you still get type checking and you don't have to
rewrite the same line for each class in the subclass hierarchy. You would gain
a lot and could then write truly generic behaviors. This would be a good
starting point for reusable classes. The same idea could be extended for
arguments if needed.

--
Franck BOISSIERE                        boissier@irisa.irisa.fr
Prototyping Lab Manager                 boissier@ccettix.UUCP
C.C.E.T.T.   B.P. 59                    boissier%irisa.irisa.fr@uunet.uu.net
35512 CESSON SEVIGNE CEDEX  FRANCE    

schemers@vela.acs.oakland.edu (Roland Schemers III) (08/03/90)

>
>|In otherwords, make the return type 'virtual' like functions. 
>
>This is nice in that it uses an already key word.  However, this
>would be too easily mixed up with people just forgetting to put
>type names in their functions.
>

Yea, I realized the conflict with virtual functions after I sent the message.
Plus:
	virtual virtual&method();

Is quite ugly!!!!

>how about
>
>	virtual class& method();
>
>instead? "class" is already a keyword and it even sounds like what

wouldn't this cause problems with functions like:

	virtual class X& method();

Where X is a class name? I'm not sure if this is legal syntax, but it
seems to me you should be able to qualify the X with the class keyword.


>But you're right, this sounds like the syntax proposed for templates, 
> so maybe they will solve this problem without this changed syntax.
>

I hope templates solve this problem, if not, maybe a new keyword plus
some extra effort on the C++ translator/compiler is needed to solve this 
problem!

>					Reid
>
>Reid Ellis  264 Broadway Avenue, Toronto ON, M4P 1V9               Canada
>rae@gpu.utcs.toronto.edu || rae%alias@csri.toronto.edu || +1 416 487 1383

-- 
Roland J. Schemers III                              Systems Programmer 
schemers@vela.acs.oakland.edu (Ultrix)              Oakland University 
schemers@argo.acs.oakland.edu (VMS)                 Rochester, MI 48309-4401
"Get off your LEF and do something!"                (313)-370-4323

shap@thebeach.wpd.sgi.com (Jonathan Shapiro) (08/03/90)

In article <27634@netnews.upenn.edu>, limsoon@saul.cis.upenn.edu
(Limsoon Wong) writes:

> the program below is a contrived one.   it cannot  be compiled.
> the reason is a type fault. however, it exhibits a very central
> characteristic of update.    something should be  done to allow
> this kinds of programs.	   			    ---limsoon.
> 
> 
> 
> #include <stream.h>
> class a { 
>   public: 
>     a& test() { 
> 	// do some modification to the state of this object,
> 	// then ...
> 	return *this;
> };
> class b : public a { 
>   public:
>     int b;
> };
> main() { 
>   b B; 
>   B = B.test();
>   // type violation despite the fact that 
>   // `B' and `B.test()' are the same object.
> }

This has surprising consequences in the face of virtuals, since the
caller thinks they are getting an instance of an A, not a B.  From a
static typechecking angle it works, and the parallel argument for
returning pointer to derived in place of pointer to base follows.

However, in the face of multiple inheritance this doesn't work at all.

Jon

dl@g.g.oswego.edu (Doug Lea) (08/03/90)

My `Customization in C++' paper in the '90 Usenix C++ Proceedings
discusses a reasonably clean, but extensive proposal that would
support the desired usage:

#include <stream.h>
class a { 
  public: 
    template a& test() {  // *** Note the qualifier ***
	// do some modification to the state of this object,
	// then ...
	return *this;
};

class b : public a { 
  public:
    int b;
};

main() { 
  b B; 
  B = B.test(); // *** Legal under customization ***
  // type violation despite the fact that 
  // `B' and `B.test()' are the same object.
}


While I played down the expressivity aspects of the type qualifier
sense of `template' and concentrated on optimization consequences,
the proposed constructs are indeed extremely valuable for writing
container classes and the like.

I'd love to hear from anyone concerning the standardizability of
the proposal. 

-Doug
--
Doug Lea, Computer Science Dept., SUNY Oswego, Oswego, NY, 13126 (315)341-2688
email: dl@g.oswego.edu            or dl@cat.syr.edu
UUCP :...cornell!devvax!oswego!dl or ...rutgers!sunybcs!oswego!dl

jeh@cs.rit.edu (Jim Heliotis) (08/04/90)

From article <56236@microsoft.UUCP>, by bobatk@microsoft.UUCP (Bob ATKINSON):
> 
> The change to the existing rules is to allow overidings of virtual
> functions to return a type to which a standard conversion may be
> applied to obtain the inherited return type.  ("standard conversion"
> may be too general; perhaps only derived-to-base standard conversions
> should be applicable).  The appropriate member function signature would 
> be chosen according to the static type of the pointer through which
> it is invoked.
> 
OK, I'll put my two cents in:

- Look at Eiffel.  They do this with some sort of type attribute called
  something like "SAME AS", so you could declare you are returning an
  object the same type as one of your arguments.
  This works because I believe Eiffel's implementation includes class ids
  in objects, but I could be wrong.

- It appears to be hard to solve this problem without dynamic class ids.

- Most C++ classes relevant to this need /have\ dynamic class ids -- their
  virtual function table addresses!  Now, this does not help much when dealing
  with persistent objects, but is it worth a shot?

I have intentionally not given details on my thoughts, to prevent undue noise
on the net.  I'd be happy to work this out some more if anyone wants (I may
do that anyway!).

				Jim Heliotis
				Rochester Institute of Technology
				Rochester, NY 14623-0887
				jeh@CS.RIT.EDU
				{allegra,seismo}!rochester!rit!jeh

dl@g.g.oswego.edu (Doug Lea) (08/04/90)

From: jeh@cs.rit.edu (Jim Heliotis)

> > The change to the existing rules is to allow overidings of virtual
> > functions to return a type to which a standard conversion may be
> > applied to obtain the inherited return type.  ("standard conversion"
> > may be too general; perhaps only derived-to-base standard conversions
> > should be applicable).  The appropriate member function signature would 
> > be chosen according to the static type of the pointer through which
> > it is invoked.
> > 
> 
> OK, I'll put my two cents in:
> 
> - Look at Eiffel.  They do this with some sort of type attribute called
>   something like "SAME AS", so you could declare you are returning an
>   object the same type as one of your arguments.
>   This works because I believe Eiffel's implementation includes class ids
>   in objects, but I could be wrong.

But look at W. Cook's ECOOP '89 paper to see how `LIKE CURRENT' (`SAME AS')
can create holes in a type system. Making something like `typeof' a first
class primitive is equally problematic because an object of
a derived class may be said to have many types, from leaf to root of an
inheritance tree. 

> - It appears to be hard to solve this problem without dynamic class ids.
> 
> - Most C++ classes relevant to this need /have\ dynamic class ids -- their
>   virtual function table addresses!  Now, this does not help much when dealing
>   with persistent objects, but is it worth a shot?

Not entirely relevant: In C++ you can either get static (compile-time)
resolution or dynamic (run-time) resolution. With non-virtual
functions/classes, you are always limited to static resolution, for
better and worse, but the same rules apply to the extent to which
types are known at compile time. So programmers don't absolutely need
dynamic ids as long as they know that static resolution does the right
thing in a particular application.

While I'm at it, here's a different angle towards explaining the 
basic idea. Suppose the original example were redone so that
a::test() returned a pointer, and pointers were used in main().

#include <stream.h>
class a { 
  public: 
    a* test() {  // *** original said `a& test()'
	// do some modification to the state of this object,
	// then ...
	return this; // *** original said `return *this;'
};
class b : public a { 
  public:
    int b;
};
main() { 
  a* B = new b;  // *** original said `b B;'
  B = B->test(); // *** original said `B = B.test();'
}

This has no type violations. The `customization' proposal extends this
usage to apply to references and values by adding a type qualifier
requesting such behavior, along with rules to support it.

-Doug
--
Doug Lea, Computer Science Dept., SUNY Oswego, Oswego, NY, 13126 (315)341-2688
email: dl@g.oswego.edu            or dl@cat.syr.edu
UUCP :...cornell!devvax!oswego!dl or ...rutgers!sunybcs!oswego!dl

limsoon@saul.cis.upenn.edu (Limsoon Wong) (08/06/90)

first a message from jeremy grodberg,

--------------------------------X----------------------------

Sorry I can't post this, but our posting software is broken. You can post this
for me if you like.

Why would you want to have a function that returns an a& be allowed to
replace a b object *as a default*.  It is inherently dangerous, since
the function only affects the "a" part of b, which, for operators like
+ and = is probably not sufficient.  The sample code you wrote shows that
the function test really should be defined as returning void, so that you
would write:

B.test();
B = B;


-- 

Jeremy Grodberg
jgro@apldbio.com    "Beware: free advice is often overpriced!"

--------------------------------X----------------------------

my reply:

the suggestion is a valid alternative, though the assignment
`B = B' is unnecessary.  however, that is not what
i am getting at. 

i am mere pointing out what i consider the part of c++ that
requires improvement: unnecessary loss of type information.
it is clear that before and after performing `B.test()',
the object identity of `B' remains invariant. hence, `B'
and `B.test()' should be given the same type. but `B.test()'
is given type `a', which is much weaker than `b'. 

it is very annoying to have an object that gets progressively
weaker every time you do something to it.


more comments on other replies:

the template suggestion was a very good try. but it does
not work in general. nevertheless, modifications to c++'s
type specification along tha line seems to be the most
satisfactory solution.

i would like a keyword `SUB' to be used as type in class
specification in the following way.

	class C { public:
		(...SUB...) f(..);
		A p(..SUB...);
	};

	class D : public  C {
		something
	};

where `(...SUB...)' is any type expression involving `SUB'.
let `d' be a class `D' object. i want

	d.f	be given the type   *   (..) -> (...D...)
	d.p	be given the type   *   (..D...) -> A

that is, `SUB' is a place holder. it gets replaced by the
corresponding subclass. i think it does not complicate type
checking or implementation; in particular, its with multiple
inheritance. therefore, i do wish something like this be provided.

limsoon.