1

Printing string that contains type info:

std::string demangle(const char* mangled_name) {
        size_t len = 0;
        int status = 0;

        std::unique_ptr<char, decltype(&std::free)> pointer (
            __cxxabiv1::__cxa_demangle(mangled_name, nullptr, &len, &status),
            &std::free);

        return pointer.get();
}

template <typename T>
std::string type_of(const T& arg) {
     return demangle(typeid(arg).name());
}

My custom is_string type trait:

template <typename T> struct is_string : std::false_type {};
template <> struct is_string<char*> : std::true_type {};
template <> struct is_string<char[]> : std::true_type {};
template <size_t N> struct is_string<char[N]> : std::true_type {};
template <> struct is_string<const char*> : std::true_type {};
template <> struct is_string<const char[]> : std::true_type {};
template <size_t N> struct is_string<const char[N]> : std::true_type {};

// Helper variable:
template <typename T>
constexpr bool is_string_v = is_string<T>::value;

Application:

std::cout << std::boolalpha;

std::cout << "Testing: " << '\n'
    << "char* = " << is_string_v<char*> << '\n'
    << "char[] = " << is_string_v<char[]> << '\n'
    << "char[10] = " << is_string_v<char[10]> << '\n'
    << "char[15] = " << is_string_v<char[15]> << '\n'
    << "const char* = " << is_string_v<const char*> << '\n'
    << "const char[] = " << is_string_v<const char[]> << '\n'
    << "const char[3] = " << is_string_v<const char[3]> << '\n'
    << "const char[6] = " << is_string_v<const char[6]> << "\n\n";

const char* string1 = "Hello";
const char string2[] = "Hello";
char string3[] = "Hello";
auto string4 = "Hello";

std::cout << "Testing with decltype(variable): " << '\n'
    << type_of(string1) << " = " << is_string_v<decltype(string1)> << '\n'
    << type_of(string2) << " = " << is_string_v<decltype(string2)> << '\n'
    << type_of(string3) << " = " << is_string_v<decltype(string3)> << '\n'
    << type_of(string4) << " = " << is_string_v<decltype(string4)> << "\n\n";

std::cout << "Testing with string literal: " << '\n'
    << type_of("Hello") << " = " << is_string_v<decltype("Hello")> << '\n';

Output:

Testing:
char* = true
char[] = true
char[10] = true
char[15] = true
const char* = true
const char[] = true
const char[3] = true
const char[6] = true

Testing with decltype(variable):
char const* = true
char [6] = true
char [6] = true
char const* = true

Testing with string literal:
char [6] = false
  • Why is is_string_v<decltype(some string literal)> false when they have the same type as string3 or char [6] which has true value when it passes as template parameter?
  • Is there something wrong with my demangling function? (since my implementation doesn't include a reference)
Desmond Gold
  • 1,517
  • 1
  • 7
  • 19
  • Have you considered comparing the mangled strings? Like, string3 and the quoted string? Have you considered checking is same in the types? – Yakk - Adam Nevraumont May 09 '21 at 13:23
  • i was forgotten to test out with the `is_same`. so my problem is all about having the additional references and cv-qualifiers without repeating myself – Desmond Gold May 09 '21 at 13:34

3 Answers3

3

This did the trick for me:

int main()
{
    std::cout << is_string_v<char const[sizeof "Hello"]> << '\n';
    std::cout << is_string_v<std::remove_reference_t<decltype("Hello")>> << '\n';
}

Output:

1
1

Looks like decltype added an extra reference. Taken from this answer:

The type denoted by decltype(e) is defined as follows:

  • if e is an unparenthesized id-expression or an unparenthesized class member access, decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;

  • otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

  • otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

  • otherwise, decltype(e) is the type of e.

§7.1.6.2 [dcl.type.simple]

And string literals are lvalues, so it adds a reference.

mediocrevegetable1
  • 4,086
  • 1
  • 11
  • 33
3

The problem is c-style string literals like "Hello" are an expressions, then decltype yields type as T&, i.e. const char (&) [6] for "Hello". (Note that decltype works in two different ways for unparenthesized id-expression or an unparenthesized class member access expression, and any other expression.)

You can add specialization as

template <size_t N> struct is_string<const char(&)[N]> : std::true_type {};
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
2

Add

template<class T> struct is_string<T&>:is_string<T>{};
template<class T> struct is_string<T&&>:is_string<T>{};
template<class T> struct is_string<T const>:is_string<T>{};
template<class T> struct is_string<T volatile>:is_string<T>{};
template<class T> struct is_string<T const volatile>:is_string<T>{};

and remove the const char[] and const char[N] specializations.

Also you might want to support u8 strings and wchar_t etc.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524