diff --git a/CMakeLists.txt b/CMakeLists.txt index ef41b1c..51a77d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,6 +167,7 @@ if(RAPIDCSV_BUILD_TESTS) if(HAS_CODECVT) add_unit_test(test085) endif() + add_unit_test(test086) # perf tests add_perf_test(ptest001) diff --git a/src/rapidcsv.h b/src/rapidcsv.h index 9831f3e..f27372e 100644 --- a/src/rapidcsv.h +++ b/src/rapidcsv.h @@ -2,7 +2,7 @@ * rapidcsv.h * * URL: https://github.com/d99kris/rapidcsv - * Version: 8.52 + * Version: 8.53 * * Copyright (C) 2017-2021 Kristofer Berggren * All rights reserved. @@ -568,9 +568,22 @@ namespace rapidcsv { if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx) { - T val; - converter.ToVal(itRow->at(columnIdx), val); - column.push_back(val); + if (columnIdx < static_cast(itRow->size())) + { + T val; + converter.ToVal(itRow->at(columnIdx), val); + column.push_back(val); + } + else + { + const std::string errStr = "requested column index " + + std::to_string(columnIdx - (mLabelParams.mRowNameIdx + 1)) + " >= " + + std::to_string(itRow->size() - (mLabelParams.mRowNameIdx + 1)) + + " (number of columns on row index " + + std::to_string(std::distance(mData.begin(), itRow) - + (mLabelParams.mColumnNameIdx + 1)) + ")"; + throw std::out_of_range(errStr); + } } } return column; diff --git a/tests/test086.cpp b/tests/test086.cpp new file mode 100644 index 0000000..e34185e --- /dev/null +++ b/tests/test086.cpp @@ -0,0 +1,43 @@ +// test086.cpp - exception message reading out-of-range column + +#include +#include "unittest.h" + +int main() +{ + int rv = 0; + + std::string csv = + "-,A,B,C\n" + "1,3,9,81\n" + "2,4,16\n" + ; + + std::string path = unittest::TempPath(); + unittest::WriteFile(path, csv); + + try + { + rapidcsv::Document doc(path, rapidcsv::LabelParams(0, 0)); + + unittest::ExpectEqual(std::string, doc.GetColumnName(0), "A"); + unittest::ExpectEqual(std::string, doc.GetColumnName(1), "B"); + unittest::ExpectEqual(std::string, doc.GetColumnName(2), "C"); + + ExpectExceptionMsg(doc.GetColumn(2), std::out_of_range, + "requested column index 2 >= 2 (number of columns on row index 1)"); + ExpectExceptionMsg(doc.GetColumn("C"), std::out_of_range, + "requested column index 2 >= 2 (number of columns on row index 1)"); + ExpectExceptionMsg(doc.GetColumn(3), std::out_of_range, + "requested column index 3 >= 3 (number of columns on row index 0)"); + } + catch (const std::exception& ex) + { + std::cout << ex.what() << std::endl; + rv = 1; + } + + unittest::DeleteFile(path); + + return rv; +} diff --git a/tests/unittest.h b/tests/unittest.h index 4126a54..606c035 100644 --- a/tests/unittest.h +++ b/tests/unittest.h @@ -47,6 +47,50 @@ while (0) +#define ExpectExceptionMsg(expr, excp, msg) \ + do \ + { \ + bool success = false; \ + try \ + { \ + expr; \ + } \ + catch (const excp& ex) \ + { \ + if (std::string(ex.what()) == msg) \ + { \ + success = true; \ + } \ + else \ + { \ + std::stringstream ss; \ + ss << unittest::detail::FileName(__FILE__) << ":" << std::to_string(__LINE__); \ + ss << " ExpectExceptionMsg failed: unexpected exception message '" << ex.what(); \ + ss << "'." << std::endl; \ + throw std::runtime_error(ss.str()); \ + } \ + } \ + catch (const std::exception& ex) \ + { \ + std::stringstream ss; \ + ss << unittest::detail::FileName(__FILE__) << ":" << std::to_string(__LINE__); \ + ss << " ExpectExceptionMsg failed: unexpected exception '" << typeid(ex).name(); \ + ss << "' thrown." << std::endl; \ + throw std::runtime_error(ss.str()); \ + } \ + \ + if (!success) \ + { \ + std::stringstream ss; \ + ss << unittest::detail::FileName(__FILE__) << ":" << std::to_string(__LINE__); \ + ss << " ExpectException failed: expected exception '" << #excp << "' not thrown."; \ + ss << std::endl; \ + throw std::runtime_error(ss.str()); \ + } \ + } \ + while (0) + + namespace unittest { namespace detail