Topic: Maximal Variants


Author: oren@wisdom.weizmann.ac.il (Ben-Kiki Oren)
Date: Mon, 31 May 1993 13:14:24 GMT
Raw View
Apologies if you get this twice. The gremlins have taken over my system :-(

maxtal@physics.su.OZ.AU (John Max Skaller) writes:
> In article <1993May24.141008.22099@wisipc.weizmann.ac.il> oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
> > No; the question is, do variants commute with *templates*?
>
>  They commute with template functions.
>
> > I assume that
> > you have already decided variants do not commute with structs; but that
> > is only half the work. I suggest that the Dunion example shows that,
> > at least for type templates, they don't; and therefore they should
> > not commute with function templates, either.
>
>  They do. I cant do anything about it.

Why not? Variants *are* your creation, aren't they? Just wave the wand... :-)

I never really understood why template member functions should be different
from function templates. Especially as one can declare an empty type template,
and define the function as a static member function. The only difference
between this and function templates is that the programmer needs to supply the
type arguments in the call, instead of the compiler figuring them out by itself.

In the case of variants, the compiler can trivially do this by creating an
ad-hoc variant which is the union of the static compile-time types of the
relevant arguments - not too difficult, IMHO, compared to the rest of the
problems the compiler has with templates.

The benefits are consistency between function and type templates, as well
as enhanced functionality (e.g., the max function). Assuming that this is
only done for templates with 'variant' type arguments, normal templates
semantics is unchanged (and does not suffer any performance penalty).

Therefore, again, why not?

> > >  What about inheritance? Can you derive from
> > > a variant?
> >
> >IMHO, you can only derive from a class. If the class contains
> >a variant member, well and good. I don't see any reason for allowing
> >inheritance from a variant directly. Do you have an example for code
> >that uses such a thing?
> >
>  The question is not whether its allowed or not,
> but whether variants commute with inheritance or not.
>
>  I cant chose the answer I want: either they do or they dont.
>
>  variant V [X,Y];
>
>  class D : public V {};
>
>  class D1 : public X {};
>  class D2 : public Y {};
>
>  variant Q [D1,D2];
>
> Is Q == D or not?

I see your point. Well, either D is, or is not, a variant.

If it is a variant, it is equivalent to Q. As such, the only reason to allow
the declaration of D is completeness ("... variants commute with every construct
... except for struct, class and template ..." - the future C++ standard :-)

If it is not - what can be done with an object of type D?
You can not select on it to call X- and Y- specific functions, because D
is not a variant! Member functions of D would have the same problem -
the type of 'this' is D*, which is *not* a variant, nor a pointer to one :-),
and therefore you can not select on it. The only thing you could conceivably
access would be functions which have the *exact same interface* in X and Y.
Likewise for data members. In effect, D becomes the intersection between X and
Y. Not a very useful concept, and a very troublesome one, IMHO.

I would conclude that D is a variant, is equivalent to Q, and that use of
this functionality would be extremely rare - How likely is the case that the
declarations of D1 and D2 are identical, so the declaration of D becomes
possible? However, it should be allowed for completeness sake.





Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 1 Jun 1993 08:54:37 GMT
Raw View
In article <1993May31.131424.3658@wisipc.weizmann.ac.il> oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
>>  They commute with template functions.
>>
>> > at least for type templates, they don't; and therefore they should
>> > not commute with function templates, either.
>>
>>  They do. I cant do anything about it.
>
>Why not? Variants *are* your creation, aren't they? Just wave the wand... :-)

 I cant create a system that is inconsistent, or it
wont be possible to implement it.

>
>I never really understood why template member functions should be different
>from function templates.

 They aren't, as far as variants go.

>In the case of variants, the compiler can trivially do this by creating an
>ad-hoc variant which is the union of the static compile-time types of the
>relevant arguments - not too difficult, IMHO, compared to the rest of the
>problems the compiler has with templates.

 All variants are 'ad hoc' as it were.

 The reason I believe the compiler cant do this I have
explained before:

 struct S;
 S* s;

The compiler must know if 'S' is variant or not. It cant tell
so it must assume not. So as to allocate 's'.

 struct S {
  variant [long,int] x;
 };

Too late, the compiler discovers that S contains a variant.
But 'S' is already an actual type, as are pointers to it.

>
>The benefits are consistency between function and type templates, as well
>as enhanced functionality (e.g., the max function). Assuming that this is
>only done for templates with 'variant' type arguments, normal templates
>semantics is unchanged (and does not suffer any performance penalty).
>
>Therefore, again, why not?

 As above.
>
>> > >  What about inheritance? Can you derive from
>> > > a variant?
>> >
>>  variant V [X,Y];
>>
>>  class D : public V {};
>>
>>  class D1 : public X {};
>>  class D2 : public Y {};
>>
>>  variant Q [D1,D2];
>>
>> Is Q == D or not?
>
>I see your point. Well, either D is, or is not, a variant.
>
>If it is a variant, it is equivalent to Q.

 Yes.

>As such, the only reason to allow
>the declaration of D is completeness
>("... variants commute with every construct
>... except for struct, class and template ..." - the future C++ standard :-)
>
>If it is not - what can be done with an object of type D?
>You can not select on it to call X- and Y- specific functions, because D
>is not a variant!

 Yes. But a given function can select *inside* itself
to do this -- so the function is effectively variant :-)

>Member functions of D would have the same problem -
>the type of 'this' is D*, which is *not* a variant, nor a pointer to one :-),
>and therefore you can not select on it.

 From the outside. You can select on it from the inside.

>The only thing you could conceivably
>access would be functions which have the *exact same interface* in X and Y.
>Likewise for data members. In effect, D becomes the intersection between X and
>Y. Not a very useful concept, and a very troublesome one, IMHO.
>
>I would conclude that D is a variant, is equivalent to Q, and that use of
>this functionality would be extremely rare - How likely is the case that the
>declarations of D1 and D2 are identical, so the declaration of D becomes
>possible? However, it should be allowed for completeness sake.
>

 I think it would be common. Because it replaces downcasting.

 class Animal : public variant [Dog, Cat, Lion] {
  int caneat(variant [Meat, Grass, Fish] )
  { .. poor mans Multiple Dispatch .. }
 };


 if( anAnimal.caneat(aFood) ) anAnimal.feed( aFood )
 else cout<<"Bad Zookeeper eaten by Animal";

I dont know just how correct this example is :-)


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Tue, 1 Jun 1993 15:13:19 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
>>> > at least for type templates, they don't; and therefore they should
>>> > not commute with function templates, either.
>>>
>>>  They do. I cant do anything about it.
>>
>>Why not? Variants *are* your creation, aren't they? Just wave the wand... :-)
>
> I cant create a system that is inconsistent, or it
>wont be possible to implement it.

Sure you can: C++ is full of inconsistencies! :-)
(For example, why can you overload operator arrow but not operator dot? :-).
But of course these are inconsistencies in the common-sense meaning of
the term, not logical inconsistencies.

>>I never really understood why template member functions should be different
>>from function templates.
>
> They aren't, as far as variants go.
>
>>In the case of variants, the compiler can trivially do this by creating an
>>ad-hoc variant which is the union of the static compile-time types of the
>>relevant arguments - not too difficult, IMHO, compared to the rest of the
>>problems the compiler has with templates.
>
> All variants are 'ad hoc' as it were.
>
> The reason I believe the compiler cant do this I have
>explained before:
>
> struct S;
> S* s;
>
>The compiler must know if 'S' is variant or not. It cant tell
>so it must assume not. So as to allocate 's'.
>
> struct S {
>  variant [long,int] x;
> };
>
>Too late, the compiler discovers that S contains a variant.
>But 'S' is already an actual type, as are pointers to it.

According to the same reasoning, C++ is fundamentally flawed
in it's over-use of casts to mean different things. Consider:

 struct S1;
 struct S2;

 S1 *s1;
 S2 *s2;
 ...
 s1 = (S1 *) s2;

Now is this cast a conversion, or is this a type punning cast?
The compiler assumes the latter, but it could be wrong. For
example, s1 may be later defined as
 struct S1 : X, S2 { ... };

Yet this didn't stop Bjarne from adding multiple inheritance to C++.
Avoiding inconsistencies in the language is just one of the many
things a language designer needs to consider.

(Maybe you just need a better wand, Max, one like Bjarnes.
Bjarne certainly seems to have managed to work wonders with his :-).

--
Fergus Henderson                     This .signature virus might be
fjh@munta.cs.mu.OZ.AU                getting old, but you still can't
                                     consistently believe it unless you
Linux: Choice of a GNU Generation    copy it to your own .signature file!




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Tue, 1 Jun 1993 17:17:58 GMT
Raw View
In article <9315301.29678@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
>> I cant create a system that is inconsistent, or it
>>wont be possible to implement it.
>
>Sure you can: C++ is full of inconsistencies! :-)

 So? That just proves no compiler has implemented 'C++'.
We all know that anyhow :-)

>According to the same reasoning, C++ is fundamentally flawed

 Yes :-)

>in it's over-use of casts to mean different things. Consider:
>
> struct S1;
> struct S2;
>
> S1 *s1;
> S2 *s2;
> ...
> s1 = (S1 *) s2;
>
>Now is this cast a conversion, or is this a type punning cast?

 Doesnt matter for pointers like the above.

>The compiler assumes the latter, but it could be wrong. For
>example, s1 may be later defined as
> struct S1 : X, S2 { ... };
>
>Yet this didn't stop Bjarne from adding multiple inheritance to C++.
>Avoiding inconsistencies in the language is just one of the many
>things a language designer needs to consider.
>
>(Maybe you just need a better wand, Max, one like Bjarnes.
>Bjarne certainly seems to have managed to work wonders with his :-).
>

 Bjarne did not create the obscure cast problems, and
has written a paper and made a proposal to fix it --
unsuccessfully so far due to inconsistencies.

 An inconsistent language cannot be implemented:
but something else like it can, and thats what all the
existing compilers are.

 A compiler is mechanistic: it has a complete and
consistent solution to everything, it implements a formal
language determined by its algorithm.

 Humans have computers .. who needs magic ?

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: oren@wisdom.weizmann.ac.il (Ben-Kiki Oren)
Date: Tue, 1 Jun 1993 14:28:46 GMT
Raw View
To: maxtal@physics.su.OZ.AU (John Max Skaller)
Subject: Re: Maximal Variants
Newsgroups: comp.std.c++

In article <1993Jun1.085437.4726@ucc.su.OZ.AU>you write:
>In article <1993May31.131424.3658@wisipc.weizmann.ac.il>oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
>> They [variants] [should not] commute with template functions.

> They do. I cant do anything about it.

>>Why not? Variants *are* your creation, aren't they? Just wave the wand... :-)

> I cant create a system that is inconsistent, or it
>wont be possible to implement it.

Did that stop the template people, or the exceptions? :-)

> The reason I believe the compiler cant do this I have
>explained before:
>
> struct S;
> S* s;
>
>The compiler must know if 'S' is variant or not. It cant tell
>so it must assume not. So as to allocate 's'.
>
> struct S {
>  variant [long,int] x;
> };
>
>Too late, the compiler discovers that S contains a variant.
>But 'S' is already an actual type, as are pointers to it.

I see why variants do not commute with structs and classes. My question
was about function templates. Suppose I say:

 template<variant T> T max(T x, T y) { ... }

meaning that max is a template function, and T is allowed to be
a variant (using 'class' would yield the current semantics). Then:

 {
  type_1 x = ...;
  type_2 y = ...;
  type_3 m = max(x, y);
 }

Now, type_1 and type_2 may, or may not, be variants. They are, however,
*known at compile time*. Now, 'max' needs a value for 'T'. Since 'variant',
not 'class', was specified, the compiler looks for a variant value for T -
the union of type_1 and type_2 = [ type_1, type_2 ]. This is also the return
type of 'max'. In effect, this is equivalent to:

 typedef variant [ type_1, type_2 ] T;
 T max(T x, T y) { ... }

 {
  type_1 x = ...;
  type_2 y = ...;
  type_3 m = max(x, y);
 }

I.e., the compiler instantiates the 'max' function for the variant
[ type_1, type_2 ]. Other invocations will use other variants for T.
If type_1 == type_2 then T becomes a simple type, which raises no problems.
If there are intersections between variants used to instantiate max, again
there is no problem - either the compiler generates a function for each
combination of real types, and selects before the call, or it does a
big select inside a single 'max' function, or it generates a max function
for each variant used, with a small internal select, and calls the appropriate
one. Either way, it works.

If max was defined with <class T> instead, then the compiler would handle
it as it does today. It would be illegal to call max if either type_1 != type_2
or if type_1 or type_2 were variants. I think allowing the programmer to control
where variants are allowed and where they are not is important; I would suggest
that the same rule will hold for type templates.

Therefore, again,
>>Therefore, again, why not?

>>> What about inheritance? Can you derive from a variant?
>>>
>>> variant V [X,Y];
>>>
>>> class D : public V {};
>>>
>>> class D1 : public X {};
>>> class D2 : public Y {};
>>>
>>> variant Q [D1,D2];
>>>
>>>Is Q == D or not?

>>I see your point. Well, either D is, or is not, a variant.
>>If it is a variant, it is equivalent to Q.

> Yes.

>>If it is not - what can be done with an object of type D?
>>You can not select on it to call X- and Y- specific functions, because D
>>is not a variant!

> Yes. But a given function can select *inside* itself
>to do this -- so the function is effectively variant :-)

>>Member functions of D would have the same problem -
>>the type of 'this' is D*, which is *not* a variant, nor a pointer to one :-),
>>and therefore you can not select on it.

> From the outside. You can select on it from the inside.

Sorry, I don't buy that. The type of 'this' is D*. If D* is not a variant, you
can't select on it. Unless you want to say that the type of 'this' is *not* D* -
in which case you have to tell me what it is, and how come it isn't D* like in
any normal class. Frankly, I don't see what the type of 'this' could conceivably
be, aside from D*.

> I think it would be common. Because it replaces downcasting.
>
> class Animal : public variant [Dog, Cat, Lion] {
>  int caneat(variant [Meat, Grass, Fish] )
>  { .. poor mans Multiple Dispatch .. }
> };
>
>
> if( anAnimal.caneat(aFood) ) anAnimal.feed( aFood )
> else cout<<"Bad Zookeeper eaten by Animal";
>
>I dont know just how correct this example is :-)
>
>        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
> Maxtal Pty Ltd,      CSERVE:10236.1703
>        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
>        NSW 2131, AUSTRALIA

Hmmm. It seems to me that it is better to define caneat in some mixin
class - say Eater - and have it accept the variant. Mix it into the
concrete classes, and implement it differently for each animal. You get
the same functionality in a much clearer code, IMHO. Or am I missing
something?

      Oren.

--
Life is tough and the hardships are many.




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 2 Jun 1993 14:58:31 GMT
Raw View
In article <1993Jun1.142846.14643@wisipc.weizmann.ac.il> oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
>To: maxtal@physics.su.OZ.AU (John Max Skaller)
>
>I see why variants do not commute with structs and classes. My question
>was about function templates. Suppose I say:
>
> template<variant T> T max(T x, T y) { ... }

>If max was defined with <class T> instead, then the compiler would handle
>it as it does today. It would be illegal to call max if either type_1 != type_2
>or if type_1 or type_2 were variants.
>I think allowing the programmer to control
>where variants are allowed and where they are not is important; I would suggest
>that the same rule will hold for type templates.

 It already does.

 'max' as written requires arguments of the same type.

 T1 max(T2,T3)

allows different types. (A proposal to allow T3 as above is under
consideration by the committee:

 max<int> (int(x), long(y));

If you want to return 'either' of T2 or T3, try this:

 template<class X, class Y> variant[X,Y] max(X,Y) { .. }

>
>> From the outside. You can select on it from the inside.
>
>Sorry, I don't buy that. The type of 'this' is D*. If D* is not a variant, you
>can't select on it. Unless you want to say that the type of 'this' is *not* D* -
>in which case you have to tell me what it is, and how come it isn't D* like in
>any normal class. Frankly, I don't see what the type of 'this' could conceivably
>be, aside from D*.

 Easy. Its either an A* or a B*, depending on the actual type,
but only *inside* a member function.

 In particular:

 class A; class B;
 variant V [A,B];
 class D : V {
  V* operator V*(){ return this; }
 }

 Note that D* can be *cast implicitly* up to the base:

 V* v= new D(...);

>
>Hmmm. It seems to me that it is better to define caneat in some mixin
>class - say Eater - and have it accept the variant. Mix it into the
>concrete classes, and implement it differently for each animal. You get
>the same functionality in a much clearer code, IMHO. Or am I missing
>something?
>

 you need multiple dispatch, and that cant be done
with polymorphism.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: oren@wisdom.weizmann.ac.il (Ben-Kiki Oren)
Date: Thu, 3 Jun 1993 10:03:56 GMT
Raw View
Subject>Re: Maximal Variants
Newsgroups>comp.std.c++
Distribution>
References><1993Jun2.145831.4480@ucc.su.OZ.AU>

maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>In article <1993Jun1.142846.14643@wisipc.weizmann.ac.il> oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
>>I see why variants do not commute with structs and classes. My question
>>was about function templates...
>
> T1 max(T2,T3)
>
>allows different types. (A proposal to allow T3 as above is under
>consideration by the committee:
>
> max<int> (int(x), long(y));
>
>If you want to return 'either' of T2 or T3, try this:
>
> template<class X, class Y> variant[X,Y] max(X,Y) { .. }

If I understand correctly, your position is:
- Variants do not commute with type templates (defining class/struct) as
  variants do not commute with class/struct.
- Variants do commute with function templates as variants commute with
  functions.
This makes sense. I take it to mean that in I define 'max' as above,
and 'X' and/or 'Y' are variants in some call, then the call gets expanded to
a select. In each case the appropriate 'real type' version of max will be
called. O.K., I am convinced. Now sell it to the ANSI C++ guys :-)

>>Sorry, I don't buy that. The type of 'this' is D*. If D* is not a variant, you
>>can't select on it. Unless you want to say that the type of 'this' is *not* D* -
>>in which case you have to tell me what it is, and how come it isn't D* like in
>>any normal class. Frankly, I don't see what the type of 'this' could conceivably
>>be, aside from D*.
>
> Easy. Its either an A* or a B*, depending on the actual type,
>but only *inside* a member function.
>
> In particular:
>
> class A; class B;
> variant V [A,B];
> class D : V {
>  V* operator V*(){ return this; }
> }
>
>        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
> Maxtal Pty Ltd,      CSERVE:10236.1703
>        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
>        NSW 2131, AUSTRALIA

No. 'this' is *not* a variant of [ A*, B* ]. Example:

 class A; class B;
 variant V [ A, B ];
 class D : public V {
    ^^^^^^
  int i;
  int get_i() {
   V* vp = ...; // vp is variant [ A*, B* ]
   D* dp = this; // dp is a simple D*
   vp->i;   // No such thing
   dp->i;   // Legal; accesses local 'i'
   this->i;  // Legal; accesses local 'i'
  }
 }

In order to properly define the type of 'this', you need to define two
implicit types:

 class DA : A { int i; ... }
 class DB : B { int i; ... }

The type of 'this' is variant [ DA*, DB* ], otherwise 'i' is inaccessible.
The compiler generates a select on DA*, DB* around each member function
(the programmer cannot do this as the names DA and DB are not available to him).

It seems we are faced with two choices:
a) Say that if the type of 'this' is variant [ DA*, DB* ] then the type of D
   is the type of '*this' - variant [ DA, DB ].
b) Say that the type of D is not the type of '*this'. D will be a new type
   that allows access to 'i' and 'get_i' in the above example, and possibly
   to the intersection of the types 'A' and 'B' (i.e. members with the
   same name & type). D would not be a variant.

It seems to me that (b) above raises much more problems then it is worth.
For example, in the above, 'dp' and 'this' would not have the same semantics.
What happens when inheriting from D? Can the derived class select on 'this'?

BTW, note that even under (a), the programmer has a hard time accessing the
types DA and DB as they have no accessible names. Selects on A and B limit
to A's and B's interface. Therefore I think that the programmer will prefer:

class A; class B;
template<class T> TD : T { int i; ... }
typedef variant [ TD<A>, TD<B> ] D;

To get the same effect, but with the ability to select on D with TD<A>
and TD<B>. And if it is required that D be not a variant, the programmer could
write:

typedef Dunion<D> UD;

and use the type UD instead.

Unless a really good reason is presented, I vote that
  D == typeof(*this) == [ DA, DB ]
I am not convinced by the poor-man-multiple-dispatch example. Couldn't
it be done under (a) as well? And even if not, is it worth the mess resulting
from adopting (b)?

     Oren.




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Mon, 24 May 1993 10:24:47 GMT
Raw View
In article <1993May23.141043.15367@wisipc.weizmann.ac.il> oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
>Would variants allow the following:
>
>  variant comparable [ short, int, long, float, double ];
>
>  comparable max(comparable x, comparable y) { return(x > y ? x : y); }

 Yes.
>
>As opposed to:
>
>  // template T max<class T> { if(x > y) return(x); else return(y); }
>
>  test()
>  {
> double dl = max(2.0, 1); // This wouldn't work for the template
>  }

 But this does:

 template<class R, class T1, class T2>
 max(T1 x, T2 y) { return x>y ? x :y; }

 double dl = max<double>(2.0, 1);

where the instance 'max<double>' is a proposed language extension.
(I believe suggested by Andrew Koenig, proposed  by Bjarne Stroustrup,
and looks pretty good to me .. hope the committee votes this one in)

>
>If yes, two interesting points:
>
>1) Variants are similar to templates with limited class parameters.

 Yes: constrained genericity, and/or constrained type inference.

> The
>   implications are obvious - the compiler generates all the code in
>   advance (no collecting of calls etc.) and gives sensible error messages.

 Depends: if static, compile time type inference can be used
to resolve the actual type, there is no need to generate all
instances of an inlined function, for example.

 If the function was external, all the cases would have
to be generated in advance.

>   The down side is that all versions are generated (even if not all are
>   used), and all types have to be known at the point the function is
>   defined. Obviously variants are not the thing for containers; but they
>   may be just the thing for a function like 'max' above.

 Why? The idea originally sprang from the need to support
heterogenous aggregates, that is, containers of several types.
If variants cant support that, their principal reason for
existence goes down the tubes, and probably all chance
(if there was any) of this extension being accepted.
>
>2) Variants allow promotions - unlike templates, a use of the variant
>   type throughout the function does not imply it is the same type. For
>   example, the return value of the 'max' function and the two arguments
>   are all 'comparable', but that does not imply all three must be of the
>   same type, as would be the case for the template function. Also note that
>   this flexibility is limited in that all possible combinations (of inputs)
>   must be valid.

 Yes. You have to cover all the cases.
>
>Combining variants and overloading adds some flexibility.

 Really, it adds nothing because variant functions
*are* overloaded families of functions.

 The main thing here is that, like template functions,
you only have to write out the source code *once*, and,
unlike templates, you can specialise for particular types
withing the variant function by using the 'select' statement.

 void func( variant [A,B,C] x )
 {
  .....
  select(x)
  {
   type(A a) { .. specialise for A }
   type( variant[B,C] x) { ... }
  }
  ...
 }

In fact: there is no reason you cant use the select statement
in an *ordinary* template function, come to think of it:

 template<class T> void func(T x) {
  ...
  select(x)
  {
   type(A a) { .. specialise for A }
   type( ... ) { }
  }
 }

although there is no way to do anything except add extra code
for certain types (since there is no way to say 'type other than A')

>For the 'max'
>example, it would be possible to separate the set of types to (disjoint?)
>subsets, where the types in each are all comparable with each other:
>
>   variant builtin [ short, int, long, float, double ];
>
>   builtin max(builtin x, builtin y) { return(x < y ? x : y) };
>
>   variant mytypes [ A, B ]; // A's and B's are comparable
>
>   mytypes max(mytypes x, mytypes y) { if(x < y) return(x); else return(y); }
>
>   test()
>   {
> A a;
> B b;
> mytypes m = max(a, b); // Note that no cast is required
> C mc = max(a, b); // O.K. if A and B can be converted to C
>   }

 Yes. Thats not really possible with template functions is it?

>
>But what about the case where the sets are not disjoint?

 Error at compile time. Every use would be ambiguous.
It constitutes a breach of the "one definition rule".

 I think however that this case:

 template<class T> f(T) { .. }

 f(variant [int, long]);
 f(variant [int, float]);

is *not* an error: it is equivalent to saying

 f(variant [int, long, float]);

or, if you prefer

 f(int);
 f(long);
 f(float);

that is, its not currently an error to say

 f(int);
 f(int);

is it?

>For example:
>
>   variant ab [ A, B ];
>   variant bc [ B, C ];
>
>   ab max(ab x, ab y) { if(x < y) return(x); else return(y); }
>   bc max(bc x, bc y) { if(x < y) return(x); else return(y); }
>
>   test()
>   {
> B b1, b2;
> C c1, c2;
> C mc = max(c1, c2); // O.K.? or must it be:
> bc mcc = max(c1, c2); // 'cause we don't know about the
>     // internals of 'max'? What if
>     // 'max' is inline?
> B mb = max(b1, b2); // Ambiguous?
> bc mbc = max(b1, c1); // O.K.?
>   }
>
>Is the current overloading rule strong enough to withstand this abuse?
>Remember that the arguments may be variants, the function may accept
>variants, or both.

 I think it must be a violation of one definition rule.
However the equivalent declarations are *not* an error.
(You can declare a function as many times as you like .. cant you?)

>
>Another question. Could variants be used as types for function templates?

 Yes. But what do you mean? There are three cases:

 a) instantiation by call
 b) specialisation by declaration
 c) specialisation by definition

Cases (a) and (b) allow overlapping declarations, case (c) does not.

>Assuming they are, maybe the template max function works after all:
>
>   variant comparable [ short, int, long, float, double ];
>   variant integer [ short, int, long ];
>
>   template T max<class T> { if(x > y) return(x); else return(y); }

 What does this mean? Do you mean

 template<class T> max(T x, T y( { ...} //1

or do you mean

 template<class X, class Y> max(X x, Y y) { ... }//2

>
>   test()
>   {
> comparable c1, c2;

 // error : uninitialised variants :-)

> integer i1, i2; = ...;
> comparable mc = max(c1, c2); // Should be O.K.

 If you meant //1 above, no it fails, if you mean //2 then
it works :-) If you used a variant function, it also works.

> integer mi = max(i1, i2); // T = comparable? integer? Ambiguous?

 T is not every anything but int, long, float, etc.
It is a type. A variant is not a type.

>Forbidding variants as type arguments for functions is hard to justify as
>they are allowed for type templates.

 If you allow declaration an initialisation of variants,
you have to allow function call because it uses initialisation
semantics to pass parameters *by definition*. Variants cant
change that: variant functions are a consequence of initialisation.

>It is also possible to specify
>the function as a member function of a type-template class (this would
>require the programmer to specify the variant when he calls the function,
>which solves the problem):
>
>   template max<class T> {
> T& x, y;
>   public:
> operator T() { if(x < y) return(x); else return(y); }

 Bug! x>y please :-)

> max(T& _x, T& _y) : x(_x), y(_y) { }
>   }
>
>   test()
>   {
> comparable m = max<comparable>(3.0, 2);
> double md = max<comparable>(3.0, 2); // would this work?
>   }

 Yes, I think so. We have:

 class max<comparable> {
  comparable& x, &y;
 public:
  operator comparable()  { .. }
  max<comparable> (comparable &x, comparable& y) ...
 }

and the call

 max<comparable>(3.0, 2);

creates an object with x the float 3.0 and y the int 2. Seems fine.
The member functions are implicity variant if they refer to x and
y, so operator comparable() will return either the float 3.0
or the int 2.

>
>All this raises some hairy questions as to the interaction between templates,
>variants, and overloading. And possibly signatures. But then, most of C++ is
>interaction between ``small and simple'' features :-)

 There really aren't that many hairy problems :-)

 Just one, really:

 do variants commute with structs?

>
>Skaller has claimed in a previous post that he has a detailed proposal;
>I assume he has thought about the above. Could you enlighten us?

 I'll try. Whenever a variant is used for

 a) initialisation
 b) passing parameters

then no problems exist: the compiler either actually uses
type inference to completely resolve the actual type
at compile time, or it optimises the code with hidden
threads and switches on a hidden tag to make it look "as if"
that resolution had been done.

If you think only of (a) and~r (b) above, (no variants in
structs, no variant returns) then you can see that
there are no problems -- there *cant* be any problems
because it can be done by pre-processing entirely and
the compiler never sees the variants at all.

Returning variants is different. The function that returns
a variant cant be split into several functions, because you
cant overload on return type: the function must
return a discriminated union, that is, the compiler must
reserve enough store to initialise the largest of the
possible types, and also have a protocol for passing the 'tag'
that indicates which type.

Now once the tag is there, static type inference wont work:
subsequent code based on the function return must be generated
for all possible cases in advance, and switching done on
the tag to ensure only code generate for the actual run-time
case is ever executed (thats how type safety is guarranteed).

The second case where this happens is if you put a variant
in a struct. Here the same single object must contain
a discriminated union: you cant overload the name of a class.

Well, I'm not 100% certain of this. :-)
But the C++ rules seem to require it: you are allowed to
say this:

 struct X;
 X* px;

without knowing anymore details: you cant allow this
if a struct might be variant (because the pointer would
have to be variant too).

 What about inheritance? Can you derive from
a variant?

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Mon, 24 May 1993 12:43:17 GMT
Raw View
In article <9314419.14533@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
> mc = valof {
>   select(c1,c2) {
>     type(short c1s, short c2s) { return max(c1s, c2s); }
>     type(int c1i, short c2i) { return max(c1i, c2i); } // ILLEGAL
>     ... the other 23 possibilities, most of them illegal ...
>   }
> }
>Of course there's no such thing as valof in C++, but I hope you get the idea.

 Actually, I was tempted to make select an expression so you could
write the above (without the valof) but decided discretion was the better
part of valour: people could use that for anonymous functions in
ordinary C++ expressions :-)

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: oren@wisdom.weizmann.ac.il (Ben-Kiki Oren)
Date: Mon, 24 May 1993 14:10:08 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:
: In article <1993May23.141043.15367@wisipc.weizmann.ac.il> oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:

 [ stuff deleted ]

: >Another question. Could variants be used as types for function templates?
:
:  Yes. But what do you mean? There are three cases:
:
:  a) instantiation by call
:  b) specialization by declaration
:  c) specialization by definition
:
: Cases (a) and (b) allow overlapping declarations, case (c) does not.

I meant instantiation by call. Specialization is a different (interesting)
issue. It seems non-trivial to use a variant to specialize (by definition,
case c) a template. A template works by giving all occurrences of T the same
value; a variant requires all combinations to be valid. I haven't really
thought about this side of the issue, though.

: >Assuming they are, maybe the template max function works after all:
: >
: >   variant comparable [ short, int, long, float, double ];
: >   variant integer [ short, int, long ];
: >
: >   template T max<class T> { if(x > y) return(x); else return(y); }
:
:  What does this mean? Do you mean
:
:  template<class T> max(T x, T y) { ...} //1
:
: or do you mean
:
:  template<class X, class Y> max(X x, Y y) { ... } //2

I meant:

 template<class T> T max(T x, T y) { ... }

Sorry for the bad syntax (it was late :-). I think case #2 won't work;
what is the return type of max?

: >   variant comparable [ short, int, long, float, double ];
: >   variant integer [ short, int, long ];
: >
: >   test()
: >   {
: > comparable c1 = ..., c2 = ...;
: > integer i1 = ..., i2 = ...; // Initialize variants :-)
: >
: > comparable mc = max(c1, c2); // Should be O.K.
:
:  If you meant //1 above, no it fails, if you mean //2 then
: it works :-) If you used a variant function, it also works.
:
: > integer mi = max(i1, i2); // T = comparable? integer? Ambiguous?
:
:  T is not every anything but int, long, float, etc.
: It is a type. A variant is not a type.

If this is true, how can you say:

 template<class T> class Dunion { T value; ... }

and then instantiate it as Dunion<MyVariant>, if T can not be a variant?
Claiming that variant does not commute with struct is not enough; you
also must allow T to take variant value! If T can only be int,
long, etc. then the instantiation of Dunion is illegal, IMHO. You can
not put a select around a data type.

(Possible solution: use the notation

 template<variant T> class Dunion { ... }

to indicate that a variant is allowed/expected. This would prevent variants
from being used where they are not expected, and would make clear where
they *are* expected. A non-variant type would be allowed as a value for T
in this case - it would be equivalent to "variant T[a_non_variant_type]")

If, however, Dunion is legal (it is too useful to outlaw :-), then it
seems there is a case for claiming that T in the max function above
*can* take variant values. The problem is, the compiler has a hard time
finding the "most appropriate" variant as the value of T. The call
max(i1, i2) above matches two possible T values - integer and comparable.
As well as an infinite number of others, if you consider all possible
(undeclared) variants.

A possible solution would be to union the types of the call arguments. At a
first glance, I don't see a problem with that. Assuming that this would
be allowed only for variant-template-functions (template functions with
<variant T> type arguments), then normal template functions would not suffer
any performance penalty. Consider:

template<variant T> T& max(T& x, T& y) { if(x < y) return(x); else return(y); }

and now max(any-one-type, any-other-type) would work, as long as they are
comparable with each other, and it would return - surprise surprise -
variant [ any-one-type, any-other-type ]. This would trivially allow using
it for promotable built-in types. I am starting to like the idea!

 [ more stuff deleted ]

: >All this raises some hairy questions as to the interaction between templates,
: >variants, and overloading. And possibly signatures. But then, most of C++ is
: >interaction between ``small and simple'' features :-)
:
:  There really aren't that many hairy problems :-)
:
:  Just one, really:
:
:  do variants commute with structs?

No; the question is, do variants commute with *templates*? I assume that
you have already decided variants do not commute with structs; but that
is only half the work. I suggest that the Dunion example shows that,
at least for type templates, they don't; and therefore they should
not commute with function templates, either.

 [ even more stuff gone ]

:
:  What about inheritance? Can you derive from
: a variant?
:
: --
:         JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
:  Maxtal Pty Ltd,      CSERVE:10236.1703
:         6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
:         NSW 2131, AUSTRALIA

IMHO, you can only derive from a class. If the class contains
a variant member, well and good. I don't see any reason for allowing
inheritance from a variant directly. Do you have an example for code
that uses such a thing?

     Oren.





Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 26 May 1993 17:11:15 GMT
Raw View
In article <1993May24.141008.22099@wisipc.weizmann.ac.il> oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
>
>No; the question is, do variants commute with *templates*?

 They commute with template functions.

>I assume that
>you have already decided variants do not commute with structs; but that
>is only half the work. I suggest that the Dunion example shows that,
>at least for type templates, they don't; and therefore they should
>not commute with function templates, either.

 They do. I cant do anything about it.

>:
>:  What about inheritance? Can you derive from
>: a variant?
>:
>IMHO, you can only derive from a class. If the class contains
>a variant member, well and good. I don't see any reason for allowing
>inheritance from a variant directly. Do you have an example for code
>that uses such a thing?
>
 The question is not whether its allowed or not,
but whether variants commute with inheritance or not.

 I cant chose the answer I want: either they do or they dont.

 variant V [X,Y];

 class D : public V {};

 class D1 : public X {};
 class D2 : public Y {};

 variant Q [D1,D2];

Is Q == D or not?


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: cbergren@comm.mot.com (Craig Bergren)
Date: Thu, 27 May 1993 03:40:09 GMT
Raw View
John Max Skaller (maxtal@physics.su.OZ.AU) wrote:
> In article <1993May24.141008.22099@wisipc.weizmann.ac.il> oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
> >
> >:
> >:  What about inheritance? Can you derive from
> >: a variant?
> >:
> >IMHO, you can only derive from a class. If the class contains
> >a variant member, well and good. I don't see any reason for allowing
> >inheritance from a variant directly. Do you have an example for code
> >that uses such a thing?
> >
>  The question is not whether its allowed or not,
> but whether variants commute with inheritance or not.

>  I cant chose the answer I want: either they do or they dont.

>  variant V [X,Y];

>  class D : public V {};

>  class D1 : public X {};
>  class D2 : public Y {};

>  variant Q [D1,D2];

> Is Q == D or not?

This is a trick question!  :-)

We don't know whether Q == D because Q and D aren't instanced.  We won't
know the type of objects instanced using these  declarations  until they
are initialized.  Therefore, if we don't know what D is until some class
derived  from D  initializes  itself.  Likewise,  we won't  know what an
object of  variant Q is until it is  initialized  with a D1 or a D2.

As far as variants commuting with inheritance...  How can one know which
interface to inherit, X or Y?  It isn't like having D inherit both X and
Y, the variant  says X or Y (not both).  Let's say that class X contains
a function "int foo()" as does Y.  Which foo is D::foo()?


--
Craig Bergren

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
!  The views I hold are not mine, nor those of my employer.                   !
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++




Author: tob@world.std.com (Tom O Breton)
Date: Thu, 27 May 1993 17:06:32 GMT
Raw View
John (MAX):

> whether variants commute with inheritance or not.


>   variant V [X,Y];
>
>   class D : public V {};
>
>   class D1 : public X {};
>   class D2 : public Y {};
>
>   variant Q [D1,D2];
>
> Is Q == D or not?

I would guess they do not.

In principle D1 and D2 can have all sorts of different things in them.
If in this case they have the same thing (nothing besides the base
class), you don't want to generally depend on that.

In other words, I don't see that D1 and D2 are synchronized, except by
accident, whereas always D == D.

        Tom

--
The Tom spreads its huge, scaly wings and soars into the sky...
(tob@world.std.com, TomBreton@delphi.com)




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 29 May 1993 00:28:20 GMT
Raw View
In article <C7p3Ix.DqI@world.std.com> tob@world.std.com (Tom O Breton) writes:
>John (MAX):
>
>> whether variants commute with inheritance or not.
>
>
>>   variant V [X,Y];
>>
>>   class D : public V {};
>>
>>   class D1 : public X {};
>>   class D2 : public Y {};
>>
>>   variant Q [D1,D2];
>>
>> Is Q == D or not?
>
>I would guess they do not.
>
>In principle D1 and D2 can have all sorts of different things in them.
>If in this case they have the same thing (nothing besides the base
>class), you don't want to generally depend on that.
>
>In other words, I don't see that D1 and D2 are synchronized, except by
>accident, whereas always D == D.
>

 yes, you could be right: in principle encapsulation
hides details that mean we *must* distinguish the types D1 and D2.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 29 May 1993 00:25:32 GMT
Raw View
In article <1993May27.034009.27570@lmpsbbs.comm.mot.com> cbergren@comm.mot.com (Craig Bergren) writes:
>John Max Skaller (maxtal@physics.su.OZ.AU) wrote:
>> In article <1993May24.141008.22099@wisipc.weizmann.ac.il> oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:
>> >
>> >:
>> >:  What about inheritance? Can you derive from
>> >: a variant?
>> >:
>> >IMHO, you can only derive from a class. If the class contains
>> >a variant member, well and good. I don't see any reason for allowing
>> >inheritance from a variant directly. Do you have an example for code
>> >that uses such a thing?
>> >
>>  The question is not whether its allowed or not,
>> but whether variants commute with inheritance or not.
>
>>  I cant chose the answer I want: either they do or they dont.
>
>>  variant V [X,Y];
>
>>  class D : public V {};
>
>>  class D1 : public X {};
>>  class D2 : public Y {};
>
>>  variant Q [D1,D2];
>
>> Is Q == D or not?
>
>This is a trick question!  :-)
>

 It wasnt meant to be.

>We don't know whether Q == D because Q and D aren't instanced.

 What I meant was whether Q and D are the same entities:
are they both variants? If so, are they the *same* variant?

>As far as variants commuting with inheritance...  How can one know which
>interface to inherit, X or Y?  It isn't like having D inherit both X and
>Y, the variant  says X or Y (not both).

 A subobject of D has to be either an X or a Y.
The question is whether D is a variant or a class.

>Let's say that class X contains
>a function "int foo()" as does Y.  Which foo is D::foo()?
>

 That is easy: both. D::foo() is a normal variant (member)
function. No problem with that. You call foo(), then if
D got initialised with an X, you call X::foo(). If D got initialised
with a Y, you call Y::foo().

 The question, to put it another way, is:
if I have a D*, is it a variant pointer or an ordinary pointer?

 I think its a variant pointer: in particular,
for any member of D, the 'this' pointer is a variant.

 But the contrary argument is that *any* pointer
to a struct cannot be variant, and indeed enclosing a
variant object within a struct walls in the variant.

 SO: I used to think variants commute with inheritance,
but now I'm not sure because the dont appear to commute with structs.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: oren@wisdom.weizmann.ac.il (Ben-Kiki Oren)
Date: Sun, 23 May 1993 14:10:43 GMT
Raw View
Would variants allow the following:

  variant comparable [ short, int, long, float, double ];

  comparable max(comparable x, comparable y) { return(x > y ? x : y); }

As opposed to:

  // template T max<class T> { if(x > y) return(x); else return(y); }

  test()
  {
 double dl = max(2.0, 1); // This wouldn't work for the template
  }

If yes, two interesting points:

1) Variants are similar to templates with limited class parameters. The
   implications are obvious - the compiler generates all the code in
   advance (no collecting of calls etc.) and gives sensible error messages.
   The down side is that all versions are generated (even if not all are
   used), and all types have to be known at the point the function is
   defined. Obviously variants are not the thing for containers; but they
   may be just the thing for a function like 'max' above.

2) Variants allow promotions - unlike templates, a use of the variant
   type throughout the function does not imply it is the same type. For
   example, the return value of the 'max' function and the two arguments
   are all 'comparable', but that does not imply all three must be of the
   same type, as would be the case for the template function. Also note that
   this flexibility is limited in that all possible combinations (of inputs)
   must be valid.

Combining variants and overloading adds some flexibility. For the 'max'
example, it would be possible to separate the set of types to (disjoint?)
subsets, where the types in each are all comparable with each other:

   variant builtin [ short, int, long, float, double ];

   builtin max(builtin x, builtin y) { return(x < y ? x : y) };

   variant mytypes [ A, B ]; // A's and B's are comparable

   mytypes max(mytypes x, mytypes y) { if(x < y) return(x); else return(y); }

   test()
   {
 A a;
 B b;
 mytypes m = max(a, b); // Note that no cast is required
 C mc = max(a, b); // O.K. if A and B can be converted to C
   }

But what about the case where the sets are not disjoint?
For example:

   variant ab [ A, B ];
   variant bc [ B, C ];

   ab max(ab x, ab y) { if(x < y) return(x); else return(y); }
   bc max(bc x, bc y) { if(x < y) return(x); else return(y); }

   test()
   {
 B b1, b2;
 C c1, c2;
 C mc = max(c1, c2); // O.K.? or must it be:
 bc mcc = max(c1, c2); // 'cause we don't know about the
     // internals of 'max'? What if
     // 'max' is inline?
 B mb = max(b1, b2); // Ambiguous?
 bc mbc = max(b1, c1); // O.K.?
   }

Is the current overloading rule strong enough to withstand this abuse?
Remember that the arguments may be variants, the function may accept
variants, or both.

Another question. Could variants be used as types for function templates?
Assuming they are, maybe the template max function works after all:

   variant comparable [ short, int, long, float, double ];
   variant integer [ short, int, long ];

   template T max<class T> { if(x > y) return(x); else return(y); }

   test()
   {
 comparable c1, c2;
 integer i1, i2; = ...;
 comparable mc = max(c1, c2); // Should be O.K.
 integer mi = max(i1, i2); // T = comparable? integer? Ambiguous?
 double d2 = max(i1, 3.0); // Error or T = comparable?
 double d1 = max(1, 2.0); // T = comparable?
   }

Forbidding variants as type arguments for functions is hard to justify as
they are allowed for type templates. It is also possible to specify
the function as a member function of a type-template class (this would
require the programmer to specify the variant when he calls the function,
which solves the problem):

   template max<class T> {
 T& x, y;
   public:
 operator T() { if(x < y) return(x); else return(y); }
 max(T& _x, T& _y) : x(_x), y(_y) { }
   }

   test()
   {
 comparable m = max<comparable>(3.0, 2);
 double md = max<comparable>(3.0, 2); // would this work?
   }

All this raises some hairy questions as to the interaction between templates,
variants, and overloading. And possibly signatures. But then, most of C++ is
interaction between ``small and simple'' features :-)

Skaller has claimed in a previous post that he has a detailed proposal;
I assume he has thought about the above. Could you enlighten us?

       Oren__GoOgLE_
X-Google-ArrivalTime: 1993-05-23 09:21:12 PST
Path: gmd.de!Germany.EU.net!mcsun!sun4nl!wtrlnd!hotline!system
From: system@hotline.wlink.nl (SYSTEM 0PERATOR)
Newsgroups: alt.bbs.pcboard
Subject: Re: Pcboard 15.0
Message-ID: <JZ5y4B1w165w@hotline.wlink.nl>
Date: Sat, 22 May 93 23:12:30 PDT
References: <34.5.233.0NC61239@commlink.wariat.org>
Organization: The Hotline Foundation
Lines: 30

jonathan.evans@commlink.wariat.org (Jonathan Evans) writes:

>
> Hi all,
>
>    I have set up Pcboard 15 and so far really like it.  I am having one
> problem with the QWK command....  I followed the directions given in the
> whatsnew file and added the work directory and made a bat file to
> run it but I always get a message
> saying error compressing and it aborts the attempt.  I am sure It is
> something simple. Any  suggestions.....
>
Did you create the QWK batch file in \PCB as listed in the whatsnew file
?
This will solve your problems.
>
> Jonathan Evans
>
>   Internet: jonathan.evans@commlink.wariat.org

Regards,
        Piet


-------------------------------------------------------------------------------

System@hotline.wlink.nl - Supervisor  Hotline PCBoard + Waffle (testing)
                          +31 20 6891014  /   +31 20 6893869    Piet Ebbes
-------------------------------------------------------------------------------





Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Mon, 24 May 1993 09:34:04 GMT
Raw View
oren@wisdom.weizmann.ac.il (Ben-Kiki Oren) writes:

>Would variants allow the following:
>
>  variant comparable [ short, int, long, float, double ];
>
>  comparable max(comparable x, comparable y) { return(x > y ? x : y); }

Yes.

>But what about the case where the sets are not disjoint?
>For example:
>
>   variant ab [ A, B ];
>   variant bc [ B, C ];
>
>   ab max(ab x, ab y) { if(x < y) return(x); else return(y); }
>   bc max(bc x, bc y) { if(x < y) return(x); else return(y); }

This is an error at the point of definition: there are
two definitions for max(B,B), which is not allowed.

>Another question. Could variants be used as types for function templates?

Well, not really. See below.

Mind you, Dunions *can* definitely be used as types for function
templates, and as shown earlier, you can use variants to efficiently
implement Dunions, so you can get a similar effect.
You can also use your technique of member function within a class.

>Assuming they are, maybe the template max function works after all:
>
>   variant comparable [ short, int, long, float, double ];
>   variant integer [ short, int, long ];
>
>   template T max<class T> { if(x > y) return(x); else return(y); }

I assume you meant
 template <class T> T max (T x, T y) { ... }

>   test()
>   {
> comparable c1, c2;
> integer i1, i2; = ...;
> comparable mc = max(c1, c2); // Should be O.K.

No, this is not OK.  Remember, any expression containing
a variant gets expanded as an implicit select. It expands to
 mc = valof {
   select(c1,c2) {
     type(short c1s, short c2s) { return max(c1s, c2s); }
     type(int c1i, short c2i) { return max(c1i, c2i); } // ILLEGAL
     ... the other 23 possibilities, most of them illegal ...
   }
 }
Of course there's no such thing as valof in C++, but I hope you get the idea.
An expression involving variants is legal only if all the cases
of the expanded select statement would be legal.

> integer mi = max(i1, i2); // T = comparable? integer? Ambiguous?

integer.

> double d2 = max(i1, 3.0); // Error or T = comparable?

Error. The mere existence of variants doesn't change the legality
of operations not involving variants.

> double d1 = max(1, 2.0); // T = comparable?

Error.

>Forbidding variants as type arguments for functions is hard to justify as
>they are allowed for type templates. It is also possible to specify
>the function as a member function of a type-template class (this would
>require the programmer to specify the variant when he calls the function,
>which solves the problem):
>
>   template max<class T> {
> T& x, y;
>   public:
> operator T() { if(x < y) return(x); else return(y); }
> max(T& _x, T& _y) : x(_x), y(_y) { }
>   }
>
>   test()
>   {
> comparable m = max<comparable>(3.0, 2);
> double md = max<comparable>(3.0, 2); // would this work?
>   }

Yes, since all the types in comparable promote to double.

Another alternative would be

 template <class T> T max(T x, T y);

 #include <dunion.h>
 typedef Dunion<comparable> Comparable;

 test() {
  Comparable m = max(Comparable(3.0), Comparable(2));
  double md = max(Comparable(3.0), Comparable(2)).value();
 }

--
Fergus Henderson                     This .signature virus might be
fjh@munta.cs.mu.OZ.AU                getting old, but you still can't
                                     consistently believe it unless you
Linux: Choice of a GNU Generation    copy it to your own .signature file!