c4core
c4core copied to clipboard
Should `atod` fail when different from `inf` or `infinity`
A string that begins with inf, regardless of what comes after (except "" and "inity"), will silently succeed :
double d;
char str[] = "Informed"
if (atod(str, &d)) {
std::cout << "This should fail !" << std::endl;
std::cout << d << std::endl;
}
// This should fail !
// inf
Does atod do whole string matching ? Is this the intended behavior.
My use case is for decoding primitives not known in advance in a specific order :
{ // bool
bool value;
if (from_chars(substr, &value))
return XmlRpc::XmlRpcValue(value);
}
{ // int
int value;
if (from_chars(substr, &value))
return XmlRpc::XmlRpcValue(value);
}
{ // double
double value;
if (from_chars(substr, &value))
return XmlRpc::XmlRpcValue(value);
}
{ // string
std::string value;
if (from_chars(substr, &value))
return XmlRpc::XmlRpcValue(value);
}
Ultimately, is it up to me to check for a inf character is valid ?
So adding this patch, works as expected for my use case (Based on https://github.com/fastfloat/fast_float/issues/164). We would need to check that the string has been fully consumed by fastfloat. Some other functions would maybe need this change :
atod-> C4CORE_HAVE_STD_FROMCHARSatof- other ?
C4_ALWAYS_INLINE bool atod(csubstr str, double * C4_RESTRICT v) noexcept
{
C4_ASSERT(str.triml(" \r\t\n").len == str.len);
#if C4CORE_HAVE_FAST_FLOAT
// fastfloat cannot parse hexadecimal floats
bool isneg = (str.str[0] == '-');
csubstr rem = str.sub(isneg || str.str[0] == '+');
if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
{
fast_float::from_chars_result result;
result = fast_float::from_chars(str.str, str.str + str.len, *v);
// str must be fully consumed to be valid
if(result.ptr - str.str != str.len)
result.ec = std::errc::invalid_argument;
return result.ec == std::errc();
}
else if(detail::scan_rhex(rem.sub(2), v))
{
*v *= isneg ? -1. : 1.;
return true;
}
return false;
#elif C4CORE_HAVE_STD_FROMCHARS
std::from_chars_result result;
result = std::from_chars(str.str, str.str + str.len, *v);
return result.ec == std::errc();
#else
csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+');
if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X'))))
return detail::scan_one(str, "lf", v) != csubstr::npos;
else
return detail::scan_one(str, "la", v) != csubstr::npos;
#endif
}