Path: utzoo!attcan!uunet!mcsun!ukc!strath-cs!dcl-cs!aber-cs!athene!pcg From: pcg@cs.aber.ac.uk (Piercarlo Grandi) Newsgroups: comp.lang.c++ Subject: Simplifying, orthogonalizing bits of C++ Message-ID: Date: 24 Sep 90 16:44:05 GMT References: <8445@jarthur.Claremont.EDU> <57570@microsoft.UUCP> <1677@lupine.NCD.COM> <57649@microsoft.UUCP>o Sender: pcg@aber-cs.UUCP Organization: Coleg Prifysgol Cymru Lines: 264 Nntp-Posting-Host: odin In-reply-to: bobatk@microsoft.UUCP's message of 23 Sep 90 21:32:45 GMT X-Subject-was: Re: Assignments to reference variables [ and operator.() ] On 23 Sep 90 21:32:45 GMT, bobatk@microsoft.UUCP (Bob ATKINSON) said: bobatk> Actually, the thing on the right hand side of a dot operator is bobatk> precisely a "reference to member." References to members don't bobatk> actually exist in C++ as defined, but they would bear the same bobatk> relationship to "pointers to members" (which do exist) as a bobatk> "reference to a non-member" bears to a "pointer to a bobatk> non-member." I can elaborate in more detail if you or others bobatk> would find that helpful. This would be interesting, because as currently defined member pointers do not make much sense as such, but are there almost only to cover up the mistake of having member functions in the language, and at the price of much additional complexity. Member references in the same vein would be amusing to contemplate :-/. If you define member pointers properly, and not as a subterfuge to make member function pointers work even if they should not exists, then I may be in agreement with you. I will now stick out my neck and finally reveal to the general public how to define properly member functions and pointers. The discussion of member references will serve as a trigger to write one of my usual treatises (sorry :->) on the following subjects: MAKING DO WITHOUT MEMBER FUNCTIONS BETTER SENSE WITH MEMBER POINTERS GETTING RID OF PREFIXING AND INHERITANCE DEFINING USEFUL RELATIVE POINTERS So our first introductory topic (after which we will be able to discuss sensibly defined member pointers or functions) will be: MAKING DO WITHOUT MEMBER FUNCTIONS First thing would be to eradicate member functions; allow any function whose first argument is a class type (or a reference to one) to be used indifferently in class member as well as prefix (and also infix if binary operator) notation on objects of that class (idea ripped straight off POP-2). Example: struct matrix { ... }; matrix m(100,100), n(100,100); matrix *p; float det(const matrix &); matrix &operator +(matrix &,matrix &); p = new matrix(100,100); assert (det(m) == m.det()); assert (p->det() == det(*p)); assert (m.operator +(n) == m + n); assert (m + n == operator +(m,n)); We would still need 'friend' for functions that needed access to private members, of course; any friend would be also able to qualify for member call notation. Retaining the ability to have member call notation, instead of getting rid of it along with member functions, would probably be useful in making certain overloading ambiguities easily resolvable. The rule above would obviate the need for a number of bogosities and complications in C++, including '.*', '->*', '::*', 'const' or other attributes after a member function signature, 'this', the default inline status only of in class defined member functions, the odious two-pass rule because of member functions defined instead of simply declared in the class definition, etc... It would also make obvious that to some extent C++ has multi method dispatching just like CLOS. What about virtual functions? Well, this dos not really change that problem. Wait until prefixing as inheritance has been discussed and thrown ou as well :-). BETTER SENSE WITH MEMBER POINTERS Once '::*()' (current member pointers) were made redundant as above, one could start having really useful member pointers, i.e. pointers to a *specific* member, not to any member of a given type. Example: struct listLink { listLink *next; }; struct listHead { listLink *first; }; struct proc { ... listHead files; ... }; struct inode { ... listHead files; ... }; struct file { ... listLink fromProc; listLink fromInode; ... }; inode itable[NINODE]; inode *i = &itable[...]; processAllFilesAttachedToInode: file *f = i->files.first; while (f) { ....; f = (file *) (file.fromInode *) f->fromInode.next; } Here the type of 'fromInode->next' is 'listLink *', but we know that really it is a pointer to a 'file.fromInode' member, and we cast the member pointer to a pointer to the whole 'file' struct containing the member. The old offsetof() and structof() macros become unnecessary! The idea would be that '&a.b' would be of type '(typeof a).b *', and such a type would be converted automatically to '(typeof a.b) *' when required by context. Any '(typeof a).b *' could be converted to a '(typeof a) *', but conversion from a '(typeof a).b *' to an '(typeof a).b *' would require a cast (we could even define member references along the same lines, and make Atkinson happy). Naturally 'b' could be also the name of a prefixed class (maybe using instead an 'a::b' style syntax), or even better class prefixing (inheritance) would be abolished and the relative syntactic sugaring obtained by allowing abbreviated member designation, when unambiguous, a la Pl/1 or Cobol. GETTING RID OF PREFIXING AND INHERITANCE Now that I have dragged myself into this as well :-), assuming we have (note that to avoid discussing more than one change at a time here we assume that we still have member functions): struct A { ... x; ... f(); ... k(); ... }; struct B { ... y; ... g(); ... k(); ... }; what is the difference between saying: struct C : A,B { ... z; ... h(); ... } c; c.x; c.y; c.z; c.f(); c.g(); c.h(); c.A::k(); c.B::k(); and struct C { ... A a; ... z; ... B b; ... h(); ... } c; c.x; c.y; c.z; c.f(); c.g(); c.h(); c.a.k(); c.b.k(); assert ((&c.x == &c.a.x) && (&x.g == &c.b.g)); if we say that any submember of a structured type can be denoted by any unique path to it, eliding any intermediate structure name that are not needed to disambiguate the path? There is no difference, and saying so avoids a large number of problems with prefixing, both in the language definition and in the implementation. What about the equivalent of virtual prefixing and virtual functions? Virtual members, of course. DEFINING USEFUL RELATIVE POINTERS As a final note, suppose we actually want to a type constructor for something like 't::*', but one that makes more sense, that is a real relative pointer. The intended use is to create position independent collections of data structures (shared memory, persistent objects, etc...). We have several choices: 1) based pointers; when '*p' is done, an implicit addition is performed of ther value of another variable, the base is performed. 2) explicit offset pointers; 'p' can only be dereferenced in the context of an explicitly provided base of the right type. 3) implicit offset or "self based" pointers; '*p' is based on '&p'. I simply dislike option 3), so let's discuss 1) and 2). I would suggest. assuming some declaration for an area (Who remembers MARY, PL/1, MUPL ?) in which to use relative pointers [please excuse the use of malloc instead of new and its overloadings, I am trying to make obvious the underlying implementation]: extern void *malloc(size_t); extern void subInit(void *,size_t); void *area = malloc(areaSize); subInit(area,areaSize); struct record { ... x,y; ... }; // these will be allocated in area Here is a possible syntax for option 1): struct record { ... x,y; ... }; extern void *::*subMalloc(void *area,size_t); record area::*r = (record area::*) subMalloc(area,sizeof (record)); r->x = ...; r->y = ...; and for option 2): extern void (void)::*subMalloc(void *,size_t); record (void)::*r = (record (void)::*) subMalloc(area,sizeof (record)); (area+r)->x = ...; (area+r)->y = ...; I am not too enthusiastic about either notation; I would probably use another symbol, e.g. '@' to denote relative pointerhood. One would also need some (fairly obvious) rules for casts and relative pointer usage. In any case I very much prefer explicit offset rather than based or self based relative pointers; they seem to me to be much more C-ish. In a very real sense, explicit offsets are like the traditional C-ish numeric pointer offsets, only they are typed explicitly, and not implicitly with the same type as the base pointer. I mean, in 'a+b' he resulting pointer would be an absolute pointer of the same type as pointer 'a' if 'b' were a numeric offset, and of the type indicated for 'b' if 'b' were a relative pointer. Finally, to simplify matters, at the arguably small price of losing some type safety, I would have all explicit offset pointers be automatically declared as relative to a 'void *' variable, and any absolute pointer entering into arithmetic with them be automatically cast to 'void *' (in any case the "real" type information is that carried by the relative pointer type). With these revisions, the example above would become [please excuse again avoiding the use of an overloading of operator new here]: extern void @subMalloc(void *,size_t); record @r = (record @) subMalloc(area,sizeof (record)); r[area].x = ...; area[r].y = ...; or even, in C-ish fashion (use as type uncostructor the same symbol used for the type constructor): r@area.x = ...; area@r.y = ...; This could be done with 'intelligent pointer' objects, but I guess they are important enough to be made a language primitive; also to have the same facility with intelligent pointer objects one would need templates, and this obviates the need for them in this case. I think this is enough material for C++ 3.0 (note that all these proposals are perfectly backward compatible, so they could be added while the "features" they would obsolete faded away). :-) :-). Incidentally, I think that adding all these things to an existing C++ 2.x compiler front end would be very easy and take up very little space, and actually replacing the existing facilities with these would make the compiler's front end (and debuggers, etc...) vastly simpler and smaller. -- Piercarlo "Peter" Grandi | ARPA: pcg%uk.ac.aber.cs@nsfnet-relay.ac.uk Dept of CS, UCW Aberystwyth | UUCP: ...!mcsun!ukc!aber-cs!pcg Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk