Boost logo

Boost :

Subject: Re: [boost] [type trait extension] I hate volatile...
From: Jeffrey Lee Hellrung, Jr. (jeffrey.hellrung_at_[hidden])
Date: 2011-07-17 08:21:36


2011/7/15 Frédéric Bron <frederic.bron_at_[hidden]>

> I am still working on the implementation of the traits to detect operators.
> I have an issue with operators returning a volatile object (being a
> reference or not). This breaks the code I wrote to detect if the
> return type is void or not.
> In particular, the following code compiles with RET=int but not with
> RET=ret. Can somebody tell me why?
>
> It seems that any volatile object (reference or not) cannot be bound
> to a const reference apart for fundamental types. So is there any
> useful case to return a volatile object?
> If not, I can maybe just ignore those cases or it becomes to be a
> nightmare. By the way, it is 1:30am. I am going to bed!
>

I'm surprised by this; I've attached what I believe to be a more complete
test that compiles on MSVC9, and all the commented-out lines fail to
compile. Indeed, we can make a little table to summarize what's allowed by
MSVC9 and what isn't (best when viewed in a fixed-width font). Here, int_c,
int_v, and int_cv are int const, int volatile, and int const volatile,
respectively, and similarly for X. The table shows which expressions g< To
>::_(f< From >()) are allowed, where

template< class T > T f();
template< class T >
struct g
{
    static int _(T);
    static void _(...);
};

I use the above funky definition of g to (try to) prevent MSVC from binding
rvalues to lvalues(-to-non-const).

To\From | int int_c int_v int_cv int& int_c& int_v& int_cv&
int | + + + + + + + +
int_c | + + + + + + + +
int_v | + + + + + + + +
int_cv | + + + + + + + +
int& | - - - - + - - -
int_c& | + + - - + + - -
int_v& | - - - - + - + -
int_cv& | - - - - + + + +

To\Fr | X X_c X_v X_cv X& X_c& X_v& X_cv&
X | + + - - + + - -
X_c | + + - - + + - -
X_v | + + - - + + - -
X_cv | + + - - + + - -
X& | - - - - + - - -
X_c& | + + - - + + - -
X_v& | + - + - + - + -
X_cv& | + + + + + + + +

The important things I see are:
(a) Any qualification (cv or &) of an int can be passed to a function
accepting an int by-value, but only at most combinations of const and
reference qualifications of X can be passed to a function accepting an X
by-value. I.e., volatile qualification anywhere seems to preclude
pass-by-value for class types. In both cases, the cv-qualification of the
by-value function parameter makes no difference.
(b) It looks like pass-by-reference(-to-non-cv) and
pass-by-reference-to-const is consistent across fundamental and class types.
(c) An int volatile & function parameter will only bind to an int& or int
volatile & expression (which makes sense), but an X volatile & function
parameter will *additionally* bind to X rvalue and X volatile rvalue
expressions! This seems wacky...
(d) An int const volatile & function parameter will only bind to an lvalue
expression (hmmm...), but an X const volatile & function parameter will bind
to any expression (of type X after removing cv and reference
qualifications).

I don't know if the above table helps you or not, and I don't know what its
results are for, e.g., GCC, but I agree that volatile qualification on
return types *and* by-reference parameter types seems to give surprising
results.

What does your code look like that detects void types? I use the following
EXPR_IS_VOID macro myself (which is not originally mine, but I think I have
modified it from the original after some testing); feel free to pilfer. The
double comma operator thingy is intended to work even if the type of ( Expr
) has an overloaded comma operator, but I've found that GCC has issues with
this construct, hence the two versions.

----------------
#ifdef __GNUC__

#define EXPR_IS_VOID( Expr ) \
    EXPR_IS_CONVERTIBLE( \
        ( ( Expr ), ::detail_expr_is_void::void_detector_t() ), \
        ::detail_expr_is_void::void_detector_t \
    )

#else // #ifdef __GNUC__

#define EXPR_IS_VOID( Expr ) \
    EXPR_IS_CONVERTIBLE( \
        ( ::detail_expr_is_void::void_detector_t(), \
          ( Expr ), \
          ::detail_expr_is_void::void_detector_t() ), \
        ::detail_expr_is_void::void_detector_t \
    )

#endif // #ifdef __GNUC__

namespace detail_expr_is_void
{

struct any_from_t { any_from_t(...); };

struct non_void_t;

struct void_detector_t
{
#ifdef __GNUC__
    template< class T > friend non_void_t operator,(const T&,
void_detector_t);
    template< class T > friend non_void_t operator,(const volatile T&,
void_detector_t);
#else // #ifdef __GNUC__
    non_void_t operator,(any_from_t) const;
#endif // #ifdef __GNUC__
};

struct non_void_t { non_void_t operator,(void_detector_t) const; };

} // namespace detail_expr_is_void
----------------

where the EXPR_IS_CONVERTIBLE macro may be defined as (this is simplified
from how it's actually defined but is sufficient for use in EXPR_IS_VOID
above)

----------------
#define EXPR_IS_CONVERTIBLE( FromExpr, ToType ) \
    ( sizeof( ::detail_expr_is_convertible::is_convertible_helper< ToType
>::apply( FromExpr ) ) \
      == sizeof( ::detail_expr_is_convertible::yes_type ) )

namespace detail_expr_is_convertible
{

template< std::size_t N > struct sizeof_t { char _dummy[N]; };
typedef sizeof_t<1> yes_type;
typedef sizeof_t<2> no_type;

template< class T >
struct is_convertible_helper
{
    static yes_type apply(T);
    static no_type apply(...);
};

} // namespace detail_expr_is_convertible
----------------

It looks like I get correct results on MSVC9 regardless of the cv and
reference qualification, and regardless of the "class'ness" or
"fundamental'ness", on the expression type passed to EXPR_IS_VOID.

HTH,

- Jeff




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