diff --git a/build/boost/doc/changelog.qbk b/build/boost/doc/changelog.qbk index 4366129..f9c832f 100644 --- a/build/boost/doc/changelog.qbk +++ b/build/boost/doc/changelog.qbk @@ -11,6 +11,7 @@ Not yet released * Fixed potential conflict with macro max * Fixed missing file name and line number in logs * Moved mock::exception in its own header so that it can be included without the error policy +* Fixed type name extraction involving template classes [endsect] diff --git a/test/detail/test_type_name.cpp b/test/detail/test_type_name.cpp index 20bdac1..bc26a9b 100644 --- a/test/detail/test_type_name.cpp +++ b/test/detail/test_type_name.cpp @@ -19,21 +19,57 @@ namespace } } -struct my_type_from_default_namespace {}; - -BOOST_AUTO_TEST_CASE( name_of_type_from_default_namespace_is_extracted ) +BOOST_AUTO_TEST_CASE( name_of_base_type_is_extracted ) { - BOOST_CHECK_EQUAL( "my_type_from_default_namespace", to_string( my_type_from_default_namespace() ) ); + BOOST_CHECK_EQUAL( "char", to_string( 'a' ) ); + BOOST_CHECK_EQUAL( "bool", to_string( true ) ); + BOOST_CHECK_EQUAL( "int", to_string< int >( 0 ) ); + BOOST_CHECK_EQUAL( "short", to_string< short >( 0 ) ); + BOOST_CHECK_EQUAL( "long", to_string< long >( 0 ) ); + BOOST_CHECK_EQUAL( "unsigned int", to_string< unsigned int >( 0 ) ); + BOOST_CHECK_EQUAL( "unsigned short", to_string< unsigned short >( 0 ) ); + BOOST_CHECK_EQUAL( "unsigned long", to_string< unsigned long >( 0 ) ); +} + +struct my_type_in_default_namespace +{ + struct inner {}; +}; + +BOOST_AUTO_TEST_CASE( name_of_type_in_default_namespace_is_extracted ) +{ + BOOST_CHECK_EQUAL( "my_type_in_default_namespace", to_string( my_type_in_default_namespace() ) ); +} + +BOOST_AUTO_TEST_CASE( name_of_inner_type_from_type_in_default_namespace_is_extracted ) +{ + BOOST_CHECK_EQUAL( "inner", to_string( my_type_in_default_namespace::inner() ) ); +} + +template< typename T > +struct my_template_type_in_default_namespace +{ + struct inner {}; +}; + +BOOST_AUTO_TEST_CASE( name_of_template_type_in_default_namespace_is_extracted ) +{ + BOOST_CHECK_EQUAL( "my_template_type_in_default_namespace", to_string( my_template_type_in_default_namespace() ) ); +} + +BOOST_AUTO_TEST_CASE( name_of_inner_type_from_template_type_in_default_namespace_is_extracted ) +{ + BOOST_CHECK_EQUAL( "inner", to_string( my_template_type_in_default_namespace::inner() ) ); } namespace { - struct my_type_from_anonymous_namespace {}; + struct my_type_in_anonymous_namespace {}; } -BOOST_AUTO_TEST_CASE( name_of_type_from_anonymous_namespace_is_extracted ) +BOOST_AUTO_TEST_CASE( name_of_type_in_anonymous_namespace_is_extracted ) { - BOOST_CHECK_EQUAL( "my_type_from_anonymous_namespace", to_string( my_type_from_anonymous_namespace() ) ); + BOOST_CHECK_EQUAL( "my_type_in_anonymous_namespace", to_string( my_type_in_anonymous_namespace() ) ); } namespace nm @@ -50,26 +86,26 @@ namespace nm { namespace inner { - struct my_type_from_named_inner_namespace {}; + struct my_type_in_named_inner_namespace {}; } } -BOOST_AUTO_TEST_CASE( name_of_type_from_named_inner_namespace_is_extracted ) +BOOST_AUTO_TEST_CASE( name_of_type_in_named_inner_namespace_is_extracted ) { - BOOST_CHECK_EQUAL( "my_type_from_named_inner_namespace", to_string( nm::inner::my_type_from_named_inner_namespace() ) ); + BOOST_CHECK_EQUAL( "my_type_in_named_inner_namespace", to_string( nm::inner::my_type_in_named_inner_namespace() ) ); } namespace { namespace inner { - struct my_type_from_unnamed_inner_namespace {}; + struct my_type_in_unnamed_inner_namespace {}; } } -BOOST_AUTO_TEST_CASE( name_of_type_from_unnamed_inner_namespace_is_extracted ) +BOOST_AUTO_TEST_CASE( name_of_type_in_unnamed_inner_namespace_is_extracted ) { - BOOST_CHECK_EQUAL( "my_type_from_unnamed_inner_namespace", to_string( inner::my_type_from_unnamed_inner_namespace() ) ); + BOOST_CHECK_EQUAL( "my_type_in_unnamed_inner_namespace", to_string( inner::my_type_in_unnamed_inner_namespace() ) ); } BOOST_AUTO_TEST_CASE( name_of_local_type_is_extracted ) @@ -77,3 +113,61 @@ BOOST_AUTO_TEST_CASE( name_of_local_type_is_extracted ) struct my_local_type {}; BOOST_CHECK_EQUAL( "my_local_type", boost::lexical_cast< std::string >( mock::detail::type_name( BOOST_SP_TYPEID( my_local_type ) ) ) ); } + +namespace +{ + template< typename T > + struct my_template_type + { + struct inner {}; + }; +} + +BOOST_AUTO_TEST_CASE( name_of_template_type_in_anonymous_namespace_is_extracted ) +{ + BOOST_CHECK_EQUAL( "my_template_type", to_string( my_template_type< int >() ) ); + BOOST_CHECK_EQUAL( "my_template_type", to_string( my_template_type< std::exception >() ) ); +} + +BOOST_AUTO_TEST_CASE( name_of_inner_type_from_template_type_in_anonymous_namespace_is_extracted ) +{ + BOOST_CHECK_EQUAL( "inner", to_string( my_template_type< int >::inner() ) ); + BOOST_CHECK_EQUAL( "inner", to_string( my_template_type< std::exception >::inner() ) ); +} + +namespace nm +{ + template< typename T > + struct my_template_type + { + struct inner {}; + }; +} + +BOOST_AUTO_TEST_CASE( name_of_template_type_in_named_namespace_is_extracted ) +{ + BOOST_CHECK_EQUAL( "my_template_type", to_string( nm::my_template_type< int >() ) ); + BOOST_CHECK_EQUAL( "my_template_type", to_string( nm::my_template_type< std::exception >() ) ); +} + +BOOST_AUTO_TEST_CASE( name_of_inner_type_from_template_type_in_named_namespace_is_extracted ) +{ + BOOST_CHECK_EQUAL( "inner", to_string( nm::my_template_type< int >::inner() ) ); + BOOST_CHECK_EQUAL( "inner", to_string( nm::my_template_type< std::exception >::inner() ) ); +} + +namespace nm2 +{ + template< typename T > + struct my_template_type + { + template< typename U > + struct inner {}; + }; +} + +BOOST_AUTO_TEST_CASE( name_of_inner_type_from_template_type_in_named_namespace_is_extracted2 ) +{ + BOOST_CHECK_EQUAL( "inner", to_string( nm2::my_template_type< int >::inner< int >() ) ); + BOOST_CHECK_EQUAL( "inner", to_string( nm2::my_template_type< std::exception >::inner< int >() ) ); +} diff --git a/turtle/detail/type_name.hpp b/turtle/detail/type_name.hpp index e2b449c..02e470d 100644 --- a/turtle/detail/type_name.hpp +++ b/turtle/detail/type_name.hpp @@ -35,13 +35,16 @@ namespace detail return s; } private: + typedef boost::unit_test::const_string const_string; + typedef const_string::size_type size_type; + void serialize( std::ostream& s, const boost::detail::sp_typeinfo& info ) const { const char* name = info.name(); #ifdef __GNUC__ int status = 0; - char* result = abi::__cxa_demangle( name, NULL, 0, &status ); + char* result = abi::__cxa_demangle( name, 0, 0, &status ); struct guard { explicit guard( char* p ) @@ -49,7 +52,7 @@ namespace detail {} ~guard() { - free( p_ ); + std::free( p_ ); } private: char* p_; @@ -60,22 +63,51 @@ namespace detail #endif serialize( s, name ); } - void serialize( std::ostream& s, - boost::unit_test::const_string name ) const + void serialize( std::ostream& s, const_string name ) const { - boost::unit_test::const_string::size_type p = name.rfind( "::" ); - if( p != boost::unit_test::const_string::npos ) + const size_type tpl_end = name.rfind( ">" ); + const size_type nm = name.rfind( "::" ); + if( tpl_end == const_string::npos || tpl_end <= nm ) { - s << name.substr( p + 2 ); + s << remove_prefix( remove_namespace( name ) ); return; } - p = name.rfind( " " ); - if( p != boost::unit_test::const_string::npos ) + const size_type tpl_start = find_template_start( name ); + s << remove_namespace( name.substr( 0, tpl_start ) ) << '<'; + serialize( s, name.substr( tpl_start + 1, tpl_end ) ); + s << '>'; + } + const_string remove_prefix( const_string name ) const + { + name = remove_prefix( name, "class " ); + name = remove_prefix( name, "struct " ); + return name; + } + const_string remove_prefix( const_string name, + const_string prefix ) const + { + if( name.substr( 0, prefix.size() ) == prefix ) + return name.substr( prefix.size() ); + return name; + } + size_type find_template_start( const_string name ) const + { + size_type count = 0; + for( size_type i = name.size(); i > 0; --i ) { - s << name.substr( p + 1 ); - return; + if( name[ i ] == '>' ) + ++count; + if( name[ i ] == '<' && --count == 0 ) + return i; } - s << name; + return static_cast< size_type >( const_string::npos ); + } + const_string remove_namespace( const_string name ) const + { + size_type p = name.rfind( "::" ); + if( p == const_string::npos ) + return name; + return name.substr( p + 2 ); } const boost::detail::sp_typeinfo* info_;