I have a class containing a string member variable and am trying to write a template function that will allow me to convert the string to another type. As a simplified example, without templates this is what I would do.
struct A {
std::vector<std::string> tokens;
};
void Test(const A& a) {
// Get the final token value as an int, or default to 42.
int i = a.tokens.empty() ? 42 : std::stoi(*a.tokens.rbegin());
}
This however has gotten tedious to write in as many places as I'm trying to get the final token and parse it, so I've written the following to assist.
struct A {
std::vector<std::string> tokens;
const string& GetLast(const string& defaul = "") {
return tokens.empty() ? defaul : *tokens.rbegin();
}
int GetLast(int defaul) {
return tokens.empty() ? defaul : std::stoi(*tokens.rbegin());
}
float GetLast(float defaul) {
return tokens.empty() ? defaul : std::stof(*tokens.rbegin());
}
};
void Test(const A& a) {
int i = a.GetLast(42);
}
This works fine but it just seems to be asking to be made into a template and allow conversion to any sort of type, provided a converter function is provided. I can't seem to find a generic solution that works, however. The following is the closest thing I can think of that should work, but I constantly get compiler errors that the template types can't be deduced.
template <typename T, typename Converter, typename... Args>
T GetLastAs(T defaul, Args ...args)
{
return tokens.empty() ? defaul : Converter(*tokens.rbegin(), args...);
}
with usage a.GetLastAs<float, std::stof>(42.0f)
Is there a generic solution to this problem, or is that just wishful thinking on my part?
Edit: I've also gotten close with the following implementation:
template<class Converter, typename T, typename ...Args>
T GetLast(T defaul, Args ...args, Converter f = Converter()) const
{
return tokens.empty() ? defaul : f(*tokens.rbegin(), args...);
}
with a converter structure defined as struct ToInt { int operator()(const std::string& str) { return std::stoi(str); } };
and a usage of a.GetLast<ToInt>(42);
or a.GetLast(42, ToInt());
But I cannot replace the ToInt
in the usage to std::stoi
and I believe the reason may be because stoi
has an overload to take wstring
instead of string
and the GetLast
method cannot figure out which overload to use. I don't know if it's even possible to specify which overload to use.