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.