[Boost.Test] Use of numeric_limits in print_log_value (VC++7.1)

I've encountered a problem when transitioning from Boost.Test 1.32 to 1.33. New to 1.33 is a set_precision(..) method on the print_log_value functor (in test_tools.hpp). The problem, as I see it, is the use of numeric_limits<T>, which exposes the assumption T is a numeric entity at all. I'm actually surprised it compiles as often as it does, so maybe my blame is misdirected. Nonetheless, I was able to create the attached test case to prove my point. I've also included the generated build log with the really nasty compile time error. Anm // Weird Compile problem with Boost.Test 1.33 #include <iostream> #include <boost/test/unit_test.hpp> using boost::unit_test::test_suite; // Class heirarchy and support functions class Abstract { virtual void go() = 0; // If Abstract is not pure virtual, everything compiles fine }; class Concrete : public Abstract { virtual void go() { } }; inline bool operator== ( const Abstract& a, const Abstract& b ) { return &a == &b; // identity } template <class C, class T > // character type, traits inline std::basic_ostream<C,T>& operator<< ( std::basic_ostream<C,T>& out, const Abstract& a ) { // Just prints a memory address. return out << "Abstract@0x" << &a; } // Function that forces us to work with the abstract interface Abstract& generalize( Concrete& c ) { return c; } // Test case & suite void test_abstract_identity() { Concrete a, b; // These work BOOST_CHECK_EQUAL( a, a ); // works BOOST_CHECK_EQUAL( a, b ); // fails correctly // These won't compile with 1.33 & VC++7.1 BOOST_CHECK_EQUAL( generalize(a), a ); Abstract& c = a; BOOST_CHECK_EQUAL( a, c ); } test_suite* init_unit_test_suite( int, char* [] ) { test_suite* test= BOOST_TEST_SUITE( "Example failure of Boost.Test 1.33.0" ); test->add( BOOST_TEST_CASE( &test_abstract_identity ), 1 ); return test; } // EOF

On 12/10/05 10:55 PM, "Andrew n marshall" <amarshal@ISI.EDU> wrote:
I've encountered a problem when transitioning from Boost.Test 1.32 to 1.33. New to 1.33 is a set_precision(..) method on the print_log_value functor (in test_tools.hpp). The problem, as I see it, is the use of numeric_limits<T>, which exposes the assumption T is a numeric entity at all. I'm actually surprised it compiles as often as it does, so maybe my blame is misdirected. Nonetheless, I was able to create the attached test case to prove my point. I've also included the generated build log with the really nasty compile time error. [TRUNCATE Mr. Marshall's code example]
Your blame may be misdirected. Looking at the Boost.Test code: template<typename T> struct print_log_value { void operator()( std::ostream& ostr, T const& t ) { typedef typename mpl::or_<is_array<T>,is_function<T> >::type couldnt_use_nl; set_precision( ostr, couldnt_use_nl() ); ostr << t; // by default print the value } void set_precision( std::ostream& ostr, mpl::false_ ) { if( std::numeric_limits<T>::is_specialized && std::numeric_limits<T>::radix == 2 ) ostr.precision( 2 + std::numeric_limits<T>::digits * 301/1000 ); } void set_precision( std::ostream&, mpl::true_ ) {} }; it uses std::numeric_limits<>::is_specialized to make sure that the precision is set only for numeric types. Otherwise, that function does nothing. The functor's operator() splits the code according to type, and types that don't even make it to the set_precision you're talking about also do nothing. Actually, since the is_specialized field should be a compile-time constant, the code should be shifted to compile-time even further: template<typename T> struct print_log_value { void operator()( std::ostream& ostr, T const& t ) { typedef typename mpl::or_<is_array<T>,is_function<T>, mpl::bool_<!std::numeric_limits<T>::is_specialized> >::type couldnt_use_nl; set_precision( ostr, couldnt_use_nl() ); ostr << t; // by default print the value } void set_precision( std::ostream& ostr, mpl::false_ ) { ostr.precision( 2 + std::numeric_limits<T>::digits * 301/1000 ); } void set_precision( std::ostream&, mpl::true_ ) {} }; Of course, this code assumes that std::numeric_limits<>::radix is 2. The 301/1000 is a logarithm conversion factor to turn a power of 2 into a power of 10, with two extra digits for slop. This lets us print out a number to all of its defined digits in decimal. Maybe more compile-time conditions should be added to confirm those assumptions. Any type that doesn't pass the test is assumed to be non-numeric and hopefully will print out enough of its state to be unambiguous without setting the precision. As far as your code is concerned, I think it should work. You always should be able to convert pointers/references to a base class, even if it's abstract. And you should be able to work/channel through those pointers and references. The only thing pure-virtual member functions block is creating an object directly of the abstract type. But I don't see any slice & copy action here. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

I've encountered a problem when transitioning from Boost.Test 1.32 to 1.33. New to 1.33 is a set_precision(..) method on the print_log_value functor (in test_tools.hpp). The problem, as I see it, is the use of numeric_limits<T>, which exposes the assumption T is a numeric entity at all. I'm actually surprised it compiles as often as it does, so maybe my blame is misdirected. Nonetheless, I was able to create the attached test case to prove my point. I've also included the generated build log with the really nasty compile time error. [TRUNCATE Mr. Marshall's code example]
Your blame may be misdirected. Looking at the Boost.Test code:
template<typename T> struct print_log_value { void operator()( std::ostream& ostr, T const& t ) { typedef typename mpl::or_<is_array<T>,is_function<T> >::type couldnt_use_nl;
I've added is_abstract<T> to the or_ above to address OP concern. [...]
do nothing. Actually, since the is_specialized field should be a compile-time constant, the code should be shifted to compile-time even further:
template<typename T> struct print_log_value { void operator()( std::ostream& ostr, T const& t ) { typedef typename mpl::or_<is_array<T>,is_function<T>, mpl::bool_<!std::numeric_limits<T>::is_specialized> >::type couldnt_use_nl;
Existent Boost.Test code intentionally avoided code like this. The thing is that for array and function types (and abstract as aparent now) depending on STL/compiler implementation std::numeric_limits<T> instantiation is invalid and you couldn't even query is_specialized. I guess I could've used another compile time if (instead of runtime one) within set_precision. But that shouldn't have any difference at runtime and I do not want to complicate an implementation without compelling reason. Gennadiy
participants (3)
-
Andrew n marshall
-
Daryle Walker
-
Gennadiy Rozental