14

I know that STL containers have a value_type parameter and I've seen how it can be used to declare a type of a variable like:

vector<int>::value_type foo;

But can we just print this value_type to a console?

I want to see "string" in this example:

int main() {
    vector<string> v = {"apple", "facebook", "microsoft", "google"};
    cout << v << endl;
    cout << v.value_type << endl;
    return 0;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Niakrais
  • 438
  • 1
  • 6
  • 12

5 Answers5

15

X::value_type is no different from any other type alias (aka typedef) in this regard — C++ has no native way of converting a type to its source code string representation (not the least because that could be ambiguous). What you can do is use std::type_info::name:

cout << typeid(decltype(v)::value_type).name() << endl;

The resulting text is compiler dependent (and not even guaranteed to be easily human readable). It will be consistent within the same build of a program, but you cannot expect it to be the same across different compilers. In other words, it's useful for debugging, but cannot reliably be used in a persistent fashion.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    Thank you, on my machine it gives me `NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE` – Niakrais Sep 03 '18 at 09:24
  • 1
    @You can use `__cxa_demangle` to get a readable type name: https://stackoverflow.com/a/23266701/1968 – Konrad Rudolph Sep 03 '18 at 09:26
  • Isn't it only for gcc? – Niakrais Sep 03 '18 at 09:30
  • @Niakrais: Not quite, it's for all compilers/platforms combinations using the Itanium ABI. The Itanium ABI is used on all Linux/MacOS platforms by all mainstream C++ compilers; the only widespread platform not using it is Windows, and thus MSVC and Clang in MSVC-compatible mode will not use it. Even on Windows, using Cygwin or MinGW means using the Itanium ABI. – Matthieu M. Sep 03 '18 at 11:14
5

In addition to the typeid-based answer, I see one further possibility using Boost like this:

#include <boost/type_index.hpp>

cout << boost::typeindex::type_id_with_cvr<decltype(v)::value_type>().pretty_name() << "\n";

The advantage is a portable, human-readable name that includes const/volatile qualifications and is consistent across major compilers and systems. The output on my machine is

// when compiled with gcc:
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
// ... and with clang:
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >

Decide for yourself whether this output qualifies as "human-readable", but also compare it to the typeid(...).name() approach, which on my machine gives:

NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lubgr
  • 37,368
  • 3
  • 66
  • 117
1

It's probably safe to assume that you need this type info for debug purposes(?). If it is, don't forget gdb's built in whatis and ptype commands:

$ cat main.cpp
#include <iostream>
#include <vector>
using namespace std;
int main() {
    vector<string> v = {"apple", "facebook", "microsoft", "google" };
    cout << v[2] << endl;
    // cout << v.value_type << endl;
    return 0;
}
$ cat gdbCommands.txt
break main
run
next
next
whatis v
quit
$ g++ -ggdb -o main main.cpp
$ gdb -x gdbCommands.txt ./main
GNU gdb (Ubuntu 8.0.1-0ubuntu1) 8.0.1
// bla bla bla ...
type = std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >>
OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87
1

I had a rather similar question some time ago. The answer there shown a function able to translate a type into a human readable string:

template <typename T> std::string TypeName() {
    auto name = typeid(T()).name();  // function type, not a constructor call!
    int status = 0;

    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };

    std::string ret((status == 0) ? res.get() : name);
    if (ret.substr(ret.size() - 3) == " ()") ret.resize(ret.size() - 3);
    return ret;
}

Once you have this TypeName function you can achieve your goal:

int main() {
    vector<string> v = {"apple", "facebook", "microsoft", "google" };
    cout << v << endl;
    cout << TypeName<v.value_type>() << endl;
    return 0;
}

Which should output (GCC HEAD 9 201809):

std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94
1

If it's for debug purpose, then this solution would work without any dependencies:

#include <regex>

template <typename T>
struct TypeToNameT
{
    static std::string getTypeFromName() {
#ifdef _MSC_VER
        std::regex re("<(.*)>::.*");
        std::string templateType(__FUNCTION__);
#else
        std::regex re(" = (.*);");
        std::string templateType(__PRETTY_FUNCTION__);
#endif
        std::smatch match;
        try
        {
            if (std::regex_search(templateType, match, re) && match.size() > 1)
                return match.str(1);
        }
        catch (std::regex_error& e) {
            // Syntax error in the regular expression
        }
        return "";
    }
};
/** Get the templated type name. This does not depends on RTTI, but on the preprocessor, so it should be quite safe to use even on old compilers */
template <typename T>
std::string getTypeName() { return TypeToNameT<T>::getTypeFromName(); }


int main() {
    std::vector<std::string> v = {"apple", "facebook", "microsoft", "google" };
    std::cout << getTypeName<decltype(v)::value_type>() << std::endl;  // Returns: "std::__cxx11::basic_string<char>"
    return 0;
}

Unlike the typeid based answer, this use the preprocessor and as such is not subject to name mangling (so the name is... readable).

Please notice that you can't use v.value_type as a type directly, it's not part of the instance v but of the type of v: std::vector<std::string>

The regex part is because the std::string class is too dumb and you need such oddities for a simple search/split code. If you have a decent string class, you'll not need regex here for extracting the part that's after the "=" string and before the ";".

xryl669
  • 3,376
  • 24
  • 47
  • `__FUNCTION__` prints just the function name, without any additional information. You want `__FUNCSIG__`. – HolyBlackCat Sep 03 '18 at 16:25
  • Just tried it here: http://rextester.com/FDZT51977 and it seems to work with __FUNCTION__. With __FUNCSIG__, the regex must be modified since the signature is more complex. In all cases, thanks, I did not know about __FUNCSIG__ before. – xryl669 Sep 03 '18 at 16:30
  • Nevermind, you're right. `__FUNCTION__` doesn't contain template parameters of the function itself, so when I used this trick before, I resorted to use `__FUNCSIG__`. But it missed me that it contains the template parameters of enclosing classes. That's a nice idea. – HolyBlackCat Sep 03 '18 at 16:33