#ifndef BOOST_CORE_TYPE_NAME_HPP_INCLUDED
#define BOOST_CORE_TYPE_NAME_HPP_INCLUDED

// MS compatible compilers support #pragma once

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

// std::string boost::core::type_name<T>()
//
// Copyright 2021 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/core/demangle.hpp>
#include <boost/config.hpp>
#include <string>
#include <functional>
#include <memory>
#include <utility>
#include <cstdio>
#include <cstddef>
#include <cstring>
#include <iosfwd>
#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)
# include <string_view>
#endif

namespace boost
{
namespace core
{
namespace detail
{

// tn_identity

template<class T> struct tn_identity
{
    typedef T type;
};

// tn_remove_prefix

inline bool tn_remove_prefix( std::string& str, char const* prefix )
{
    std::size_t n = std::strlen( prefix );

    if( str.substr( 0, n ) == prefix )
    {
        str = str.substr( n );
        return true;
    }
    else
    {
        return false;
    }
}

#if !defined(BOOST_NO_TYPEID)

// typeid_name

inline std::string fix_typeid_name( char const* n )
{
    std::string r = boost::core::demangle( n );

#if defined(_MSC_VER)

    tn_remove_prefix( r, "class " );
    tn_remove_prefix( r, "struct " );
    tn_remove_prefix( r, "enum " );

#endif

    // libc++ inline namespace

    if( tn_remove_prefix( r, "std::__1::" ) )
    {
        r = "std::" + r;
    }

    // libstdc++ inline namespace

    if( tn_remove_prefix( r, "std::__cxx11::" ) )
    {
        r = "std::" + r;
    }

#if defined(BOOST_MSVC) && BOOST_MSVC == 1600

    // msvc-10.0 puts TR1 things in std::tr1

    if( tn_remove_prefix( r, "std::tr1::" ) )
    {
        r = "std::" + r;
    }

#endif

    return r;
}

// class types can be incomplete
template<class T> std::string typeid_name_impl( int T::* )
{
    std::string r = fix_typeid_name( typeid(T[1]).name() );
    return r.substr( 0, r.size() - 4 ); // remove ' [1]' suffix
}

template<class T> std::string typeid_name_impl( ... )
{
    return fix_typeid_name( typeid(T).name() );
}

template<class T> std::string typeid_name()
{
    return typeid_name_impl<T>( 0 );
}

// template names

template<class T> std::string class_template_name()
{
#if defined(BOOST_GCC)

    std::string r = typeid_name<T()>();

#else

    std::string r = typeid_name<T*>();

#endif
    return r.substr( 0, r.find( '<' ) );
}

template<class T> std::string sequence_template_name()
{
    return detail::class_template_name<T>();
}

template<class T> std::string set_template_name()
{
    return detail::class_template_name<T>();
}

template<class T> std::string map_template_name()
{
    return detail::class_template_name<T>();
}

template<class T> std::string array_template_name()
{
    return detail::class_template_name<T>();
}

#else // #if !defined(BOOST_NO_TYPEID)

template<class T> std::string typeid_name()
{
    return "_Tp";
}

template<class T> std::string class_template_name()
{
    return "_Tm";
}

template<class T> std::string sequence_template_name()
{
    return "_Sq";
}

template<class T> std::string set_template_name()
{
    return "_St";
}

template<class T> std::string map_template_name()
{
    return "_Mp";
}

template<class T> std::string array_template_name()
{
    return "_Ar";
}

#endif

// tn_to_string

#if defined(BOOST_MSVC)
# pragma warning( push )
# pragma warning( disable: 4996 )
#endif

// Use snprintf if available as some compilers (clang 14.0) issue deprecation warnings for sprintf
#if ( defined(_MSC_VER) && _MSC_VER < 1900 ) || ( defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) )
# define BOOST_CORE_DETAIL_SNPRINTF(buffer, format, arg) std::sprintf(buffer, format, arg)
#else
# define BOOST_CORE_DETAIL_SNPRINTF(buffer, format, arg) std::snprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), format, arg)
#endif

inline std::string tn_to_string( std::size_t n )
{
    char buffer[ 32 ];
    BOOST_CORE_DETAIL_SNPRINTF( buffer, "%lu", static_cast< unsigned long >( n ) );

    return buffer;
}

#undef BOOST_CORE_DETAIL_SNPRINTF

#if defined(BOOST_MSVC)
# pragma warning( pop )
#endif

// tn_holder

template<class T> struct tn_holder
{
    static std::string type_name( std::string const& suffix )
    {
        return typeid_name<T>() + suffix;
    }
};

// integrals

template<> struct tn_holder<bool>
{
    static std::string type_name( std::string const& suffix )
    {
        return "bool" + suffix;
    }
};

template<> struct tn_holder<char>
{
    static std::string type_name( std::string const& suffix )
    {
        return "char" + suffix;
    }
};

template<> struct tn_holder<signed char>
{
    static std::string type_name( std::string const& suffix )
    {
        return "signed char" + suffix;
    }
};

template<> struct tn_holder<unsigned char>
{
    static std::string type_name( std::string const& suffix )
    {
        return "unsigned char" + suffix;
    }
};

template<> struct tn_holder<short>
{
    static std::string type_name( std::string const& suffix )
    {
        return "short" + suffix;
    }
};

template<> struct tn_holder<unsigned short>
{
    static std::string type_name( std::string const& suffix )
    {
        return "unsigned short" + suffix;
    }
};

template<> struct tn_holder<int>
{
    static std::string type_name( std::string const& suffix )
    {
        return "int" + suffix;
    }
};

template<> struct tn_holder<unsigned>
{
    static std::string type_name( std::string const& suffix )
    {
        return "unsigned" + suffix;
    }
};

template<> struct tn_holder<long>
{
    static std::string type_name( std::string const& suffix )
    {
        return "long" + suffix;
    }
};

template<> struct tn_holder<unsigned long>
{
    static std::string type_name( std::string const& suffix )
    {
        return "unsigned long" + suffix;
    }
};

template<> struct tn_holder<boost::long_long_type>
{
    static std::string type_name( std::string const& suffix )
    {
        return "long long" + suffix;
    }
};

template<> struct tn_holder<boost::ulong_long_type>
{
    static std::string type_name( std::string const& suffix )
    {
        return "unsigned long long" + suffix;
    }
};

#if defined(BOOST_HAS_INT128)

template<> struct tn_holder<boost::int128_type>
{
    static std::string type_name( std::string const& suffix )
    {
        return "__int128" + suffix;
    }
};

template<> struct tn_holder<boost::uint128_type>
{
    static std::string type_name( std::string const& suffix )
    {
        return "unsigned __int128" + suffix;
    }
};

#endif

template<> struct tn_holder<wchar_t>
{
    static std::string type_name( std::string const& suffix )
    {
        return "wchar_t" + suffix;
    }
};

#if !defined(BOOST_NO_CXX11_CHAR16_T)

template<> struct tn_holder<char16_t>
{
    static std::string type_name( std::string const& suffix )
    {
        return "char16_t" + suffix;
    }
};

#endif

#if !defined(BOOST_NO_CXX11_CHAR32_T)

template<> struct tn_holder<char32_t>
{
    static std::string type_name( std::string const& suffix )
    {
        return "char32_t" + suffix;
    }
};

#endif

#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L

template<> struct tn_holder<char8_t>
{
    static std::string type_name( std::string const& suffix )
    {
        return "char8_t" + suffix;
    }
};

#endif

#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L

template<> struct tn_holder<std::byte>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::byte" + suffix;
    }
};

#endif

// floating point

template<> struct tn_holder<float>
{
    static std::string type_name( std::string const& suffix )
    {
        return "float" + suffix;
    }
};

template<> struct tn_holder<double>
{
    static std::string type_name( std::string const& suffix )
    {
        return "double" + suffix;
    }
};

template<> struct tn_holder<long double>
{
    static std::string type_name( std::string const& suffix )
    {
        return "long double" + suffix;
    }
};

// void

template<> struct tn_holder<void>
{
    static std::string type_name( std::string const& suffix )
    {
        return "void" + suffix;
    }
};

// nullptr_t

#if !defined(BOOST_NO_CXX11_NULLPTR)

template<> struct tn_holder<std::nullptr_t>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::nullptr_t" + suffix;
    }
};

#endif

// cv

template<class T> struct tn_holder<T const>
{
    static std::string type_name( std::string const& suffix )
    {
        return tn_holder<T>::type_name( " const" + suffix );
    }
};

template<class T> struct tn_holder<T volatile>
{
    static std::string type_name( std::string const& suffix )
    {
        return tn_holder<T>::type_name( " volatile" + suffix );
    }
};

template<class T> struct tn_holder<T const volatile>
{
    static std::string type_name( std::string const& suffix )
    {
        return tn_holder<T>::type_name( " const volatile" + suffix );
    }
};

// refs

template<class T> struct tn_holder<T&>
{
    static std::string type_name( std::string const& suffix )
    {
        return tn_holder<T>::type_name( "&" + suffix );
    }
};

#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)

template<class T> struct tn_holder<T&&>
{
    static std::string type_name( std::string const& suffix )
    {
        return tn_holder<T>::type_name( "&&" + suffix );
    }
};

#endif

// function types

#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

// tn_add_each

template<class T> int tn_add_each_impl( std::string& st )
{
    if( !st.empty() ) st += ", ";
    st += tn_holder<T>::type_name( "" );
    return 0;
}

template<class... T> std::string tn_add_each()
{
    std::string st;

    typedef int A[ sizeof...(T) + 1 ];
    (void)A{ 0, tn_add_each_impl<T>( st )... };

    return st;
}

template<class R, class... A> std::string function_type_name( tn_identity<R(A...)>, std::string const& trailer, std::string const& suffix )
{
    std::string r = tn_holder<R>::type_name( "" );

    if( !suffix.empty() )
    {
        r += '(';

        if( suffix[ 0 ] == ' ' )
        {
            r += suffix.substr( 1 );
        }
        else
        {
            r += suffix;
        }

        r += ')';
    }

    r += '(' + tn_add_each<A...>() + ')';
    r += trailer;

    return r;
}

template<class R, class... A> struct tn_holder<R(A...)>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), "", suffix );
    }
};

#if !defined(BOOST_MSVC) || BOOST_MSVC >= 1900

template<class R, class... A> struct tn_holder<R(A...) const>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) volatile>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " volatile", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const volatile>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const volatile", suffix );
    }
};

#endif

#if !defined(BOOST_NO_CXX11_REF_QUALIFIERS)

template<class R, class... A> struct tn_holder<R(A...) &>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " &", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const &>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const &", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) volatile &>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " volatile &", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const volatile &>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const volatile &", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) &&>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " &&", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const &&>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const &&", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) volatile &&>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " volatile &&", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const volatile &&>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const volatile &&", suffix );
    }
};

#endif

#if defined( __cpp_noexcept_function_type ) || defined( _NOEXCEPT_TYPES_SUPPORTED )

template<class R, class... A> struct tn_holder<R(A...) noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) volatile noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " volatile noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const volatile noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const volatile noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) & noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " & noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const & noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const & noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) volatile & noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " volatile & noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const volatile & noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const volatile & noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) && noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " && noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const && noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const && noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) volatile && noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " volatile && noexcept", suffix );
    }
};

template<class R, class... A> struct tn_holder<R(A...) const volatile && noexcept>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const volatile && noexcept", suffix );
    }
};

#endif

#endif // #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

// pointers

template<class T> struct tn_holder<T*>
{
    static std::string type_name( std::string const& suffix )
    {
        return tn_holder<T>::type_name( "*" + suffix );
    }
};

// arrays

template<class T> std::pair<std::string, std::string> array_prefix_suffix( tn_identity<T> )
{
    return std::pair<std::string, std::string>( tn_holder<T>::type_name( "" ), "" );
}

template<class T, std::size_t N> std::pair<std::string, std::string> array_prefix_suffix( tn_identity<T[N]> )
{
    std::pair<std::string, std::string> r = detail::array_prefix_suffix( tn_identity<T>() );

    r.second = '[' + tn_to_string( N ) + ']' + r.second;

    return r;
}

template<class T> std::string array_type_name( tn_identity<T[]>, std::string const& suffix )
{
    std::pair<std::string, std::string> r = detail::array_prefix_suffix( tn_identity<T>() );

    if( suffix.empty() )
    {
        return r.first + "[]" + r.second;
    }
    else
    {
        return r.first + '(' + suffix + ")[]" + r.second;
    }
}

template<class T> struct tn_holder<T[]>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::array_type_name( tn_identity<T[]>(), suffix );
    }
};

template<class T> struct tn_holder<T const[]>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::array_type_name( tn_identity<T const[]>(), suffix );
    }
};

template<class T> struct tn_holder<T volatile[]>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::array_type_name( tn_identity<T volatile[]>(), suffix );
    }
};

template<class T> struct tn_holder<T const volatile[]>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::array_type_name( tn_identity<T const volatile[]>(), suffix );
    }
};

template<class T, std::size_t N> std::string array_type_name( tn_identity<T[N]>, std::string const& suffix )
{
    std::pair<std::string, std::string> r = detail::array_prefix_suffix( tn_identity<T[N]>() );

    if( suffix.empty() )
    {
        return r.first + r.second;
    }
    else
    {
        return r.first + '(' + suffix + ")" + r.second;
    }
}

template<class T, std::size_t N> struct tn_holder<T[N]>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::array_type_name( tn_identity<T[N]>(), suffix );
    }
};

template<class T, std::size_t N> struct tn_holder<T const[N]>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::array_type_name( tn_identity<T const[N]>(), suffix );
    }
};

template<class T, std::size_t N> struct tn_holder<T volatile[N]>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::array_type_name( tn_identity<T volatile[N]>(), suffix );
    }
};

template<class T, std::size_t N> struct tn_holder<T const volatile[N]>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::array_type_name( tn_identity<T const volatile[N]>(), suffix );
    }
};

// pointers to members

#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

template<class R, class T> struct tn_holder<R T::*>
{
    static std::string type_name( std::string const& suffix )
    {
        return tn_holder<R>::type_name( ' ' + tn_holder<T>::type_name( "" ) + "::*" + suffix );
    }
};

#if defined(BOOST_MSVC) && BOOST_MSVC < 1900

template<class R, class T, class... A> struct tn_holder<R(T::*)(A...)>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), "", ' ' + tn_holder<T>::type_name( "" ) + "::*" + suffix );
    }
};

template<class R, class T, class... A> struct tn_holder<R(T::*)(A...) const>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const", ' ' + tn_holder<T>::type_name( "" ) + "::*" + suffix );
    }
};

template<class R, class T, class... A> struct tn_holder<R(T::*)(A...) volatile>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " volatile", ' ' + tn_holder<T>::type_name( "" ) + "::*" + suffix );
    }
};

template<class R, class T, class... A> struct tn_holder<R(T::*)(A...) const volatile>
{
    static std::string type_name( std::string const& suffix )
    {
        return detail::function_type_name( tn_identity<R(A...)>(), " const volatile", ' ' + tn_holder<T>::type_name( "" ) + "::*" + suffix );
    }
};

#endif // #if defined(BOOST_MSVC) && BOOST_MSVC < 1900

#endif // #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

// strings

template<template<class Ch, class Tr, class A> class L, class Ch> struct tn_holder< L<Ch, std::char_traits<Ch>, std::allocator<Ch> > >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = sequence_template_name< L<Ch, std::char_traits<Ch>, std::allocator<Ch> > >();
        return tn + '<' + tn_holder<Ch>::type_name( "" ) + '>' + suffix;
    }
};

template<> struct tn_holder<std::string>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::string" + suffix;
    }
};

template<> struct tn_holder<std::wstring>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::wstring" + suffix;
    }
};

#if !defined(BOOST_NO_CXX11_CHAR16_T)

template<> struct tn_holder<std::u16string>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::u16string" + suffix;
    }
};

#endif

#if !defined(BOOST_NO_CXX11_CHAR32_T)

template<> struct tn_holder<std::u32string>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::u32string" + suffix;
    }
};

#endif

#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L

template<> struct tn_holder< std::basic_string<char8_t> >
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::u8string" + suffix;
    }
};

#endif

// string views (et al)

template<template<class Ch, class Tr> class L, class Ch> struct tn_holder< L<Ch, std::char_traits<Ch> > >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = sequence_template_name< L<Ch, std::char_traits<Ch> > >();
        return tn + '<' + tn_holder<Ch>::type_name( "" ) + '>' + suffix;
    }
};

// needed for libstdc++
template<> struct tn_holder<std::ostream>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::ostream" + suffix;
    }
};

#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW)

template<> struct tn_holder<std::string_view>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::string_view" + suffix;
    }
};

template<> struct tn_holder<std::wstring_view>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::wstring_view" + suffix;
    }
};

#if !defined(BOOST_NO_CXX11_CHAR16_T)

template<> struct tn_holder<std::u16string_view>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::u16string_view" + suffix;
    }
};

#endif

#if !defined(BOOST_NO_CXX11_CHAR32_T)

template<> struct tn_holder<std::u32string_view>
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::u32string_view" + suffix;
    }
};

#endif

#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L

template<> struct tn_holder< std::basic_string_view<char8_t> >
{
    static std::string type_name( std::string const& suffix )
    {
        return "std::u8string_view" + suffix;
    }
};

#endif

#endif

// class templates

#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

template<template<class...> class L, class... T> struct tn_holder< L<T...> >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = detail::class_template_name< L<T...> >();
        std::string st = tn_add_each<T...>();

        return tn + '<' + st + '>' + suffix;
    }
};

#else

template<template<class T1> class L, class T1> struct tn_holder< L<T1> >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = detail::class_template_name< L<T1> >();
        return tn + '<' + tn_holder<T1>::type_name( "" ) + '>' + suffix;
    }
};

template<template<class T1, class T2> class L, class T1, class T2> struct tn_holder< L<T1, T2> >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = detail::class_template_name< L<T1, T2> >();
        return tn + '<' + tn_holder<T1>::type_name( "" ) + ", " + tn_holder<T2>::type_name( "" ) + '>' + suffix;
    }
};

#endif

// sequence containers

template<template<class T, class A> class L, class T> struct tn_holder< L<T, std::allocator<T> > >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = detail::sequence_template_name< L<T, std::allocator<T> > >();
        return tn + '<' + tn_holder<T>::type_name( "" ) + '>' + suffix;
    }
};

// set

template<template<class T, class Pr, class A> class L, class T> struct tn_holder< L<T, std::less<T>, std::allocator<T> > >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = detail::set_template_name< L<T, std::less<T>, std::allocator<T> > >();
        return tn + '<' + tn_holder<T>::type_name( "" ) + '>' + suffix;
    }
};

// map

template<template<class T, class U, class Pr, class A> class L, class T, class U> struct tn_holder< L<T, U, std::less<T>, std::allocator<std::pair<T const, U> > > >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = detail::map_template_name< L<T, U, std::less<T>, std::allocator<std::pair<T const, U> > > >();
        return tn + '<' + tn_holder<T>::type_name( "" ) + ", " + tn_holder<U>::type_name( "" ) +  '>' + suffix;
    }
};

#if !defined(BOOST_NO_CXX11_HDR_FUNCTIONAL)

// unordered_set

template<template<class T, class H, class Eq, class A> class L, class T> struct tn_holder< L<T, std::hash<T>, std::equal_to<T>, std::allocator<T> > >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = detail::set_template_name< L<T, std::hash<T>, std::equal_to<T>, std::allocator<T> > >();
        return tn + '<' + tn_holder<T>::type_name( "" ) + '>' + suffix;
    }
};

// unordered_map

template<template<class T, class U, class H, class Eq, class A> class L, class T, class U> struct tn_holder< L<T, U, std::hash<T>, std::equal_to<T>, std::allocator<std::pair<T const, U> > > >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = detail::map_template_name< L<T, U, std::hash<T>, std::equal_to<T>, std::allocator<std::pair<T const, U> > > >();
        return tn + '<' + tn_holder<T>::type_name( "" ) + ", " + tn_holder<U>::type_name( "" ) +  '>' + suffix;
    }
};

#endif

// array

template<template<class T, std::size_t N> class L, class T, std::size_t N> struct tn_holder< L<T, N> >
{
    static std::string type_name( std::string const& suffix )
    {
        std::string tn = detail::array_template_name< L<T, N> >();
        return tn + '<' + tn_holder<T>::type_name( "" ) + ", " + tn_to_string( N ) + '>' + suffix;
    }
};

} // namespace detail

template<class T> std::string type_name()
{
    return core::detail::tn_holder<T>::type_name( "" );
}

} // namespace core
} // namespace boost

#endif  // #ifndef BOOST_CORE_TYPE_NAME_HPP_INCLUDED
