Description:
Follow-up to #206 (silent truncation of narrow types). The range check added in #210 covers signed char, unsigned char, short, and unsigned short, but the wider unsigned types (unsigned, unsigned long, unsigned long long) still accept negative string inputs silently.
std::stoul("-1") returns ULONG_MAX per the C++ standard (it delegates to strtoul, which wraps negatives). So GetCell<unsigned> on a cell containing "-1" returns 4294967295 instead of throwing. This is inconsistent with the narrow-type behavior where GetCell<unsigned char> on "-1" correctly throws std::out_of_range after #210.
How to reproduce it:
#include <rapidcsv.h>
#include <sstream>
#include <iostream>
#include <climits>
int main()
{
std::istringstream ss("val\n-1\n-100\n");
rapidcsv::Document doc(ss);
unsigned u = doc.GetCell<unsigned>(0, 0);
std::cout << "-1 as unsigned = " << u << std::endl;
// Output: -1 as unsigned = 4294967295 (UINT_MAX, no exception!)
unsigned long ul = doc.GetCell<unsigned long>(0, 0);
std::cout << "-1 as unsigned long = " << ul << std::endl;
// Output: -1 as unsigned long = 18446744073709551615 (ULONG_MAX)
unsigned long long ull = doc.GetCell<unsigned long long>(0, 0);
std::cout << "-1 as unsigned long long = " << ull << std::endl;
// Output: -1 as unsigned long long = 18446744073709551615 (ULLONG_MAX)
// Compare: narrow types correctly throw after #210
try
{
unsigned char uc = doc.GetCell<unsigned char>(0, 0);
std::cout << "unsigned char didn't throw?! " << static_cast<int>(uc) << std::endl;
}
catch (const std::out_of_range& e)
{
std::cout << "unsigned char correctly threw: " << e.what() << std::endl;
}
}
Environment:
- Version: 8.97
- OS / distro: macOS Sonoma / Ubuntu 24.04 (header-only, platform-independent)
Proposed fix:
Check if the input string starts with - (after skipping whitespace) in the stoul and stoull branches (lines 216-230), and throw std::out_of_range if so. This matches what the narrow-type path already does via std::numeric_limits bounds checking.
Something like:
// After stoul/stoull call, before return:
const auto firstNonSpace = pStr.find_first_not_of(" \t");
if (firstNonSpace != std::string::npos && pStr[firstNonSpace] == '-')
{
throw std::out_of_range("conversion: negative value for unsigned type");
}
Happy to send a PR if you'd like.
Description:
Follow-up to #206 (silent truncation of narrow types). The range check added in #210 covers
signed char,unsigned char,short, andunsigned short, but the wider unsigned types (unsigned,unsigned long,unsigned long long) still accept negative string inputs silently.std::stoul("-1")returnsULONG_MAXper the C++ standard (it delegates tostrtoul, which wraps negatives). SoGetCell<unsigned>on a cell containing"-1"returns4294967295instead of throwing. This is inconsistent with the narrow-type behavior whereGetCell<unsigned char>on"-1"correctly throwsstd::out_of_rangeafter #210.How to reproduce it:
Environment:
Proposed fix:
Check if the input string starts with
-(after skipping whitespace) in thestoulandstoullbranches (lines 216-230), and throwstd::out_of_rangeif so. This matches what the narrow-type path already does viastd::numeric_limitsbounds checking.Something like:
Happy to send a PR if you'd like.