How do we print out the value_type of a C++ STL container?

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;
}


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.

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


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> >>


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> >



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 ";".

Comments

Popular posts from this blog

Meaning of `{}` for return expression

Get current scroll position of ScrollView in React Native

flutter websocket connection issue