
Hi, What is current state of the art on l-value/r-value detection? (I know Eric made some progress in the past by using the "?" operator). What we have in typeof is quite primitive :-( I think it might be a good idea to eventially factor it out into a separate header file, and reuse, to avoid contradictions between different libs, and provide the single point of maintenance... Regards, Arkadiy

Arkadiy Vertleyb wrote:
FOREACH does rvalue detection that is able to detect even const-qualified rvalues on highly conforming compilers, but there is a serious limitation to the technique: it doesn't yield a compile-time constant. This means it is not applicable to typeof, sadly. The closest I've been able to come to reliable, compile-time detection of rvalues in standard C++ is to see if the expression can be bound to a reference. Naturally, this is foiled by const-qualified rvalues. I think that's what you're already doing in typeof, right? It's an open question whether it's possible to do this in standard C++. If it's possible, I'd be very interested. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Here's my latest attempt. This works on gcc 3.4.4 (cygwin), but not on VC7.1 or (sadly) Comeau Online. Language lawyers, start your engines! template<typename T> struct probe { operator T (); operator T volatile const &() const; }; template<typename T> probe<T> make_probe(T const &); template<typename T> char is_volatile(T const &); template<typename T> double is_volatile(T const volatile &); #define IS_RVALUE(x) \ (\ sizeof(char) == \ sizeof(is_volatile(true?make_probe(x):(x)))\ ) // tests struct foo { foo() {} }; foo const make_foo() { return foo(); } int main() { int i; foo const f1; foo f2; char t1[!IS_RVALUE(f1)]; char t2[!IS_RVALUE(f2)]; char t3[IS_RVALUE(foo())]; char t4[IS_RVALUE(make_foo())]; char t5[!IS_RVALUE(i)]; char t6[IS_RVALUE(1)]; return 0; } -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Eric Niebler wrote:
Here's my latest attempt. This works on gcc 3.4.4 (cygwin), but not on VC7.1 or (sadly) Comeau Online. Language lawyers, start your engines!
Eric, this is very cool :-) I tried to simplify it a little:
template<typename T> struct probe { operator T (); operator T&() const; };
template<typename T> char is_volatile( const T& ); template<typename T> double is_volatile( T& ); That gave the same results. Then I did
template<typename T> probe<T> make_probe(T const &);
template<typename T> const probe<T> make_probe(T const &); Now it works on vc7.1 too. Not como. I'm not a language specialist and my understanding of ?: comes mainly from your article. That said, I find the solution very intuitive. best regards Thorsten

Thorsten Ottosen wrote:
Eric Niebler wrote:
Here's my latest attempt. This works on gcc 3.4.4 (cygwin), but not on VC7.1 or (sadly) Comeau Online. Language lawyers, start your engines!
Now it works on vc7.1 too. Not como.
Btw, the error on como is down to one, name the line char t1[!IS_RVALUE(f1)]; (remember to compile with --a or --A) To unconfuse the compiler, one might try to add another utility: template< class T > struct ref_probe { operator T&(); }; template<class T > ref_probe<T> make_probe( T& ); now the conversions from probe<T> should only happen for const T& and T(). -Thorsten

Thorsten Ottosen wrote:
I noticed this too, and decided it was a bug in gcc. I think that given template<typename T> struct conv { operator T() { return T(); } }; template<typename T> void bar(T&) {} the expression: typedef foo const cfoo; bar(true ? conv<foo>() : cfoo()); should compile, and that in bar, T should be deduced at "foo const". With gcc, it does *not* compile because the expression "true ? conv<foo>() : cfoo()" appears to have the type "rvalue of foo" instead of "rvalue of foo const". gcc is inadvertantly dropping cv-modifiers where it shouldn't. But we're certainly free to use that to our advantage. :-)
Whoa. If the probe is const-qualified, how can the non-const member ever get called on it? Seems like you've found a compiler bug. I would test with Comeau Online, but it's not responding at the moment. Anyway, getting this to work with vc7.1 is a major advance. Thanks.
I'm not a language specialist and my understanding of ?: comes mainly from your article. That said, I find the solution very intuitive.
If we can make this work on the popular compilers, it'll be a big win for both boost.foreach and boost.typeof. It doesn't matter if we need to take advantage of compiler bugs to get it. I wonder if other compilers and other versions of gcc accept either my variant or yours. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Thorsten Ottosen wrote:
Even though bar takes a T& and not const T& parameter?
sure.
Good question. It seems to me that conversion operators are a poorly understood part of the language. Maybe you should take this up on the reflector. Another thing I find wierd is that this seems to lead to ambiguity: template< class T > struct probe { operator T&(); operator T(); }; int main() { int i = probe<int>(); // error int& r = probe<int>(); // ok } If I add const to the T() conversion, it suddenly works ok again. -Thorsten

Thorsten Ottosen wrote:
Yes. The type of (true ? conv<foo>() : cfoo()) is "rvalue of foo const." And const rvalues successfully bind to T&, with T deduced as "foo const."
It seems to me that conversion operators are a poorly understood part of the language. Maybe you should take this up on the reflector.
Just because you and I don't understand this completely doesn't mean it's poorly understood by everyone. :-)
Yes, that's because for the first line, there are two ways for the conversion to succeed. It could call operator T(), or it could call operator T&() followed by the standard lvalue-to-rvalue conversion. Overload resolution is needed to choose between them, and without the const qualification, neither is preferred. With the const, an extra standard non-const-to-const conversion is needed before operator T&()const can be invoked, which makes that conversion sequence longer and less preferred. HTH, -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
well, if you call bar(1), it won't compile. what makes this different? I'm also confused by the fact that the above conversion operator can add a const to T, that is to say, T is specified as foo, not const foo, and so the generated class would look like struct conv_foo { operator foo() { return foo(); } }; I just don't get it :-( -Thorsten

Thorsten Ottosen wrote:
The type of 1 is "rvalue of int," not "rvalue of int const."
I'm also confused by the fact that the above conversion operator can add a const to T
It's not adding a const. Look closely: true ? conv<foo>() : cfoo() ^^^^ Now look above it: "typedef foo const cfoo". So the type of the 3rd operand is "rvalue of foo const". The 2nd operand gets converted to "rvalue of foo" and then picks up the "const" from the 3rd operand. Make sense now? -- Eric Niebler Boost Consulting www.boost-consulting.com

"Eric Niebler" <eric@boost-consulting.com> writes: [...] | > well, if you call bar(1), it won't compile. what makes this different? | | | The type of 1 is "rvalue of int," not "rvalue of int const." [...] | Now look above it: "typedef foo const cfoo". So the type of the 3rd | operand is "rvalue of foo const". The 2nd operand gets converted to ^^^^^^^^^ | "rvalue of foo" and then picks up the "const" from the 3rd operand. | | Make sense now? A bit confusing :-) [there is no type "rvalue of ..." as you said earlier]

"Eric Niebler" <eric@boost-consulting.com> writes: | Gabriel Dos Reis wrote: | > "Eric Niebler" <eric@boost-consulting.com> writes: | > | > | Now look above it: "typedef foo const cfoo". So the type of the 3rd | > | operand is "rvalue of foo const". The 2nd operand gets converted to | > ^^^^^^^^^ | > | > [there is no type "rvalue of ..." as you said earlier] | > | | Thanks for the correction. So, the above would be more properly stated, | "the 3rd operand is an rvalue of type foo const," is that right? Yup.

You can help improve Boost.Foreach and Boost.Typeof for your favorite compiler! Read on. Eric Niebler wrote:
I am now using the compile-time-const-rvalue-detection trick in the new-and-improved BOOST_FOREACH. For the first time, it's passing its tests at 100% for VC7.1 and VC8. Also, const rvalues are being detected on gcc 3.4+ with zero runtime overhead. Very nice! OK, now here's a simple thing anybody can do to improve Foreach and Typeof. Try compiling the attached file on your favorite compiler and report the results. I already know it works for VC7.1+ and gcc 3.4+. Everything else is up for grabs. Extra credit #1: If it doesn't compile for you, patch it so it does. :-) Extra credit #2: Find a formulation that is 100% standard compliant. Back up your claim by citing references from the standard. :-))) -- Eric Niebler Boost Consulting www.boost-consulting.com // Copyright 2005 Eric Niebler. // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include <boost/config.hpp> #include <boost/mpl/if.hpp> #include <boost/mpl/logical.hpp> #include <boost/type_traits/is_array.hpp> namespace impl_ { typedef char yes_type; typedef char (&no_type)[2]; yes_type is_true(boost::mpl::true_ *); no_type is_true(boost::mpl::false_ *); #define PROTECT(expr) \ (static_cast<boost::mpl::bool_<1 == sizeof(impl_::is_true(expr))> *>(0)) template<typename Bool1, typename Bool2> inline boost::mpl::and_<Bool1, Bool2> *and_(Bool1 *, Bool2 *) { return 0; } template<typename Bool> inline boost::mpl::not_<Bool> *not_(Bool *) { return 0; } template<typename T> inline boost::mpl::false_ *is_rvalue(T &, int) { return 0; } template<typename T> inline boost::mpl::true_ *is_rvalue(T const &, ...) { return 0; } template<typename T> inline boost::is_array<T> *is_array(T const &) { return 0; } template<typename T> struct rvalue_probe { // can't ever return an array by value typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_<boost::is_array<T>, int, T>::type value_type; operator value_type(); operator T &() const; }; template<typename T> rvalue_probe<T> const make_probe(T const &t); } // namespace impl_ #define IS_RVALUE_IMPL(COL) \ impl_::and_(impl_::not_(impl_::is_array(COL)) \ , PROTECT(impl_::is_rvalue((true ? impl_::make_probe(COL) : (COL)), 0))) #define IS_RVALUE(COL) (1==sizeof(impl_::is_true(IS_RVALUE_IMPL(COL)))) ///////////////////////////////////////////////////////////////////////// // test begins here // struct foo { foo() {} }; foo const make_foo() { return foo(); } int main() { int i = 0; int const ci = 0; char test1[ IS_RVALUE(1) ]; char test2[ IS_RVALUE(int()) ]; char test3[ ! IS_RVALUE(i) ]; char test4[ ! IS_RVALUE(ci) ]; foo f; foo const cf; typedef foo const cfoo; char test5[ IS_RVALUE(foo()) ]; char test6[ IS_RVALUE(cfoo()) ]; char test7[ IS_RVALUE(make_foo()) ]; char test8[ ! IS_RVALUE(f) ]; char test9[ ! IS_RVALUE(cf) ]; int rgi[2] = {1,2}; int const crgi[2] = {1,2}; char test10[ ! IS_RVALUE(rgi) ]; char test11[ ! IS_RVALUE(crgi) ]; return 0; }

At 11:34 AM -0800 12/28/05, Eric Niebler wrote:
Mac OS X 10.4 --> gcc 4.0.1, boost 1.33.0 compiles w/o errors. [ Runs w/o output, too! ;-) ] -- -- Marshall Marshall Clow Idio Software <mailto:marshall@idio.com> It is by caffeine alone I set my mind in motion. It is by the beans of Java that thoughts acquire speed, the hands acquire shaking, the shaking becomes a warning. It is by caffeine alone I set my mind in motion.

Eric Niebler wrote:
cxx on Tru64:
cxx -version V7.1-006 -std strict_ansi -model ansi -I/vol2/boost/src/boost-HEAD is_rvalue.cpp
cxx: Error: is_rvalue.cpp, line 69: the size of an array must be greater than zero char test4[ ! IS_RVALUE(ci) ]; ----------------^ cxx: Error: is_rvalue.cpp, line 76: the size of an array must be greater than zero char test6[ IS_RVALUE(cfoo()) ]; ------------------^ cxx: Error: is_rvalue.cpp, line 77: the size of an array must be greater than zero char test7[ IS_RVALUE(make_foo()) ]; ------------------^ cxx: Info: 3 errors detected in the compilation of "is_rvalue.cpp". Compilation exited abnormally with code 1 at Thu Dec 29 08:31:45 Markus

On Dec 28, 2005, at 8:34 PM, Eric Niebler wrote:
IBM Visual Age C++ version 6 and 7 gives the following compile time error: line 68.17: 1540-0135 (S) The array bound cannot be zero. Adding 1 to all the array sizes gives the following error: line 22.40: 1540-0704 (S) The definitions of "boost::mpl::and_<boost::mpl::not_<boost::is_array<foo>
I'll do that when I find some time. Matthias

Eric Niebler wrote:
You can help improve Boost.Foreach and Boost.Typeof for your favorite compiler! Read on.
Well typeof is already native and working fine for CW (8.3) :-)
Doesn't work at all with CW-8.3. With some mods to get some better error messages (attached): is_rvalue.cpp:76: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:77: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:78: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:79: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:90: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:91: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:92: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:93: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:94: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:101: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' is_rvalue.cpp:102: illegal use of incomplete struct/union/class 'impl_::rvalue<2, 0>' ...Without the mods it just says "illegal constant expression" a bunch of times :-) -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - Grafik/jabber.org

"Rene Rivera" <grafik.list@redshift-software.com> wrote
Right, but you can't use native typeof to develop portable code on this compiler. You would have to use emulation mode. Besides, Eric actualy means LVALUE_TYPEOF (something that tries to approximate decltype), and this functionality is built on top of typeof, whether native or emulation. Regards, Arkadiy

On Dec 17, 2005, at 12:17 AM, Eric Niebler wrote:
That's very cool! Just as a thought experiment, I tried to re-implement your IS_RVALUE macro in (what I hope to be) C++0X. My purpose was mainly just to amuse myself, but also as a sanity check that C++0X will actually make things like this easier. I'm aware that the motivation for IS_RVALUE is also greatly reduced in C++0X. I'm just exploring. Fwiw I came up with: #include <type_traits> template<typename T> T&& test(T&&); #define IS_RVALUE(x) (std::is_rvalue_reference<decltype(test(x))
::value)
I only spent a couple of minutes on it, so I wouldn't take it to the bank. But it is lightly tested. -Howard

On 12/17/05 12:17 AM, "Eric Niebler" <eric@boost-consulting.com> wrote:
Other versions of the "choose via 'sizeof' comparisons" trick, including ones I've seen in Boost, use types like "char[1]" and "char[2]". Why? Because there is no ban on _all_ built-in types being the same size(*). So the assumption of "sizeof(char) != sizeof(double)" can fail. * Which would have to be at least 32 bits (or 64 in C99). -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com
participants (11)
-
Arkadiy Vertleyb
-
Daryle Walker
-
Edward Diener
-
Eric Niebler
-
Gabriel Dos Reis
-
Howard Hinnant
-
Markus Schöpflin
-
Marshall Clow
-
Matthias Troyer
-
Rene Rivera
-
Thorsten Ottosen