Reputation: 3324
I'm looking for a way to generically parse a whole string in C++. What I mean by that is that the entire string is used in the parse, or else it's an error. This means that the naive operator >>
behavior, which delimits on whitespace for most types, is not what I want (though I'd be perfectly happy if the solution involved it). Some examples:
// Parse a whole value, throwing an exception if there's a parse error
void parse_into<typename T>(const char* input, T& result);
double value = 0;
parse_into("12.5", value); // #1: Success
parse_into("ABC", value); // #2: Fail
parse_into("12.5 abc", value); //#3: Fail
std::string str_value;
parse_into("ABC", str_value); // #4: Success
parse_into("ABC ABC", str_value); // #5: Success; str_value == "ABC ABC"
parse_into("ABC ABC", str_value); // #6: Success; str_value == "ABC ABC'
I'm confident that what I want revolves around stringstream
and operator >>
. I can see how to detect if not the whole string was parsed (case #3), by checking if the stringstream
is empty. However, what I can't figure out is a generic way to allow for types that can be constructed from any string, like std::string
. Perhaps a SIFNAE overload involving types that can be constructed from a const char*
?
I don't have a particular formal requirement for the final interface; what I want is for it to do what feels like the "right thing" for any given type. For context, this is for a command-line argument parsing library. The library handles detecting word boundaries, so I know that the input to parse_into
is the entire word I'm trying to read.
Upvotes: 1
Views: 81
Reputation: 34618
For a double
this can be achieved with std::stod
after first reading your input as std::string
.
std::optional<double> parse(std::string const& str) {
std::size_t num = 0;
try {
auto const res = std::stod(str,&num);
if (num < str.size()) return std::nullopt;
return res;
} catch (...) {
return std::nullopt;
}
}
You can create overloads (using a passed in out value, for example) for different types. I'm using an std::optional
here, but you can also throw
or use any other means of communicating an error.
For instance:
bool parse(std::string const& str, double& out);
bool parse(std::string const& str, std::uint32_t& out);
bool parse(std::string const& str, std::string& out); // always succeeds
// ...
Edit: A more general interface that allows to pass any type of stoXYZ
function:
template <typename T>
static void parse(std::string const& s, T (*fun)(std::string const&,std::size_t*,int), std::optional<T>& out) { /**/ }
template <typename T>
static void parse(std::string const& s, T (*fun)(std::string const&,std::size_t*), std::optional<T>& out) { /**/ }
This takes a pointer-to-function, but it could also take a std::function
object. The two overloads exist because std::stoi
and std::stod
have different argument lists (the integer ones want a base).
Upvotes: 2