From 6b1dd5181818156023fa161c0abd96ff24d8ad2a Mon Sep 17 00:00:00 2001 From: Andrey Semashev Date: Thu, 12 Jun 2014 19:23:36 +0400 Subject: [PATCH] Added low level tools for demangling. Requested by Boost.TypeIndex author. --- doc/demangle.qbk | 120 ++++++++++++++++++++++++-------- include/boost/core/demangle.hpp | 86 +++++++++++++++-------- test/demangle_test.cpp | 42 ++++++++++- 3 files changed, 189 insertions(+), 59 deletions(-) diff --git a/doc/demangle.qbk b/doc/demangle.qbk index 0255bfb..16bd65e 100644 --- a/doc/demangle.qbk +++ b/doc/demangle.qbk @@ -1,5 +1,6 @@ [/ Copyright 2014 Peter Dimov + Copyright 2014 Andrey Semashev Distributed under the Boost Software License, Version 1.0. @@ -18,46 +19,107 @@ [section Header ] -The header `` defines the function -`boost::core::demangle`. It takes a mangled string such as -those returned by `typeid(T).name()` on certain implementations -such as `g++`, and returns its demangled, human-readable, form. +The header `` defines several tools for undecorating +symbol names. [section Synopsis] -`` -namespace boost -{ + namespace boost + { -namespace core -{ - std::string demangle( char const * name ); -} + namespace core + { + std::string demangle( char const * name ); -} -`` + char const * demangle_alloc( char const * name ) noexcept; + void demangle_free( char const * demangled_name ) noexcept; + + class scoped_demangled_name + { + public: + explicit scoped_demangled_name( char const * name ) noexcept; + ~scoped_demangled_name() noexcept; + char const * get() const noexcept; + + scoped_demangled_name( scoped_demangled_name const& ) = delete; + scoped_demangled_name& operator= ( scoped_demangled_name const& ) = delete; + }; + } + + } [endsect] +[section Conventional interface] + +The function `boost::core::demangle` is the conventional +way to obtain demangled symbol name. It takes a mangled string such as +those returned by `typeid(T).name()` on certain implementations +such as `g++`, and returns its demangled, human-readable, form. In case if +demangling fails (e.g. if `name` cannot be interpreted as a mangled name) +the function returns `name`. + [section Example] -`` -#include -#include -#include + #include + #include + #include -template struct X -{ -}; + template struct X + { + }; -int main() -{ - char const * name = typeid( X ).name(); - - std::cout << name << std::endl; // prints 1XIiE - std::cout << boost::core::demangle( name ) << std::endl; // prints X -} -`` + int main() + { + char const * name = typeid( X ).name(); + + std::cout << name << std::endl; // prints 1XIiE + std::cout << boost::core::demangle( name ) << std::endl; // prints X + } + +[endsect] + +[endsect] + +[section Low level interface] + +In some cases more low level interface may be desirable. For example: + +* Assuming that symbol demangling may fail, the user wants to be able to handle such errors. +* The user needs to post-process the demangled name (e.g. remove common namespaces), and +allocating a temporary string with the complete demangled name is significant overhead. + +The function `boost::core::demangle_alloc` performs name demangling and returns a pointer +to a string with the demangled name, if succeeded, or `nullptr` otherwise. The returned pointer +must be passed to `boost::core::demangle_free` to reclaim resources. Note that on some platforms +the pointer returned by `boost::core::demangle_alloc` may refer to the string denoted by `name`, +so this string must be kept immutable for the whole life time of the returned pointer. + +The `boost::core::scoped_demangled_name` class is a scope guard that automates the calls to +`boost::core::demangle_alloc` (on its construction) and `boost::core::demangle_free` (on destruction). +The string with the demangled name can be obtained with its `get` method. Note that this method may +return `nullptr` if demangling failed. + +[section Example] + + #include + #include + #include + + template struct X + { + }; + + int main() + { + char const * name = typeid( X ).name(); + boost::core::scoped_demangled_name demangled( name ); + + std::cout << name << std::endl; // prints 1XIiE + std::cout << (demangled.get() ? demangled.get() : "[unknown]") << std::endl; // prints X + } + +[endsect] [endsect] @@ -67,7 +129,7 @@ int main() The implementation of `core::demangle` was taken from `boost/exception/detail/type_info.hpp`, which in turn was adapted -from `boost/units/detail/utility.hpp`. +from `boost/units/detail/utility.hpp` and `boost/log/utility/type_info_wrapper.hpp`. [endsect] diff --git a/include/boost/core/demangle.hpp b/include/boost/core/demangle.hpp index 556ebb3..67cad8a 100644 --- a/include/boost/core/demangle.hpp +++ b/include/boost/core/demangle.hpp @@ -1,15 +1,10 @@ #ifndef BOOST_CORE_DEMANGLE_HPP_INCLUDED #define BOOST_CORE_DEMANGLE_HPP_INCLUDED -// MS compatible compilers support #pragma once - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -# pragma once -#endif - // core::demangle // // Copyright 2014 Peter Dimov +// Copyright 2014 Andrey Semashev // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at @@ -18,6 +13,10 @@ #include #include +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif + #if defined( __clang__ ) && defined( __has_include ) # if __has_include() # define BOOST_CORE_HAS_CXXABI_H @@ -38,34 +37,56 @@ namespace boost namespace core { +char const * demangle_alloc( char const * name ) BOOST_NOEXCEPT; +void demangle_free( char const * name ) BOOST_NOEXCEPT; + +class scoped_demangled_name +{ +private: + char const * m_p; + +public: + explicit scoped_demangled_name( char const * name ) BOOST_NOEXCEPT : + m_p( demangle_alloc( name ) ) + { + } + + ~scoped_demangled_name() BOOST_NOEXCEPT + { + demangle_free( m_p ); + } + + char const * get() const BOOST_NOEXCEPT + { + return m_p; + } + + BOOST_DELETED_FUNCTION(scoped_demangled_name( scoped_demangled_name const& )) + BOOST_DELETED_FUNCTION(scoped_demangled_name& operator= ( scoped_demangled_name const& )) +}; + + #if defined( BOOST_CORE_HAS_CXXABI_H ) -// lifted from boost/exception/detail/type_info.hpp +inline char const * demangle_alloc( char const * name ) BOOST_NOEXCEPT +{ + int status = 0; + std::size_t size = 0; + return abi::__cxa_demangle( name, NULL, &size, &status ); +} + +inline void demangle_free( char const * name ) BOOST_NOEXCEPT +{ + std::free( const_cast< char* >( name ) ); +} inline std::string demangle( char const * name ) { - struct auto_free + scoped_demangled_name demangled_name( name ); + char const * const p = demangled_name.get(); + if( p ) { - explicit auto_free( char * ptr ): p( ptr ) - { - } - - ~auto_free() - { - std::free( p ); - } - - char * p; - }; - - int status = 0; - std::size_t size = 0; - - auto_free demangled( abi::__cxa_demangle( name, NULL, &size, &status ) ); - - if( demangled.p ) - { - return demangled.p; + return p; } else { @@ -75,6 +96,15 @@ inline std::string demangle( char const * name ) #else +inline char const * demangle_alloc( char const * name ) BOOST_NOEXCEPT +{ + return name; +} + +inline void demangle_free( char const * ) BOOST_NOEXCEPT +{ +} + inline std::string demangle( char const * name ) { return name; diff --git a/test/demangle_test.cpp b/test/demangle_test.cpp index a54e743..ca12608 100644 --- a/test/demangle_test.cpp +++ b/test/demangle_test.cpp @@ -2,6 +2,7 @@ // Trivial test for core::demangle // // Copyright (c) 2014 Peter Dimov +// Copyright (c) 2014 Andrey Semashev // // Distributed under the Boost Software License, Version 1.0. // See accompanying file LICENSE_1_0.txt or copy at @@ -15,9 +16,46 @@ template struct Y1 { }; -int main() +void test_demangle() { typedef Y1 T; - std::cout << boost::core::demangle( typeid( T ).name() ); + std::cout << boost::core::demangle( typeid( T ).name() ) << std::endl; +} + +void test_demangle_alloc() +{ + typedef Y1 T; + const char* p = boost::core::demangle_alloc( typeid( T ).name() ); + if (p) + { + std::cout << p << std::endl; + boost::core::demangle_free(p); + } + else + { + std::cout << "[demangling failed]" << std::endl; + } +} + +void test_scoped_demangled_name() +{ + typedef Y1 T; + boost::core::scoped_demangled_name demangled_name( typeid( T ).name() ); + const char* p = demangled_name.get(); + if (p) + { + std::cout << p << std::endl; + } + else + { + std::cout << "[demangling failed]" << std::endl; + } +} + +int main() +{ + test_demangle(); + test_demangle_alloc(); + test_scoped_demangled_name(); return 0; }