Reputation: 4604
I have the following template function ToString
that uses std::to_string
for arithmetic types and tries doing a static_cast
for pointer and enum types.
#include <iostream>
#include <string>
template <typename T>
std::string ToString(T val)
{
if (std::is_arithmetic<T>::value)
{
return std::to_string(val);
}
if (std::is_enum<T>::value || std::is_pointer<T>::value)
{
return std::to_string(static_cast<size_t>(val));
}
}
enum class MyEnum : int
{
E1,
E2
};
int main(int argc, char* argv[])
{
MyEnum e = MyEnum::E1;
const void* ptr = &e;
std::cout << ToString(e) << std::endl;
std::cout << ToString(ptr) << std::endl;
}
The code above doesn't compile. Is there a way I can achieve the desired functionality?
The compilation errors on VS2017 are
Error C2440 'static_cast': cannot convert from 'T' to 'size_t'
Error C2665 'std::to_string': none of the 9 overloads could convert all the argument types
Upvotes: 1
Views: 822
Reputation: 1045
This works with c++11. I hacked it together so I thought I may as well post it. You should pay attention to the other answers here though.
#include <cstdint>
#include <string>
#include <type_traits>
#include <iostream>
template <typename T>
auto ToString(T val) -> typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type
{
return std::to_string(val);
}
template <typename T>
auto ToString(T val) -> typename std::enable_if<std::is_enum<T>::value, std::string>::type
{
return std::to_string(static_cast<typename std::underlying_type<T>::type>(val));
}
template <typename T>
auto ToString(T val) -> typename std::enable_if<std::is_pointer<T>::value, std::string>::type
{
return std::to_string(reinterpret_cast<std::uintptr_t>(val));
}
enum class MyEnum : int
{
E1,
E2
};
int main(int argc, char* argv[])
{
MyEnum e = MyEnum::E1;
const void* ptr = &e;
std::cout << ToString(e) << std::endl;
std::cout << ToString(ptr) << std::endl;
}
Upvotes: 1
Reputation: 238341
You need to use if constexpr
. Otherwise you instantiate std::to_string
with a pointer or enum, which fails to compile (C2665).
Futhermore you cannot static cast pointer to an integer (C2440). You need reinterpret cast.
Also, your function lacks a return if the passed argument is neither enum, pointer nor an arithmetic type. In such case the behaviour is undefined. Solution: Always return something (or fail compilation if template arguments are invalid).
Also, size_t
is not guaranteed to be large enough to represent all pointer values. you want std::uintptr_t
.
And you'll probably want to use std::underlying_type_t
to get the correct type for enum class
.
if constexpr (std::is_pointer<T>::value) {
return std::to_string(reinterpret_cast<std::uintptr_t>(val));
} else if constexpr (std::is_enum<T>::value)
{
return std::to_string(static_cast<std::underlying_type_t<T>>(val));
} else {
return std::to_string(val);
}
Once you add the missing headers, this should work.
P.S. Design note: Now printing a pointer, integer or enum are all identical in output. You might want to add some prefix or similar to disambiguate the result.
Upvotes: 4
Reputation: 372784
I think the immediate issue here is the static_cast
if T is a pointer type. To convert a pointer to an integer, you need to use a reinterpret_cast
rather than a static_cast
, since that cast fundamentally reinterprets the bits of the pointer variable as a number rather than simply telling C++ to change what kind of object if thinks is being pointed at.
As the comments have pointed out, you’ll likely need to do some restructuring on your code to get it to work correctly, because when you instantiate the template, all the code in the body of the function will be compiled with that choice of T. To add to the list of suggestions of how to do this, if you’re using a C++17 compiler, consider using if constexpr
in your code instead of a regular if
. This will tell C++ to only conditionally compile the different branches of the if/else chain.
Upvotes: 1