diff --git a/doc/src/Developer_utils.rst b/doc/src/Developer_utils.rst index db47a9e3c3..a9969b7543 100644 --- a/doc/src/Developer_utils.rst +++ b/doc/src/Developer_utils.rst @@ -56,11 +56,11 @@ String to number conversions with validity check These functions should be used to convert strings to numbers. They are 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 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 -easily detect typos or issues when processing input files. +where the string is not a valid number. This behavior improves +detecting typos or issues when processing input files. Similarly the :cpp:func:`logical() ` function 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 -.. doxygenfunction:: inumeric +.. doxygenfunction:: numeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp) :project: progguide -.. doxygenfunction:: bnumeric +.. doxygenfunction:: inumeric(const char *file, int line, const std::string &str, bool do_abort, LAMMPS *lmp) :project: progguide -.. doxygenfunction:: tnumeric +.. doxygenfunction:: inumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp) :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 diff --git a/doc/src/Developer_write.rst b/doc/src/Developer_write.rst index c3df6ad6bb..bdb51540c7 100644 --- a/doc/src/Developer_write.rst +++ b/doc/src/Developer_write.rst @@ -55,7 +55,7 @@ of each timestep. First of all, implement a constructor: if (narg < 4) error->all(FLERR,"Illegal fix print/vel command"); - nevery = force->inumeric(FLERR,arg[3]); + nevery = utils::inumeric(FLERR,arg[3]); if (nevery <= 0) error->all(FLERR,"Illegal fix print/vel command"); } diff --git a/src/utils.cpp b/src/utils.cpp index f70a60da7c..81d886f3ed 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -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 ------------------------------------------------------------------------- */ -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) n = strlen(str); - if (n == 0) { + if (str.empty()) { const char msg[] = "Expected boolean parameter instead of NULL or empty string " "in input script or data file"; if (do_abort) @@ -332,18 +329,28 @@ int utils::logical(const char *file, int line, const char *str, bool do_abort, L 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 generate an error if not a legitimate floating point value 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) n = strlen(str); - if (n == 0) { + if (str.empty()) { const char msg[] = "Expected floating point parameter instead of" " NULL or empty string in input script or data file"; 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()); } +/* ---------------------------------------------------------------------- + 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 generate an error if not a legitimate integer value 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) n = strlen(str); - if (n == 0) { + if (str.empty()) { const char msg[] = "Expected integer parameter instead of" " NULL or empty string in input script or data file"; 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()); } +/* ---------------------------------------------------------------------- + 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 generate an error if not a legitimate integer value 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) n = strlen(str); - if (n == 0) { + if (str.empty()) { const char msg[] = "Expected integer parameter instead of" " NULL or empty string in input script or data file"; 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()); } +/* ---------------------------------------------------------------------- + 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 generate an error if not a legitimate integer value 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) n = strlen(str); - if (n == 0) { + if (str.empty()) { const char msg[] = "Expected integer parameter instead of" " NULL or empty string in input script or data file"; 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()); } +/* ---------------------------------------------------------------------- + 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 ------------------------------------------------------------------------- */ diff --git a/src/utils.h b/src/utils.h index 1feee26f27..4a8080e2ba 100644 --- a/src/utils.h +++ b/src/utils.h @@ -169,6 +169,17 @@ namespace utils { * \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 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); /*! 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 * \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); /*! Convert a string to an integer number while checking @@ -193,7 +215,18 @@ namespace utils { * \param lmp pointer to top-level LAMMPS class instance * \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 * if it is a valid integer number (bigint) @@ -205,7 +238,18 @@ namespace utils { * \param lmp pointer to top-level LAMMPS class instance * \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 * if it is a valid integer number (tagint) @@ -217,7 +261,18 @@ namespace utils { * \param lmp pointer to top-level LAMMPS class instance * \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 * diff --git a/unittest/formats/test_input_convert.cpp b/unittest/formats/test_input_convert.cpp index 0ff6878b13..5930824735 100644 --- a/unittest/formats/test_input_convert.cpp +++ b/unittest/formats/test_input_convert.cpp @@ -49,6 +49,15 @@ TEST_F(InputConvertTest, logical) EXPECT_EQ(utils::logical(FLERR, "off", 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.*", utils::logical(FLERR, "YES", false, lmp);); 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, "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, "", 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, "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, "0.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, "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, "0.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, "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, "0.1", false, lmp);); TEST_FAILURE(".*ERROR: Expected integer.*", utils::tnumeric(FLERR, "1.1", false, lmp););