implement overloads so that utils::*numeric() functions can be safely used with std::string()

This commit is contained in:
Axel Kohlmeyer
2021-12-30 10:43:33 -05:00
parent 64d6a2fd1f
commit cf9429dc68
5 changed files with 194 additions and 37 deletions

View File

@ -56,11 +56,11 @@ String to number conversions with validity check
These functions should be used to convert strings to numbers. They are These functions should be used to convert strings to numbers. They are
are strongly preferred over C library calls like ``atoi()`` or are strongly preferred over C library calls like ``atoi()`` or
``atof()`` since they check if the **entire** provided string is a valid ``atof()`` since they check if the **entire** string is a valid
(floating-point or integer) number, and will error out instead of (floating-point or integer) number, and will error out instead of
silently returning the result of a partial conversion or zero in cases silently returning the result of a partial conversion or zero in cases
where the string is not a valid number. This behavior allows to more where the string is not a valid number. This behavior improves
easily detect typos or issues when processing input files. detecting typos or issues when processing input files.
Similarly the :cpp:func:`logical() <LAMMPS_NS::utils::logical>` function Similarly the :cpp:func:`logical() <LAMMPS_NS::utils::logical>` function
will convert a string into a boolean and will only accept certain words. will convert a string into a boolean and will only accept certain words.
@ -76,19 +76,34 @@ strings for compliance without conversion.
---------- ----------
.. doxygenfunction:: numeric .. doxygenfunction:: numeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp)
:project: progguide :project: progguide
.. doxygenfunction:: inumeric .. doxygenfunction:: numeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
:project: progguide :project: progguide
.. doxygenfunction:: bnumeric .. doxygenfunction:: inumeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp)
:project: progguide :project: progguide
.. doxygenfunction:: tnumeric .. doxygenfunction:: inumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
:project: progguide :project: progguide
.. doxygenfunction:: logical .. doxygenfunction:: bnumeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp)
:project: progguide
.. doxygenfunction:: bnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
:project: progguide
.. doxygenfunction:: tnumeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp)
:project: progguide
.. doxygenfunction:: tnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
:project: progguide
.. doxygenfunction:: logical(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp)
:project: progguide
.. doxygenfunction:: logical(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
:project: progguide :project: progguide

View File

@ -55,7 +55,7 @@ of each timestep. First of all, implement a constructor:
if (narg < 4) if (narg < 4)
error->all(FLERR,"Illegal fix print/vel command"); error->all(FLERR,"Illegal fix print/vel command");
nevery = force->inumeric(FLERR,arg[3]); nevery = utils::inumeric(FLERR,arg[3]);
if (nevery <= 0) if (nevery <= 0)
error->all(FLERR,"Illegal fix print/vel command"); error->all(FLERR,"Illegal fix print/vel command");
} }

View File

@ -298,12 +298,9 @@ std::string utils::check_packages_for_style(const std::string &style, const std:
called by various commands to check validity of their arguments called by various commands to check validity of their arguments
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
int utils::logical(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp) int utils::logical(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp)
{ {
int n = 0; if (str.empty()) {
if (str) n = strlen(str);
if (n == 0) {
const char msg[] = "Expected boolean parameter instead of NULL or empty string " const char msg[] = "Expected boolean parameter instead of NULL or empty string "
"in input script or data file"; "in input script or data file";
if (do_abort) if (do_abort)
@ -332,18 +329,28 @@ int utils::logical(const char *file, int line, const char *str, bool do_abort, L
return rv; return rv;
} }
/* ----------------------------------------------------------------------
wrapper for logical() that accepts a char pointer instead of a string
------------------------------------------------------------------------- */
int utils::logical(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
{
if (str)
return logical(file, line, std::string(str), do_abort, lmp);
else
return logical(file, line, std::string(""), do_abort, lmp);
}
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
read a floating point value from a string read a floating point value from a string
generate an error if not a legitimate floating point value generate an error if not a legitimate floating point value
called by various commands to check validity of their arguments called by various commands to check validity of their arguments
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
double utils::numeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp) double utils::numeric(const char *file, int line, const std::string &str, bool do_abort,
LAMMPS *lmp)
{ {
int n = 0; if (str.empty()) {
if (str) n = strlen(str);
if (n == 0) {
const char msg[] = "Expected floating point parameter instead of" const char msg[] = "Expected floating point parameter instead of"
" NULL or empty string in input script or data file"; " NULL or empty string in input script or data file";
if (do_abort) if (do_abort)
@ -367,18 +374,27 @@ double utils::numeric(const char *file, int line, const char *str, bool do_abort
return atof(buf.c_str()); return atof(buf.c_str());
} }
/* ----------------------------------------------------------------------
wrapper for numeric() that accepts a char pointer instead of a string
------------------------------------------------------------------------- */
double utils::numeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
{
if (str)
return numeric(file, line, std::string(str), do_abort, lmp);
else
return numeric(file, line, std::string(""), do_abort, lmp);
}
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
read an integer value from a string read an integer value from a string
generate an error if not a legitimate integer value generate an error if not a legitimate integer value
called by various commands to check validity of their arguments called by various commands to check validity of their arguments
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
int utils::inumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp) int utils::inumeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp)
{ {
int n = 0; if (str.empty()) {
if (str) n = strlen(str);
if (n == 0) {
const char msg[] = "Expected integer parameter instead of" const char msg[] = "Expected integer parameter instead of"
" NULL or empty string in input script or data file"; " NULL or empty string in input script or data file";
if (do_abort) if (do_abort)
@ -402,18 +418,28 @@ int utils::inumeric(const char *file, int line, const char *str, bool do_abort,
return atoi(buf.c_str()); return atoi(buf.c_str());
} }
/* ----------------------------------------------------------------------
wrapper for inumeric() that accepts a char pointer instead of a string
------------------------------------------------------------------------- */
double utils::inumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
{
if (str)
return inumeric(file, line, std::string(str), do_abort, lmp);
else
return inumeric(file, line, std::string(""), do_abort, lmp);
}
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
read a big integer value from a string read a big integer value from a string
generate an error if not a legitimate integer value generate an error if not a legitimate integer value
called by various commands to check validity of their arguments called by various commands to check validity of their arguments
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
bigint utils::bnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp) bigint utils::bnumeric(const char *file, int line, const std::string &str, bool do_abort,
LAMMPS *lmp)
{ {
int n = 0; if (str.empty()) {
if (str) n = strlen(str);
if (n == 0) {
const char msg[] = "Expected integer parameter instead of" const char msg[] = "Expected integer parameter instead of"
" NULL or empty string in input script or data file"; " NULL or empty string in input script or data file";
if (do_abort) if (do_abort)
@ -437,18 +463,28 @@ bigint utils::bnumeric(const char *file, int line, const char *str, bool do_abor
return ATOBIGINT(buf.c_str()); return ATOBIGINT(buf.c_str());
} }
/* ----------------------------------------------------------------------
wrapper for bnumeric() that accepts a char pointer instead of a string
------------------------------------------------------------------------- */
double utils::bnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
{
if (str)
return bnumeric(file, line, std::string(str), do_abort, lmp);
else
return bnumeric(file, line, std::string(""), do_abort, lmp);
}
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
read a tag integer value from a string read a tag integer value from a string
generate an error if not a legitimate integer value generate an error if not a legitimate integer value
called by various commands to check validity of their arguments called by various commands to check validity of their arguments
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */
tagint utils::tnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp) tagint utils::tnumeric(const char *file, int line, const std::string &str, bool do_abort,
LAMMPS *lmp)
{ {
int n = 0; if (str.empty()) {
if (str) n = strlen(str);
if (n == 0) {
const char msg[] = "Expected integer parameter instead of" const char msg[] = "Expected integer parameter instead of"
" NULL or empty string in input script or data file"; " NULL or empty string in input script or data file";
if (do_abort) if (do_abort)
@ -472,6 +508,18 @@ tagint utils::tnumeric(const char *file, int line, const char *str, bool do_abor
return ATOTAGINT(buf.c_str()); return ATOTAGINT(buf.c_str());
} }
/* ----------------------------------------------------------------------
wrapper for tnumeric() that accepts a char pointer instead of a string
------------------------------------------------------------------------- */
double utils::tnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp)
{
if (str)
return tnumeric(file, line, std::string(str), do_abort, lmp);
else
return tnumeric(file, line, std::string(""), do_abort, lmp);
}
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
compute bounds implied by numeric str with a possible wildcard asterisk compute bounds implied by numeric str with a possible wildcard asterisk
------------------------------------------------------------------------- */ ------------------------------------------------------------------------- */

View File

@ -169,6 +169,17 @@ namespace utils {
* \param lmp pointer to top-level LAMMPS class instance * \param lmp pointer to top-level LAMMPS class instance
* \return 1 if string resolves to "true", otherwise 0 */ * \return 1 if string resolves to "true", otherwise 0 */
int logical(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp);
/*! \overload
*
* \param file name of source file for error message
* \param line line number in source file for error message
* \param str string to be converted to logical
* \param do_abort determines whether to call Error::one() or Error::all()
* \param lmp pointer to top-level LAMMPS class instance
* \return 1 if string resolves to "true", otherwise 0 */
int logical(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp); int logical(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp);
/*! Convert a string to a floating point number while checking /*! Convert a string to a floating point number while checking
@ -181,6 +192,17 @@ namespace utils {
* \param lmp pointer to top-level LAMMPS class instance * \param lmp pointer to top-level LAMMPS class instance
* \return double precision floating point number */ * \return double precision floating point number */
double numeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp);
/*! \overload
*
* \param file name of source file for error message
* \param line line number in source file for error message
* \param str string to be converted to number
* \param do_abort determines whether to call Error::one() or Error::all()
* \param lmp pointer to top-level LAMMPS class instance
* \return double precision floating point number */
double numeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp); double numeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp);
/*! Convert a string to an integer number while checking /*! Convert a string to an integer number while checking
@ -193,7 +215,18 @@ namespace utils {
* \param lmp pointer to top-level LAMMPS class instance * \param lmp pointer to top-level LAMMPS class instance
* \return integer number (regular int) */ * \return integer number (regular int) */
int inumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp); int inumeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp);
/*! \overload
*
* \param file name of source file for error message
* \param line line number in source file for error message
* \param str string to be converted to number
* \param do_abort determines whether to call Error::one() or Error::all()
* \param lmp pointer to top-level LAMMPS class instance
* \return double precision floating point number */
double inumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp);
/*! Convert a string to an integer number while checking /*! Convert a string to an integer number while checking
* if it is a valid integer number (bigint) * if it is a valid integer number (bigint)
@ -205,7 +238,18 @@ namespace utils {
* \param lmp pointer to top-level LAMMPS class instance * \param lmp pointer to top-level LAMMPS class instance
* \return integer number (bigint) */ * \return integer number (bigint) */
bigint bnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp); bigint bnumeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp);
/*! \overload
*
* \param file name of source file for error message
* \param line line number in source file for error message
* \param str string to be converted to number
* \param do_abort determines whether to call Error::one() or Error::all()
* \param lmp pointer to top-level LAMMPS class instance
* \return double precision floating point number */
double bnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp);
/*! Convert a string to an integer number while checking /*! Convert a string to an integer number while checking
* if it is a valid integer number (tagint) * if it is a valid integer number (tagint)
@ -217,7 +261,18 @@ namespace utils {
* \param lmp pointer to top-level LAMMPS class instance * \param lmp pointer to top-level LAMMPS class instance
* \return integer number (tagint) */ * \return integer number (tagint) */
tagint tnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp); tagint tnumeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp);
/*! \overload
*
* \param file name of source file for error message
* \param line line number in source file for error message
* \param str string to be converted to number
* \param do_abort determines whether to call Error::one() or Error::all()
* \param lmp pointer to top-level LAMMPS class instance
* \return double precision floating point number */
double tnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp);
/*! Compute index bounds derived from a string with a possible wildcard /*! Compute index bounds derived from a string with a possible wildcard
* *

View File

@ -49,6 +49,15 @@ TEST_F(InputConvertTest, logical)
EXPECT_EQ(utils::logical(FLERR, "off", false, lmp), 0); EXPECT_EQ(utils::logical(FLERR, "off", false, lmp), 0);
EXPECT_EQ(utils::logical(FLERR, "0", false, lmp), 0); EXPECT_EQ(utils::logical(FLERR, "0", false, lmp), 0);
EXPECT_EQ(utils::logical(FLERR, std::string("yes"), false, lmp), 1);
EXPECT_EQ(utils::logical(FLERR, std::string("true"), false, lmp), 1);
EXPECT_EQ(utils::logical(FLERR, std::string("on"), false, lmp), 1);
EXPECT_EQ(utils::logical(FLERR, std::string("1"), false, lmp), 1);
EXPECT_EQ(utils::logical(FLERR, std::string("no"), false, lmp), 0);
EXPECT_EQ(utils::logical(FLERR, std::string("false"), false, lmp), 0);
EXPECT_EQ(utils::logical(FLERR, std::string("off"), false, lmp), 0);
EXPECT_EQ(utils::logical(FLERR, std::string("0"), false, lmp), 0);
TEST_FAILURE(".*ERROR: Expected boolean parameter instead of.*", TEST_FAILURE(".*ERROR: Expected boolean parameter instead of.*",
utils::logical(FLERR, "YES", false, lmp);); utils::logical(FLERR, "YES", false, lmp););
TEST_FAILURE(".*ERROR: Expected boolean parameter instead of.*", TEST_FAILURE(".*ERROR: Expected boolean parameter instead of.*",
@ -94,6 +103,15 @@ TEST_F(InputConvertTest, numeric)
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, "10000000000", false, lmp), 1e10); EXPECT_DOUBLE_EQ(utils::numeric(FLERR, "10000000000", false, lmp), 1e10);
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, "2.56E+3", false, lmp), 2560); EXPECT_DOUBLE_EQ(utils::numeric(FLERR, "2.56E+3", false, lmp), 2560);
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, std::string("0"), false, lmp), 0);
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, std::string("0.1"), false, lmp), 0.1);
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, std::string("-.232"), false, lmp), -0.232);
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, std::string(".2e5"), false, lmp), 20000.0);
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, std::string("2.5e-10"), false, lmp), 2.5e-10);
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, std::string("+0.3"), false, lmp), 0.3);
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, std::string("10000000000"), false, lmp), 1e10);
EXPECT_DOUBLE_EQ(utils::numeric(FLERR, std::string("2.56E+3"), false, lmp), 2560);
TEST_FAILURE(".*ERROR: Expected floating point.*", utils::numeric(FLERR, "yay", false, lmp);); TEST_FAILURE(".*ERROR: Expected floating point.*", utils::numeric(FLERR, "yay", false, lmp););
TEST_FAILURE(".*ERROR: Expected floating point.*", utils::numeric(FLERR, "", false, lmp);); TEST_FAILURE(".*ERROR: Expected floating point.*", utils::numeric(FLERR, "", false, lmp););
TEST_FAILURE(".*ERROR: Expected floating point.*", utils::numeric(FLERR, nullptr, false, lmp);); TEST_FAILURE(".*ERROR: Expected floating point.*", utils::numeric(FLERR, nullptr, false, lmp););
@ -110,6 +128,13 @@ TEST_F(InputConvertTest, inumeric)
EXPECT_EQ(utils::inumeric(FLERR, "-0", false, lmp), 0); EXPECT_EQ(utils::inumeric(FLERR, "-0", false, lmp), 0);
EXPECT_EQ(utils::inumeric(FLERR, "0100", false, lmp), 100); EXPECT_EQ(utils::inumeric(FLERR, "0100", false, lmp), 100);
EXPECT_EQ(utils::inumeric(FLERR, std::string("0"), false, lmp), 0);
EXPECT_EQ(utils::inumeric(FLERR, std::string("-1"), false, lmp), -1);
EXPECT_EQ(utils::inumeric(FLERR, std::string("10000"), false, lmp), 10000);
EXPECT_EQ(utils::inumeric(FLERR, std::string("-532410"), false, lmp), -532410);
EXPECT_EQ(utils::inumeric(FLERR, std::string("-0"), false, lmp), 0);
EXPECT_EQ(utils::inumeric(FLERR, std::string("0100"), false, lmp), 100);
TEST_FAILURE(".*ERROR: Expected integer.*", utils::inumeric(FLERR, "yay", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::inumeric(FLERR, "yay", false, lmp););
TEST_FAILURE(".*ERROR: Expected integer.*", utils::inumeric(FLERR, "0.1", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::inumeric(FLERR, "0.1", false, lmp););
TEST_FAILURE(".*ERROR: Expected integer.*", utils::inumeric(FLERR, "1.1", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::inumeric(FLERR, "1.1", false, lmp););
@ -128,6 +153,13 @@ TEST_F(InputConvertTest, bnumeric)
EXPECT_EQ(utils::bnumeric(FLERR, "-0", false, lmp), 0); EXPECT_EQ(utils::bnumeric(FLERR, "-0", false, lmp), 0);
EXPECT_EQ(utils::bnumeric(FLERR, "0100", false, lmp), 100); EXPECT_EQ(utils::bnumeric(FLERR, "0100", false, lmp), 100);
EXPECT_EQ(utils::bnumeric(FLERR, std::string("0"), false, lmp), 0);
EXPECT_EQ(utils::bnumeric(FLERR, std::string("-1"), false, lmp), -1);
EXPECT_EQ(utils::bnumeric(FLERR, std::string("10000"), false, lmp), 10000);
EXPECT_EQ(utils::bnumeric(FLERR, std::string("-532410"), false, lmp), -532410);
EXPECT_EQ(utils::bnumeric(FLERR, std::string("-0"), false, lmp), 0);
EXPECT_EQ(utils::bnumeric(FLERR, std::string("0100"), false, lmp), 100);
TEST_FAILURE(".*ERROR: Expected integer.*", utils::bnumeric(FLERR, "yay", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::bnumeric(FLERR, "yay", false, lmp););
TEST_FAILURE(".*ERROR: Expected integer.*", utils::bnumeric(FLERR, "0.1", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::bnumeric(FLERR, "0.1", false, lmp););
TEST_FAILURE(".*ERROR: Expected integer.*", utils::bnumeric(FLERR, "1.1", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::bnumeric(FLERR, "1.1", false, lmp););
@ -146,6 +178,13 @@ TEST_F(InputConvertTest, tnumeric)
EXPECT_EQ(utils::tnumeric(FLERR, "-0", false, lmp), 0); EXPECT_EQ(utils::tnumeric(FLERR, "-0", false, lmp), 0);
EXPECT_EQ(utils::tnumeric(FLERR, "0100", false, lmp), 100); EXPECT_EQ(utils::tnumeric(FLERR, "0100", false, lmp), 100);
EXPECT_EQ(utils::tnumeric(FLERR, std::string("0"), false, lmp), 0);
EXPECT_EQ(utils::tnumeric(FLERR, std::string("-1"), false, lmp), -1);
EXPECT_EQ(utils::tnumeric(FLERR, std::string("10000"), false, lmp), 10000);
EXPECT_EQ(utils::tnumeric(FLERR, std::string("-532410"), false, lmp), -532410);
EXPECT_EQ(utils::tnumeric(FLERR, std::string("-0"), false, lmp), 0);
EXPECT_EQ(utils::tnumeric(FLERR, std::string("0100"), false, lmp), 100);
TEST_FAILURE(".*ERROR: Expected integer.*", utils::tnumeric(FLERR, "yay", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::tnumeric(FLERR, "yay", false, lmp););
TEST_FAILURE(".*ERROR: Expected integer.*", utils::tnumeric(FLERR, "0.1", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::tnumeric(FLERR, "0.1", false, lmp););
TEST_FAILURE(".*ERROR: Expected integer.*", utils::tnumeric(FLERR, "1.1", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::tnumeric(FLERR, "1.1", false, lmp););