home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.std.c++
- Path: sparky!uunet!munnari.oz.au!metro!extro.ucc.su.OZ.AU!maxtal
- From: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
- Subject: Variants
- Message-ID: <1993Jan1.174133.12629@ucc.su.OZ.AU>
- Sender: news@ucc.su.OZ.AU
- Nntp-Posting-Host: extro.ucc.su.oz.au
- Organization: MAXTAL P/L C/- University Computing Centre, Sydney
- Date: Fri, 1 Jan 1993 17:41:33 GMT
- Lines: 103
-
- I am considering the problem of heterogenous aggregates,
- in which for example we have an array of pointers to objects
- that may be either a V1, V2 or V3 object. These objects need
- not share a common base, and even if they did, we do not
- wish to use dynamic downcasting for this purpose.
-
- Furthermore, the classes for V1, V2 and V3 may already be
- closed and not available for modification, and they
- may or may not contain virtual functions.
-
- The 'correct' solution is basically as follows:
-
- struct V {
- enum {t1,t2,t3} tag;
- union {
- V1 * v1;
- V2 * v2;
- V3 * v3;
- };
- V(V1* x1) : tag(t1), v1(x1) {}
- V(V2* x2) : tag(t2), v2(x2) {}
- V(V3* x3) : tag(t3), v3(x3) {}
- } v;
-
- switch(v.tag) {
- case t1: ... v.v1 ... break;
- case t2: ... v.v2 ... break;
- case t3: ... v.v3 ... break;
- };
-
- This is better than using a void* and a cast I think, because it constrains
- the user to accessing one of the declared types V1, V2 or V3.
-
- However, it is clearly insecure in that the tag need not agree with the
- actual type pointed to, and the switch might have the same problem
- (to say nothing of forgetting the break).
-
- To fix this problem I invent a secure form of the union called a variant,
- its not quite as above but that is the initial picture.
-
- variant V [V1*, V2*, V3*] v;
- select(v)
- {
- type(V1* v1) { ... v1 ... }
- type(V2* v2) { ... v2 ... }
- type(V3* v3) { ... v3 ... }
- }
-
- By not naming the union components or providing explict access to the type
- tag the only way to access the components is via a select statement.
- Provided the variant is properly initialised this is statically
- secure.
-
- The code
-
- v->print();
-
- is also allowed if all the Vi have a print function, it expands to the
- obvious select statement without the overhead of replicated source.
- In general, any statement containing the name of a variant
- (except initialisation)
- is nominally expanded into multiple statements in the obvious way.
- I say 'nominally' because often we can restructure things to optimise
- them, by expanding only up to the smallest unifying subexpression.
-
- Variants are initialised in the obvious way, the static type of the
- initialiser is used to set the tag field.
-
- V v=V1(); // v is now a V1
-
- The usual function overloading/matching rules can be applied
- in most places you expect.
-
- ***Problem 1**** assignment causes some problems. If interpreted
- as user defined assignment, the type cannot be changed, if
- the type changes like an initialisation then user defined assignments
- will not perform as expected. Mm. BTW: this is the only big problem
- I can think of.
-
- It is clearly implied that one can have variant functions:
-
- f(V) { ... }
-
- which of course accept a variant OR any of its components.
-
- Now to the interesting bits. Naturally one must ask,
- what happens if one specialises or instantiates a template
- function with a variant or variant object? Do we generate
- a single variant function or a family of ordinary functions?
-
- The answer is .. it is implementation defined. It doesnt matter.
- Variants commute with templates.
-
- There is more ... but this will do for a start.
-
- Comments? Any help with the assignment problem?
-
-
- --
- ;----------------------------------------------------------------------
- JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
- Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
- ;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
-