add overloads for Error::all() and Error::one() that can point out the location of a faulty argument

This commit is contained in:
Axel Kohlmeyer
2025-01-15 23:10:50 -05:00
parent 48f49837d8
commit 8c93986e47
6 changed files with 96 additions and 24 deletions

View File

@ -103,7 +103,7 @@ void CreateAtoms::command(int narg, char **arg)
style = REGION;
if (narg < 3) utils::missing_cmd_args(FLERR, "create_atoms region", error);
region = domain->get_region_by_id(arg[2]);
if (!region) error->all(FLERR, "Create_atoms region {} does not exist", arg[2]);
if (!region) error->all(FLERR, 2, "Create_atoms region {} does not exist", arg[2]);
region->init();
region->prematch();
iarg = 3;
@ -127,7 +127,7 @@ void CreateAtoms::command(int narg, char **arg)
region = nullptr;
else {
region = domain->get_region_by_id(arg[4]);
if (!region) error->all(FLERR, "Create_atoms region {} does not exist", arg[4]);
if (!region) error->all(FLERR, 4, "Create_atoms region {} does not exist", arg[4]);
region->init();
region->prematch();
}
@ -138,7 +138,7 @@ void CreateAtoms::command(int narg, char **arg)
meshfile = arg[2];
iarg = 3;
} else
error->all(FLERR, "Unknown create_atoms command option {}", arg[1]);
error->all(FLERR, 1, "Unknown create_atoms command option {}", arg[1]);
// process optional keywords

View File

@ -114,7 +114,7 @@ void Error::universe_warn(const std::string &file, int line, const std::string &
force MPI_Abort if running in multi-partition mode
------------------------------------------------------------------------- */
void Error::all(const std::string &file, int line, const std::string &str)
void Error::all(const std::string &file, int line, int failed, const std::string &str)
{
MPI_Barrier(world);
@ -125,9 +125,11 @@ void Error::all(const std::string &file, int line, const std::string &str)
if (me == 0) {
std::string mesg = "ERROR: " + str;
if (input && input->line) lastcmd = input->line;
try {
mesg += fmt::format(" ({}:{})\nLast command: {}\n", truncpath(file),line,lastcmd);
if (failed > NOPOINTER) mesg += utils::point_to_error(input, failed);
} catch (fmt::format_error &) {
; // do nothing
}
@ -140,6 +142,7 @@ void Error::all(const std::string &file, int line, const std::string &str)
if (update) update->whichflag = 0;
std::string msg = fmt::format("ERROR: {} ({}:{})\n", str, truncpath(file), line);
if (failed > NOPOINTER) msg += utils::point_to_error(input, failed);
if (universe->nworlds > 1)
throw LAMMPSAbortException(msg, universe->uworld);
@ -154,15 +157,21 @@ void Error::all(const std::string &file, int line, const std::string &str)
forces abort of entire world (and universe) if any proc in world calls
------------------------------------------------------------------------- */
void Error::one(const std::string &file, int line, const std::string &str)
void Error::one(const std::string &file, int line, int failed, const std::string &str)
{
int me;
std::string lastcmd = "(unknown)";
MPI_Comm_rank(world,&me);
if (input && input->line) lastcmd = input->line;
std::string mesg = fmt::format("ERROR on proc {}: {} ({}:{})\nLast command: {}\n",
me,str,truncpath(file),line,lastcmd);
std::string mesg;
try {
mesg = fmt::format("ERROR on proc {}: {} ({}:{})\nLast command: {}\n",
me,str,truncpath(file),line,lastcmd);
if (failed > NOPOINTER) mesg += utils::point_to_error(input, failed);
} catch (fmt::format_error &) {
; // do nothing
}
utils::logmesg(lmp,mesg);
if (universe->nworlds > 1)
@ -177,27 +186,27 @@ void Error::one(const std::string &file, int line, const std::string &str)
}
/* ----------------------------------------------------------------------
forward vararg version to single string version
forward vararg versions to single string version
------------------------------------------------------------------------- */
void Error::_all(const std::string &file, int line, fmt::string_view format,
void Error::_all(const std::string &file, int line, int failed, fmt::string_view format,
fmt::format_args args)
{
try {
all(file,line,fmt::vformat(format, args));
all(file, line, failed, fmt::vformat(format, args));
} catch (fmt::format_error &e) {
all(file,line,e.what());
all(file, line, NOPOINTER, e.what());
}
exit(1); // to trick "smart" compilers into believing this does not return
}
void Error::_one(const std::string &file, int line, fmt::string_view format,
void Error::_one(const std::string &file, int line, int failed, fmt::string_view format,
fmt::format_args args)
{
try {
one(file,line,fmt::vformat(format, args));
one(file, line, failed, fmt::vformat(format, args));
} catch (fmt::format_error &e) {
one(file,line,e.what());
one(file, line, NOPOINTER, e.what());
}
exit(1); // to trick "smart" compilers into believing this does not return
}

View File

@ -27,18 +27,49 @@ class Error : protected Pointers {
[[noreturn]] void universe_one(const std::string &, int, const std::string &);
void universe_warn(const std::string &, int, const std::string &);
[[noreturn]] void all(const std::string &, int, const std::string &);
template <typename... Args>
[[noreturn]] void all(const std::string &file, int line, const std::string &format, Args &&...args)
static constexpr int NOPOINTER = -2;
// regular error calls
[[noreturn]] void all(const std::string &file, int line, const std::string &str)
{
_all(file, line, format, fmt::make_format_args(args...));
all(file, line, NOPOINTER, str);
}
[[noreturn]] void one(const std::string &, int, const std::string &);
template <typename... Args>
[[noreturn]] void one(const std::string &file, int line, const std::string &format, Args &&...args)
[[noreturn]] void all(const std::string &file, int line, const std::string &format,
Args &&...args)
{
_one(file, line, format, fmt::make_format_args(args...));
_all(file, line, NOPOINTER, format, fmt::make_format_args(args...));
}
[[noreturn]] void one(const std::string &file, int line, const std::string &str)
{
one(file, line, NOPOINTER, str);
}
template <typename... Args>
[[noreturn]] void one(const std::string &file, int line, const std::string &format,
Args &&...args)
{
_one(file, line, NOPOINTER, format, fmt::make_format_args(args...));
}
// overloaded error calls indicating faulty argument in command line
[[noreturn]] void all(const std::string &, int, int, const std::string &);
template <typename... Args>
[[noreturn]] void all(const std::string &file, int line, int failed, const std::string &format,
Args &&...args)
{
_all(file, line, failed, format, fmt::make_format_args(args...));
}
[[noreturn]] void one(const std::string &, int, int, const std::string &);
template <typename... Args>
[[noreturn]] void one(const std::string &file, int line, int failed, const std::string &format,
Args &&...args)
{
_one(file, line, failed, format, fmt::make_format_args(args...));
}
void warning(const std::string &, int, const std::string &);
@ -72,8 +103,8 @@ class Error : protected Pointers {
int numwarn, maxwarn, allwarn;
// internal versions that accept explicit fmtlib arguments
[[noreturn]] void _all(const std::string &, int, fmt::string_view, fmt::format_args args);
[[noreturn]] void _one(const std::string &, int, fmt::string_view, fmt::format_args args);
[[noreturn]] void _all(const std::string &, int, int, fmt::string_view, fmt::format_args args);
[[noreturn]] void _one(const std::string &, int, int, fmt::string_view, fmt::format_args args);
void _warning(const std::string &, int, fmt::string_view, fmt::format_args args);
void _message(const std::string &, int, fmt::string_view, fmt::format_args args);
};

View File

@ -28,6 +28,7 @@ class Input : protected Pointers {
friend class SimpleCommandsTest_Echo_Test;
public:
char *command; // ptr to current command
int narg; // # of command args
char **arg; // parsed args for command
class Variable *variable; // defined variables
@ -42,7 +43,6 @@ class Input : protected Pointers {
int get_jump_skip() const { return jump_skip; }
protected:
char *command; // ptr to current command
int echo_screen; // 0 = no, 1 = yes
int echo_log; // 0 = no, 1 = yes

View File

@ -132,6 +132,28 @@ void utils::missing_cmd_args(const std::string &file, int line, const std::strin
if (error) error->all(file, line, "Illegal {} command: missing argument(s)", cmd);
}
std::string utils::point_to_error(Input *input, int failedarg)
{
if (input) {
std::string cmdline = "Preprocessed: ";
int indicator = cmdline.size(); // error indicator points to command by default
cmdline += input->command;
cmdline += ' ';
// assemble pre-processed command line and update error indicator position, if needed.
for (int i = 0; i < input->narg; ++i) {
if (i == failedarg) indicator = cmdline.size();
cmdline += input->arg[i];
cmdline += ' ';
}
// construct and append error indicator line
cmdline += '\n';
cmdline += std::string(indicator, ' ');
cmdline += "^\n";
return cmdline;
} else return std::string("(Failed command line text not available)");
}
/* specialization for the case of just a single string argument */
void utils::logmesg(LAMMPS *lmp, const std::string &mesg)

View File

@ -28,6 +28,7 @@ namespace LAMMPS_NS {
// forward declarations
class Error;
class Input;
class LAMMPS;
namespace utils {
@ -59,6 +60,15 @@ namespace utils {
void missing_cmd_args(const std::string &file, int line, const std::string &cmd, Error *error);
/*! Create string with last command after pre-processing and pointing to arg with error
*
* This function is a helper function for error messages. It creates
*
* \param input pointer to the Input class instance (for access to last command args)
* \param failedarg index of the faulty argument (-1 to point to the command itself)
* \return string with two lines: the pre-processed command and a '^' pointing to the faulty argument */
std::string point_to_error(Input *input, int failedarg);
/*! Internal function handling the argument list for logmesg(). */
void fmtargs_logmesg(LAMMPS *lmp, fmt::string_view format, fmt::format_args args);