Printing a type description works inversely for arrays!

Hi,

I have the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
template <typename T>
struct type_description;

template<>
struct type_description<int>
{
    operator const char*()
    {
        return "int";
    }
};


// handle array
template<typename T, size_t N>
struct type_description<T[N]>
{
    operator const char*()
    {
        static char buf[BUF_SIZE];
        strcpy(buf, type_description<T>());
        strcat(buf, "[");
        string s = to_string(N);
        strcat(buf, s.c_str());
        strcat(buf, "]");
        return buf; 
    }
};



void display()
{
    cout << type_description<int[3][5]>() << endl;
}


The output of display() is:


int[5][3]


so its inverted!

How can I correct this? It must be simple but I can't see it!!

Thanks

Juan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <typeinfo>
#include <type_traits>
#include <string>

#ifdef __GNUG__ // GNU or compatible

  #include <cxxabi.h>
  #include <cstdlib>
  #include <memory>

  std::string demangle( const char* raw_name )
  {
      int result ;
      std::unique_ptr< char, decltype(&std::free) > ptr(
         __cxxabiv1::__cxa_demangle( raw_name, nullptr, nullptr, std::addressof(result) ),
         &std::free ) ;
      return result == 0 ? ptr.get() : raw_name ;
  }

  template < typename T > std::string qualifiers()
  {
      std::string qualifier_token ;
      if( std::is_const<T>::value ) qualifier_token += "const " ;
      if( std::is_volatile<T>::value ) qualifier_token += "volatile " ;
      return qualifier_token ;
  }

#else // hopefully microsoft or compatible

  #include <regex>
  std::string demangle( const char* raw_name )
  {
      std::string str = raw_name ;
      return std::regex_replace( str, std::regex( "\\[0\\]" ), "[]" ) ;
  }

  template < typename T > std::string qualifiers() { return {} ; }

#endif // _GNUG_

template < typename T > std::string type_desc()
{ return qualifiers<T>() + demangle( typeid(T).name() ) ; }

namespace one { struct two{ using type = int ; }; }

int main()
{
    std::cout << type_desc< const one::two >() << '\n' // const one::two
              << type_desc< one::two[] >() << '\n' // one::two[] (incomplete type)
              << type_desc< const one::two::type[5] >() << '\n' // const int[5]
              << type_desc< volatile one::two[][5] >() << '\n' // volatile one::two[][5] (incomplete type)
              << type_desc< const volatile one::two[8][5] >() << '\n' ; // const volatile one::two[8][5]
}

http://coliru.stacked-crooked.com/a/2353fef70ab35510
http://rextester.com/XUPY3449
Hi JLBorges!!

You are always available and your solutions are always interesting.

However, I am trying to solve this problem of printing (return a const char*) for the name of a type, without using typeid(T).


Its an exercise from the book C++ Template Metaprogramming.

I only have to make it work for simple and compound combinations of char, short, int and long.


Any ideas along this line?

Best regards,
Juan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <string>

template <typename> struct extents { static std::string str() { return {}; } };

template < typename T > struct extents<T[]>
{ static std::string str() { return "[]" + extents<T>::str() ; } };

template < typename T, std::size_t N > struct extents< T[N] >
{ static std::string str() { return '[' + std::to_string(N) + ']' + extents<T>::str() ; } };

template <typename> struct name { static std::string str() { return "'unknown type'" ; } };
template <> struct name<char> { static std::string str() { return "char" ; } };
template <> struct name<short> { static std::string str() { return "short" ; } };
template <> struct name<int> { static std::string str() { return "int" ; } };
template <> struct name<long> { static std::string str() { return "long" ; } };

template < typename T > struct name< const T >
{ static std::string str() { return "const " + name<T>::str() ; } };

template < typename T > struct name< volatile T >
{ static std::string str() { return "volatile " + name<T>::str() ; } };

template < typename T > struct name< volatile const T >
{ static std::string str() { return "volatile const " + name<T>::str() ; } };

// TO DO: take care of pointers, references

template < typename T > struct base_type
{
    using type = T ;
    static std::string str() { return name<type>::str() ; }
};

template < typename T > struct base_type<T[]>
{
    using type = typename base_type<T>::type ;
    static std::string str() { return name<type>::str() ; }
};

template < typename T, std::size_t N > struct base_type< T[N] >
{
    using type = typename base_type<T>::type ;
    static std::string str() { return name<type>::str() ; }
};

template < typename T > struct type_desc
{ static std::string str() { return base_type<T>::str() + ' ' + extents<T>::str() ; } };

int main()
{
    const char cstr[] = "abcdefgh" ;
    std::cout << type_desc<char>::str() << '\n' // char
              << type_desc< decltype(cstr) >::str() << '\n' // const char [9]
              << type_desc< short[] >::str() << '\n' // short []
              << type_desc< const short[7][5] >::str() << '\n' // const short [7][5]
              << type_desc< volatile int[5][3][8][6] >::str() << '\n' // volatile int [5][3][8][6]
              << type_desc< volatile const long[][3][8][6] >::str() << '\n' // volatile const long [][3][8][6]
              << type_desc< const std::string[6] >::str() << '\n' ; // const 'unknown type' [6] 
}

http://coliru.stacked-crooked.com/a/09e9268ad1973f8a
http://rextester.com/LOU21747
Great reply thank you!!

I added support for pointers and references as follows. I think it works well.

1
2
3
4
5
template < typename T > struct name< T* >
{ static std::string str() { return name<T>::str() + "*" ; } };

template < typename T > struct name< T& >
{ static std::string str() { return name<T>::str() + "&" ; } };


Best regards!!!

Juan
You may want to add
1
2
template < typename T > struct name< T&& >
{ static std::string str() { return name<T>::str() + "&&" ; } };


TO DO: take care of cv-qualified pointers. eg. type_desc< const char* const* const >::str()
Ok, for cv-qualified pointers wee need:

1
2
3
4
5
6
7
8
template < typename T > struct name< T* const>
{ static std::string str() { return name<T>::str() + "* const" ; } };

template < typename T > struct name< T* volatile>
{ static std::string str() { return name<T>::str() + "* volatile" ; } };

template < typename T > struct name< T* const volatile>
{ static std::string str() { return name<T>::str() + "* const volatile" ; } };



And for references and pointers to arrays we need:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template < typename T, std::size_t N > struct extents< T(&)[N] >
{ static std::string str() { return "(&)[" + std::to_string(N) + ']' + extents<T>::str() ; } };

template < typename T, std::size_t N > struct extents< T(*)[N] >
{ static std::string str() { return "(*)[" + std::to_string(N) + ']' + extents<T>::str() ; } };

template < typename T, std::size_t N > struct base_type< T(&)[N] >
{
    using type = typename base_type<T>::type ;
    static std::string str() { return name<type>::str() ; }
};

template < typename T, std::size_t N > struct base_type< T(*)[N] >
{
    using type = typename base_type<T>::type ;
    static std::string str() { return name<type>::str() ; }
};


and now we can do:

1
2
3
4
5
6
7
8
9
    int v[14];
    int (&vr)[14] = v;
    cout << type_desc<decltype(vr)>::str() << endl;
    int ia[3][4];
    int (&vrr)[3][4] = ia;
    cout << type_desc<decltype(vrr)>::str() << endl;
    int (*par)[4] = ia;
    cout << type_desc<decltype(par)>::str() << endl;



This is so beautiful!! Thanks so much for your help!! Any other enhancement?

Regards,
Juan Dent
Hi!!

I thought I would support for reference to functions receiving any number/type of parameters!! It works!

I added:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
template <> struct name<void> { static std::string str() { return "void" ; } };

template< typename ...Args> struct separator
{
    static const std::array<std::string, 2> constants;
};

template< typename ...Args>
const std::array<std::string, 2> separator<Args...>::constants = {"", ","};


template< typename ...Args> struct arg_names
{
    static std::string str() { return "Args..."; }
};

template<> struct arg_names<>
{
    static std::string str() { return ""; }
};

template < typename Arg0, typename ...Args> struct arg_names< Arg0, Args...>
{
    static std::string str() { return name<Arg0>::str() + separator<Args...>::constants[ sizeof...(Args) > 0] + arg_names<Args...>::str() ; } 
};

template < typename T > struct name< T(&)() >
{ static std::string str() { return name<T>::str() + "(&)()" ; } };

template < typename T, typename ...Args> struct name< T(&)(Args...) >
{ static std::string str() { return name<T>::str() + "(&)(" + arg_names<Args...>::str() + ")" ; } };


We can now call:

1
2
3
4
5
6
7
8
9
10
11
12
    void (&pf)() = funny;
    cout << type_desc<decltype(pf)>::str() << endl;
    
    void (&pfi)(int) = funny;
    cout << type_desc<decltype(pfi)>::str() << endl;
    
    void (&pfil)(int,long) = funny;
    cout << type_desc<decltype(pfil)>::str() << endl;
    
    void (&pfilc)(int,long,char) = funny;
    cout << type_desc<decltype(pfilc)>::str() << endl;


where the funny functions are:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void funny()
{
    std::cout << "fun\n";
}
void funny(int i)
{
    std::cout << "funny\n";
}

void funny(int i, long l)
{
    std::cout << "funny\n";
}

void funny(int i, long l, char c)
{
    std::cout << "funny\n";
}


It works!!

Thanks again!!

Any other enhancements?

Regards,
Juan Dent
Topic archived. No new replies allowed.