Stringify all things in C++ [Wednesday, 2013-03-13]
While working on amirite I needed a nice way to print strings from any kind of incoming data to make assertions look decent, here goes nothing:
namespace stringify
{
# ifndef NO_DEMANGLE
# include <cxxabi.h>
template <typename Type>
std::string
type (void)
{
std::string result;
int status;
char* demangled = abi::__cxa_demangle(typeid(Type).name(),
nullptr, nullptr, &status);
result = demangled;
free(demangled);
return result;
}
# else
template <typename Type>
std::string
type (void)
{
return typeid(Type).name();
}
# endif
namespace _has_cout
{
typedef char no[1];
typedef char yes[2];
struct any
{
template <typename T>
any (T const&);
};
no& operator << (std::ostream const&, any const&);
yes& test (std::ostream&);
no& test (no&);
template <typename Type>
struct has_cout
{
static std::ostream& s;
static Type const& t;
static bool const value = sizeof(test(s << t)) == sizeof(yes);
};
}
template <typename Type>
struct has_cout : _has_cout::has_cout<Type>
{};
template <typename Type>
struct has_to_string
{
typedef char no[1];
typedef char yes[2];
template <typename C>
static typename std::enable_if<std::is_same<decltype(&C::to_string),
std::string(C::*)(void)>::value, yes&>::type
test (decltype(&C::to_string));
template <typename C>
static no& test (...);
static const bool value = sizeof(test<Type>(0)) == sizeof(yes);
};
template <typename Type>
struct is_string
{
static const bool value = std::is_same<Type, std::string>::value ||
std::is_same<Type, std::wstring>::value ||
std::is_same<Type, std::u16string>::value ||
std::is_same<Type, std::u32string>::value;
};
template <typename Type>
struct is_raw_string
{
static const bool value = (std::is_array<Type>::value &&
std::is_same<typename std::remove_const<
typename std::remove_extent<Type>::type>::type, char>::value) ||
(std::is_pointer<Type>::value &&
std::is_same<typename std::remove_const<
typename std::remove_pointer<Type>::type>::type, char>::value);
};
template <typename Type>
typename std::enable_if<is_string<Type>::value, Type>::type
value (Type& value)
{
return value;
}
template <typename Type>
typename std::enable_if<is_raw_string<Type>::value, std::string>::type
value (Type& value)
{
return value;
}
template <typename Type>
typename std::enable_if<std::is_fundamental<Type>::value, std::string>::type
value (Type value)
{
std::ostringstream ss(std::ostringstream::out);
ss << std::boolalpha << value;
return ss.str();
}
template <typename Type>
typename std::enable_if<std::is_pointer<Type>::value &&
!is_raw_string<Type>::value, std::string>::type
value (Type& value)
{
std::ostringstream ss(std::ostringstream::out);
ss << "(" << type<Type>() << ") " << value;
return ss.str();
}
template <typename Type>
typename std::enable_if<std::is_enum<Type>::value &&
has_cout<Type>::value, std::string>::type
value (Type value)
{
std::ostringstream ss(std::ostringstream::out);
ss << "#<enum " << type<Type>() << ": " << value << ">";
return ss.str();
}
template <typename Type>
typename std::enable_if<std::is_enum<Type>::value &&
!has_cout<Type>::value, std::string>::type
value (Type value)
{
std::ostringstream ss(std::ostringstream::out);
ss << "#<enum " << type<Type>() << ": " <<
static_cast<typename std::underlying_type<Type>::type>(value) << ">";
return ss.str();
}
template <typename Type>
typename std::enable_if<std::is_union<Type>::value &&
has_cout<Type>::value, std::string>::type
value (Type& value)
{
std::ostringstream ss(std::ostringstream::out);
ss << "#<union " << type<Type>() << ": " << value << ">";
return ss.str();
}
template <typename Type>
typename std::enable_if<std::is_union<Type>::value &&
!has_cout<Type>::value, std::string>::type
value (Type& value)
{
std::ostringstream ss(std::ostringstream::out);
ss << "#<union " << type<Type>() << ":" << &value << ">";
return ss.str();
}
template <typename Type>
typename std::enable_if<std::is_class<Type>::value &&
!is_string<Type>::value &&
!has_to_string<Type>::value &&
has_cout<Type>::value, std::string>::type
value (Type& value)
{
std::ostringstream ss(std::ostringstream::out);
ss << "#<" << type<Type>() << ": " << value << ">";
return ss.str();
}
template <typename Type>
typename std::enable_if<std::is_class<Type>::value &&
!has_to_string<Type>::value &&
!is_string<Type>::value &&
!has_cout<Type>::value, std::string>::type
value (Type& value)
{
std::ostringstream ss(std::ostringstream::out);
ss << "#<" << type<Type>() << ":" << &value << ">";
return ss.str();
}
template <typename Type>
typename std::enable_if<has_to_string<Type>::value, std::string>::type
value (Type& value)
{
return value.to_string();
}
template <typename Type>
typename std::enable_if<std::is_array<Type>::value &&
!is_raw_string<Type>::value, std::string>::type
value (Type& value)
{
std::ostringstream ss(std::ostringstream::out);
ss << "[";
for (unsigned i = 0; i < std::extent<Type>::value; i++) {
ss << stringify::value(value[i]);
if (i < std::extent<Type>::value - 1) {
ss << ", ";
}
}
ss << "]";
return ss.str();
}
}
An example stringifying things:
class foo
{};
class bar
{
public:
std::string
to_string (void)
{
return "#<bar: dabbah>";
}
};
enum class derp
{
lol, wut, omg
};
std::ostream& operator << (std::ostream& on, derp v)
{
switch (v) {
case derp::lol:
on << "lol";
break;
case derp::wut:
on << "wut";
break;
case derp::omg:
on << "omg";
break;
}
return on;
}
int
main (int argc, char* argv[])
{
std::string str = "34";
std::cout << stringify::value(str) << std::endl;
foo a;
std::cout << stringify::value(a) << std::endl;
bar b;
std::cout << stringify::value(b) << std::endl;
std::cout << stringify::value(23) << std::endl;
std::cout << stringify::value(derp::omg) << std::endl;
int lol[][2] = { { 1, 2 }, { 3, 4 } };
std::cout << stringify::value(lol) << std::endl;
float* duh = (float*) 342;
std::cout << stringify::value(duh) << std::endl;
std::cout << stringify::value("lol") << std::endl;
std::cout << stringify::value(true) << std::endl;
return 0;
}
And the output:
34
#<foo:0x7fff253f8730>
#<bar: dabbah>
23
#<enum derp: omg>
[[1, 2], [3, 4]]
(float*) 0x156
lol
true
Bam, magic (as usual).