Reputation: 1063
I have this template function that gets an element from an std::map
as a string, converts it to generic T
through std::stringstream operator>>
and returns it.
However the extraction operator only extracts the first word in the stream.
I have tried .str()
, but for a template function it won't do. I have also tried ss >> std::noskipws >> result;
but it does not seem to return valid data.
template<typename T>
T CConfig::get(const char *setting)
{
std::stringstream ss;
ss << this->m_settings[setting];
// this->m_settings[setting] = "this is a test"
T result;
ss >> result;
// result = "this"
return result;
}
My expectation is that if m_settings[setting] is "this is a test"
, the same string will be returned in my get
function. And at the same time, I don't want to break the template by hardcoding ss.str()
and using a different function for strings.
Upvotes: 1
Views: 965
Reputation: 96800
Here are 3 ways:
Like I said in my comment, you can specialize the function get<std::string>
and call std::getline()
from there.
template<typename T>
T CConfig::get(const char* settings) {
std::istringstream stream(m_settings[settings]);
T t; assert(stream >> t);
return t;
}
template<>
std::string CConfig::get<std::string>(const char* settings) {
std::istringstream stream(m_settings[settings]);
T t; assert((std::getline(stream, t)));
return t;
}
The cons of this approach is that it causes code-duplication.
Delegate to a helper function that uses a primary and specialized overload.
template<typename T>
T CConfig::get(const char* settings) {
T t;
assert(
do_get(std::istringstream(m_settings[settings]) >> std::skipws, t);
);
return t;
}
template<typename T>
bool do_get(std::istringstream& stream, T& data) {
return stream >> data;
}
template<>
bool do_get(std::istringstream& stream, std::string& data) {
return std::getline(stream, data);
}
If you have c++17 then just use if constexpr
:
template<class T>
T CConfig::get(const char* settings) {
T t;
std::istringstream stream(m_settings[settings]);
if constexpr(std::is_same_v<T, std::string>) {
assert((std::getline(stream, t)));
} else {
assert(stream >> t);
}
return t;
}
Upvotes: 1
Reputation: 385108
You have to decide exactly what you want the behaviour to be for strings. If you want to extract a whole line, you can use std::getline()
. Or in this case, perhaps just return this->m_settings[setting];
.
Whatever you end up doing, the solution is to put the variant behaviour into a specialisation for get<std::string>
.
Just be careful that the specialised behaviour doesn't deviate too far from that of the primary code, because that might be confusing to your users (which includes yourself!).
Upvotes: 3