For example, there are three variable conversion functions.
//Int
int toInt(std::string input)
{
int ret = strtol(input.c_str(), 0, 10);
return ret;
}
//Double
double toDouble(std::string input)
{
double ret = strtod(input.c_str(), 0);
return ret;
}
//Const char*
const char* toChar(std::string input)
{
return input.c_str();
}
I want to combine these functions like below
~~ toConvert(std::string input)
{
if ( Variable type is Int )
return strtol(~~~)
else if ( Varibale type is Double )
return strtod(~~~)
...
// Using
int i = toConvert<int>(input);
double d = toConvert<double>(input);
const char* c = toConvert<const char*>(input);
Is it possible?
Please help for implemet above function.
CodePudding user response:
Your "using" code is passing a template argument to toConvert()
, so make sure toConvert()
is actually a template, and then you can specialize it for each type you want, eg:
template<typename T>
T toConvert(std::string &input) { return T{}; /* or, throw an exception... */ }
template<>
int toConvert<int>(std::string &input)
{
return strtol(input.c_str(), 0, 10);
}
template<>
double toConvert<double>(std::string &input)
{
return strtod(input.c_str(), 0);
}
template<>
const char* toConvert<const char*>(std::string &input)
{
return input.c_str();
}
Or, if you are using C 17 or later, you can instead use if constexpr
in a single template:
#include <type_traits>
template<typename T>
T toConvert(std::string &input)
{
if constexpr (std::is_same_v<T, int>)
return strtol(input.c_str(), 0, 10);
else if constexpr (std::is_same_v<T, double>)
return strtod(input.c_str(), 0);
else if constexpr (std::is_same_v<T, const char*>)
return input.c_str();
else
return T{}; // or, throw an exception, or static_assert a compiler error...
}
Notice in either case that I changed the input parameter to std::string&
. Your original code was taking in the std::string
by value, which means it takes in a copy of the caller's string, and so the const char*
conversion would return a dangling pointer to invalid memory when the copied std::string
is freed upon return
. By taking a reference instead, you avoid that copy.
You might be tempted to take in a const std::string&
reference instead, but that would allow calls like toConvert<const char*>("string")
to compile but still return a dangling pointer, since the compiler would have to create a temporary std::string
to bind to the const reference. But a string literal can't bind to a non-const reference.
CodePudding user response:
That's possible using C 17's if constexpr
syntax. For example:
template <typename T>
constexpr bool dependent_false = false;
template <typename T>
T convertTo(const std::string& input)
{
if constexpr (std::is_same_v<T, int>) {
return std::stoi(input);
} else if constexpr (std::is_same_v<T, double>) {
return std::stod(input);
} else {
static_assert(dependent_false<T>, "Can't convert to the specified type");
}
}
The whole dependent_false
thing exists to make the static_assert
ion dependent on the template parameter so that it only gets checked when the function template is instantiated. Just an odd quirk of the C template rules.
Note that I left the const char*
case out. It has very different semantics from the others, since the pointer returned by c_str
points to memory owned by the std::string
object.