nagle@well.sf.ca.us (John Nagle) (03/22/90)
Let's start a discussion about the problem of how to achieve generic objects (ones with types as parameters, at least at compile time) in C++. Some possibilities: 1. Explicitly use "pointer to void". Works, but violates type safety and requires much repetitive writing. 2. Encapsulate the necessary messy definitions in preprocessor defines, as shown in "The C Answer Book". Ugly and tends to produce confusing error messages when trouble occurs. May not be type safe. 3. Add compile-time generic facilities along the lines of those in Ada to the language. Provides similar functionality to #2, with proper checking and diagnostics, but requires a language change. 4. Add types as first-class objects, as in Smalltalk, so that one can have type-valued variables and can do declarations at run time. 5. Other? Comments? John Nagle
marc@dumbcat.UUCP (Marco S Hyman) (03/23/90)
In article <16808@well.sf.ca.us> nagle@well.UUCP (John Nagle) writes: > Let's start a discussion about the problem of how to achieve > generic objects (ones with types as parameters, at least at compile > time) in C++. Some possibilities: > > 1. Explicitly use "pointer to void". Works, but violates > type safety and requires much repetitive writing. Doesn't always work. In the case of multiple inheritance pointer adjustment is not performed when assigning to a void pointer. When you take from a void * a different type than was placed in the void pointer, things break. Example: #include <stdio.h> class B1 { public: int i1; B1(): i1(0) {}; }; class B2 { public: int i2; B2(): i2(0) {}; }; class D: public B1, public B2 { public: int d; D(): d(0) {}; }; main() { void * generic; D something; B1 * b1 = &something; B2 * b2; ++b1->i1; generic = b1; // no adjustment b2 = generic; // no adjustment printf( "%d\n", b2->i2 ); } The output will be 1, not zero. The compiler assumes you know what you're doing when you use void *. Ouch. // marc -- // {ames,decwrl,sun}!pacbell!dumbcat!marc // pacbell!dumbcat!marc@lll-winken.llnl.gov
basti@orthogo.UUCP (Sebastian Wangnick) (03/23/90)
nagle@well.sf.ca.us (John Nagle) writes: > Let's start a discussion about the problem of how to achieve >generic objects (ones with types as parameters, at least at compile >time) in C++. What about the following possibility: Encapsulate the constaints put onto the parameter type by the generic type into a new class, and inherit a new parameter class from this one and from the original parameter class. This seems to be the way choosen in Smalltalk, where the most obvious constraints put onto objects by, let say, Collections or Sets, i.e., test for equality and generation of a hash code per object, are inherited by every object from the base class Object. Other classes putting additional burdens onto their parameter classes, such as linked lists, describe these constraints in another class, Link in this case, from which every possible parameter class has to be inherited. But with the dawn of multiple inheritance, it will be possible to use parameter classes for certain generic classes *without* recoding them, just by inheriting a new class from the original parameter class *and* the constraint class, as I stated above. This has even further benefits, because the constraint class is the source of information where all the requirements needed by the generic class from their parameter classes are bundled. Viewed with the eyes of a software engineer, this special class shouuld better be called the requirements class for the generic class. Sebastian Wangnick, BRD -> D -|||> Deutsches Reich
dog@cbnewsl.ATT.COM (edward.n.schiebel) (03/24/90)
From article <16808@well.sf.ca.us>, by nagle@well.sf.ca.us (John Nagle): > > Let's start a discussion about the problem of how to achieve > generic objects (ones with types as parameters, at least at compile > time) in C++. Some possibilities: > > 3. Add compile-time generic facilities along the lines of those > in Ada to the language. Provides similar functionality to > #2, with proper checking and diagnostics, but requires > a language change. > This is my choice. Although I am not at all familiar with Ada, I assume you are refering to an implementation generics similar to parameterized types described by Bjarne in some paper I can't find right now. It seems to me that parameterized types provide the type saftey (as opposed to using void*'s) and the run-time efficiency (vrs. types as first-class objects) that makes the most sense within the C++ language. C++'s contributors have always taken these two concerns very seriously throughout the language's evolution, and I don't expect them to stop now. I have used and written (with more or less success) generic types based on the preprocessor and I must say that it is an interim solution at best. Nonetheless, the generic types created by that scheme are a fair approximation of pramaterized types, and I have come to rely on that kind of capability. From the first Usenix C++ Workshop (my first real introduction to the language) to the last OOPSLA, and probably through this years Usenix conference (see you there), all I have heard is "what about generics" "what about exception handling" "what about garbage collection"? I don't even want to get another garbage collection discussion started, and between the other two, for my money, I would like to see parameterized types implemented first. I believe they would be adopted and used by more programs much quicker than exception handling and would provide the greatest benefit to C++'s users. Ed Schiebel AT&T Bell Laboratories dog@cblph.att.com
leo@atcmp.nl (Leo Willems) (03/26/90)
From article <152@dumbcat.UUCP>, by marc@dumbcat.UUCP (Marco S Hyman): > In article <16808@well.sf.ca.us> nagle@well.UUCP (John Nagle) writes: > > Let's start a discussion about the problem of how to achieve > > generic objects (ones with types as parameters, at least at compile > > time) in C++. Some possibilities: > > > > 1. Explicitly use "pointer to void". Works, but violates > > type safety and requires much repetitive writing. > > Doesn't always work. In the case of multiple inheritance pointer adjustment > void * a different type than was placed in the void pointer, things break. Stuff deleted > void * generic; > D something; > B1 * b1 = &something; > B2 * b2; > > ++b1->i1; > generic = b1; // no adjustment > b2 = generic; // no adjustment Under C++ 1.2 the last sentence will give a "bad assigment type" error. As I understand it, assignment to void* must be casted in C++ (which is different in ANSI-C) thereby bringing type-information back. Isn't this true in C++ 2.0? (I have a lot of code (of the preprocessor- generic type) which will break if type-information is lost (using a cast)). If you apply a cast in the example does it work correct then? (If it works, is it legal or just luck? Andrew Koenig told me a long time ago it is legal, but that was in the 1.2 darkages, in which I still live :-)) Leo Willems Internet: leo@atcmp.nl AT Computing UUCP: mcvax!hp4nl!kunivv1!atcmpe!leo P. O. Box 1428 6501 BK Nijmegen Phone: +31-80-566880 The Netherlands Fax: +31-80-555887
marc@dumbcat.UUCP (Marco S Hyman) (03/27/90)
In article <574@atcmpe.atcmp.nl> leo@atcmp.nl (Leo Willems) writes: > From article <152@dumbcat.UUCP>, by marc@dumbcat.UUCP (Marco S Hyman): > > void * generic; > > D something; > > B1 * b1 = &something; > > B2 * b2; > > > > ++b1->i1; > > generic = b1; // no adjustment > > b2 = generic; // no adjustment > > If you apply a cast in the example does it work correct then? > (If it works, is it legal or just luck? Andrew Koenig told me a long time > ago it is legal, but that was in the 1.2 darkages, in which I still live :-)) G++ (the version I used to check the above example) does not require the cast. My cfront 2.0 hasn't arived for this machine yet so I can't test it, but am under the impression that when you use a case you are only telling the compiler that you know what you are doing. If you specify b2 = (B2 *) generic; you are telling the compiler that you know generic is a B2. No adjustment will be made and your code breaks. The moral is *don't* use void* for generics. // marc -- // {ames,decwrl,sun}!pacbell!dumbcat!marc // pacbell!dumbcat!marc@lll-winken.llnl.gov
leo@atcmp.nl (Leo Willems) (03/28/90)
From article <574@atcmpe.atcmp.nl>, by leo@atcmp.nl (Leo Willems): > From article <152@dumbcat.UUCP>, by marc@dumbcat.UUCP (Marco S Hyman): stuff deleted >> Doesn't always work. In the case of multiple inheritance pointer adjustment >> void * a different type than was placed in the void pointer, things break. > > Stuff deleted > >> void * generic; >> D something; >> B1 * b1 = &something; >> B2 * b2; >> >> ++b1->i1; >> generic = b1; // no adjustment >> b2 = generic; // no adjustment > > Under C++ 1.2 the last sentence will give a "bad assigment type" error. > As I understand it, assignment to void* must be casted in C++ (which > is different in ANSI-C) thereby bringing type-information back. > Oops, I spoke to soon: casting a B1* to B2* is nonsense (most of the time) b2 = (B2*) generic; // most of the time wrong. However, even if probably wrong, leaving out the cast will genete a compiler error. What I had in mind (and of which I think is legal in C++ 1.2 and 2.0): class b{ ... }; class bd : b { .... }; bd bd_o; void *g; b *bp; g = &bd_o; bp = (b*) g; // not casting here will give an error Sorry about that. Leo Willems Internet: leo@atcmp.nl AT Computing UUCP: mcvax!hp4nl!kunivv1!atcmpe!leo P. O. Box 1428 6501 BK Nijmegen Phone: +31-80-566880 The Netherlands Fax: +31-80-555887 Leo.
rfg@ics.uci.edu (Ronald Guilmette) (03/28/90)
In article <153@dumbcat.UUCP> marc@dumbcat.UUCP (Marco S Hyman) writes: >In article <574@atcmpe.atcmp.nl> leo@atcmp.nl (Leo Willems) writes: > > From article <152@dumbcat.UUCP>, by marc@dumbcat.UUCP (Marco S Hyman): > > > void * generic; > > > D something; > > > B1 * b1 = &something; > > > B2 * b2; > > > > > > ++b1->i1; > > > generic = b1; // no adjustment > > > b2 = generic; // no adjustment > > > > If you apply a cast in the example does it work correct then? > > (If it works, is it legal or just luck? Andrew Koenig told me a long time > > ago it is legal, but that was in the 1.2 darkages, in which I still live :-)) > >G++ (the version I used to check the above example) does not require the >cast. My cfront 2.0 hasn't arived for this machine yet so I can't test it, g++ started out as a C compiler, and got changed into a C++ compiler later. You can still find occasional reminders of its lineage in places, like for instance in the fact that the type-checking & type-conversions have not been "hardened" to cfront standards yet. (Some people may prefer the looser typing that g++ provides.) g++ still allows you to assign a void* to other types of pointer variables. Cfront doesn't allow this without explicit casting. >... The moral is *don't* use void* for generics. I concur. // Ron Guilmette (rfg@ics.uci.edu) // C++ Entomologist // Motto: If it sticks, force it. If it breaks, it needed replacing anyway.