Boost logo

Boost :

Subject: Re: [boost] [chrono] [dependence of common_type on Boost.TypeOf]
From: Jeffrey Lee Hellrung, Jr. (jhellrung_at_[hidden])
Date: 2010-08-16 02:23:43


On 8/14/2010 6:43 AM, vicente.botet wrote:
> ----- Original Message -----
> From: "vicente.botet"<vicente.botet_at_[hidden]>
> To:<boost_at_[hidden]>
> Sent: Saturday, August 14, 2010 1:15 PM
> Subject: Re: [boost] [chrono] [dependence of common_type on Boost.TypeOf]
> Hi Jeff,
>
> Yes I see now better what was your idea. Have you tried to implement it?

I have. Unfortunately, 1) it will take some effort to remove
dependencies on other in-house utilities; 2) my implementation is quite
a bit more complex; and 3) the extra complexity is probably necessary.
I will explain below.

> I will try the approach to see what I can get, but I'm not sure to been able to manage with the ambiguous overloads.

I will comment on this below, as well.

> Howard, what do you think about this approach? Have you already tried something like that?
>
> Thanks,
> Vicente
> _______________________________________________
>
> Hi again,
>
> I have reached to manage with the ambiguous overloads and all the tests works as before.
>
> I have do it as follows:
[...template code removed...]
> #if 0
> public:
> typedef BOOST_TYPEOF_TPL(m_f() ? m_t() : m_u()) type;
> #else
> typedef char (&yes)[1];
> typedef char (&no)[2];
> static yes deduce(T);
> static no deduce(U);
> public:
> typedef typename mpl::if_c< sizeof( deduce(m_f() ? m_t() : m_u()) ) == sizeof( yes ), T, U>::type type;
>
> #endif
[...more template code removed...]

As far as I know, this will generally only give the right result of
common_type<T,U> for non-reference and non-pointer T's and U's.
Unfortunately, it won't take care of cases such as common_type< int
const *, int volatile * > -> int const volatile *. Reference types are
even trickier, as their common_type could be either an lvalue reference
(this is similar to the pointer case) or an rvalue.

Here is a summary of the logic I use.

For concreteness, we wish to compute common_type<T,U>, which is the type
of the "conditional expression" (*), (bool_() ? t() : u()), where
bool_() has type bool, t() has return type T, and u() has return type U.
  For simplicity, assume that cv qualifiers have already been stripped
from T and U, as they don't affect the type of common_type<T,U>.

First, we obtain a set of "candidate types", with the property that
common_type<T,U> is among these candidate types. Once we have a finite
set of candidate types, we can apply an extension of the "sizeof trick"
(used above for deduce) to determine which of the candidates is the
actual desired type. The set of candidate types will be constructed to
make it easy to avoid ambiguous overloads, since we will remove
duplicates, and either all candidate types will be reference types or
all candidate types will be non-reference (and non-cv-qualified) types.

To determine these candidate types, the logic goes roughly as follows
(apply in order):
(1) If T and U are reference types, say, T = T' & and U = U' &: If the
conditional expression (*) is an rvalue, go on to (2) (this is a
property that can be determined via another "sizeof trick"). Otherwise,
the set of candidate types is { T' &, U' &, T' [cvU'] &, U' [cvT'] & };
where [cvX'] are the cv-qualifiers on X'; and assume duplicates are removed.
(2) If T and U (after stripping any reference and cv qualifiers), are
pointer types, say, T = T' * and U = U' *: The set of candidate types
is { T' *, U' *, T' [cvU'] *, U' [cvT'] * }; where [cvX'] is as before;
and assume duplicates are removed.
(3) If T and U (after stripping any reference and cv qualifiers) are
both builtin integral types: The set of candidate types is { T, U,
make_signed(T), make_signed(U), make_unsigned(T), make_unsigned(U) },
with duplicates removed. This should catch any transfers of signed-ness
or unsigned-ness.
(4) If none of the above, then the set of candidate types is simply { T,
U } (again, duplicates are removed, and these are the types after
removal of reference and cv qualifiers).

Case (4) is the case you've implemented in the code above, but (I think)
it really should be the least prioritized case.

I haven't rigorously tested this logic out, so I don't know if the
preceding is a complete description of the conditional operator, but I
think it should be pretty close. What do you think?

- Jeff


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk