Add new command line parser

parent 7fe1ae78
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
#include "tools/CXMLConfig.hpp" #include "tools/CXMLConfig.hpp"
#endif #endif
#include "tools/args.hh"
#include "tools/help.hh" #include "tools/help.hh"
#include "tools/Logger.hh" #include "tools/Logger.hh"
#include "tools/ProgressBar.hh" #include "tools/ProgressBar.hh"
...@@ -88,25 +89,6 @@ void exchangeBottomTopGhostLayers( const int i_bottomNeighborRank, SWE_Block1D* ...@@ -88,25 +89,6 @@ void exchangeBottomTopGhostLayers( const int i_bottomNeighborRank, SWE_Block1D*
const int i_topNeighborRank, SWE_Block1D* o_topNeighborInflow, SWE_Block1D* i_topNeighborOutflow, const int i_topNeighborRank, SWE_Block1D* o_topNeighborInflow, SWE_Block1D* i_topNeighborOutflow,
const MPI_Datatype i_mpiRow); const MPI_Datatype i_mpiRow);
// Get command line argument by the specified name.
static char* getArgByName(std::vector<std::string> vargs, std::string arg_name, char** argv)
{
std::vector<std::string>::iterator it = std::find(vargs.begin(), vargs.end(), arg_name);
if (it == vargs.end()) {
std::cerr << "Cannot find the command line argument matching the name \"" <<
arg_name << "\"" << endl;
// abort MPI execution
MPI_Abort(MPI_COMM_WORLD, -1);
exit(1);
}
return argv[it - vargs.begin()];
}
#define ARG(arg_name) getArgByName(vargs, arg_name, argv)
/** /**
* Main program for the simulation on a single SWE_WavePropagationBlock. * Main program for the simulation on a single SWE_WavePropagationBlock.
*/ */
...@@ -141,32 +123,33 @@ int main( int argc, char** argv ) { ...@@ -141,32 +123,33 @@ int main( int argc, char** argv ) {
tools::Logger::logger.printNumberOfProcesses(l_numberOfProcesses); tools::Logger::logger.printNumberOfProcesses(l_numberOfProcesses);
// check if the necessary command line input parameters are given // check if the necessary command line input parameters are given
tools::Args args;
#ifndef READXML #ifndef READXML
std::vector<std::string> vargs; args.addOption("grid-size-x", 'x', "Number of cell in x direction");
vargs.push_back(argv[0]); args.addOption("grid-size-y", 'y', "Number of cell in y direction");
vargs.push_back("grid_size_x"); args.addOption("output-basepath", 'o', "Output base file name");
vargs.push_back("grid_size_y"); args.addOption("output-steps-count", 'c', "Number of output time steps");
vargs.push_back("output_basepath");
vargs.push_back("output_steps_count");
#ifdef ASAGI #ifdef ASAGI
vargs.push_back("bathymetry_file"); args.addOption("bathymetry-file", 'b', "File containing the bathymetry");
vargs.push_back("displacement_file"); args.addOption("displacement-file", 'd', "File containing the displacement");
vargs.push_back("simul_area_min_x"); args.addOption("simul-area-min-x", 0, "Simulation area");
vargs.push_back("simul_area_max_x"); args.addOption("simul-area-max-x", 0, "Simulation area");
vargs.push_back("simul_area_min_y"); args.addOption("simul-area-min-y", 0, "Simulation area");
vargs.push_back("simul_area_max_y"); args.addOption("simul-area-max-y", 0, "Simulation area");
vargs.push_back("simul_duration_secs"); args.addOption("simul-duration", 0, "Simulation time in seconds");
#endif
#endif #endif
if (argc != vargs.size()) { tools::Args::Result ret = args.parse(argc, argv, l_mpiRank == 0);
std::cout << "Usage: " << vargs[0];
for (int i = 1, e = vargs.size(); i != e; i++)
std::cout << " <" << vargs[i] << ">";
std::cout << std::endl << std::flush;
MPI_Finalize(); switch (ret)
{
case tools::Args::Error:
MPI_Abort(MPI_COMM_WORLD, -1);
return 1; return 1;
case tools::Args::Help:
MPI_Finalize();
return 0;
} }
#endif
//! total number of grid cell in x- and y-direction. //! total number of grid cell in x- and y-direction.
int l_nX, l_nY; int l_nX, l_nY;
...@@ -176,9 +159,9 @@ int main( int argc, char** argv ) { ...@@ -176,9 +159,9 @@ int main( int argc, char** argv ) {
// read command line parameters // read command line parameters
#ifndef READXML #ifndef READXML
l_nX = atoi(ARG("grid_size_x")); l_nX = args.getArgument<int>("grid-size-x");
l_nY = atoi(ARG("grid_size_y")); l_nY = args.getArgument<int>("grid-size-y");
l_baseName = std::string(ARG("output_basepath")); l_baseName = args.getArgument<std::string>("output-basepath");
#endif #endif
// read xml file // read xml file
...@@ -230,14 +213,14 @@ int main( int argc, char** argv ) { ...@@ -230,14 +213,14 @@ int main( int argc, char** argv ) {
//simulation area //simulation area
float simulationArea[4]; float simulationArea[4];
simulationArea[0] = atof(ARG("simul_area_min_x")); simulationArea[0] = args.getArgument<float>("simul-area-min-x");
simulationArea[1] = atof(ARG("simul_area_max_x")); simulationArea[1] = args.getArgument<float>("simul-area-max-x");
simulationArea[2] = atof(ARG("simul_area_min_y")); simulationArea[2] = args.getArgument<float>("simul-area-min-y");
simulationArea[3] = atof(ARG("simul_area_max_y")); simulationArea[3] = args.getArgument<float>("simul-area-max-y");
float simulationDuration = atof(ARG("simul_duration_secs")); float simulationDuration = args.getArgument<float>("simul-duration");
SWE_AsagiScenario l_scenario(ARG("bathymetry_file"), ARG("displacement_file"), SWE_AsagiScenario l_scenario(args.getArgument<std::string>("bathymetry-file"), args.getArgument<std::string>("displacement-file"),
simulationDuration, simulationArea); simulationDuration, simulationArea);
#else #else
// create a simple artificial scenario // create a simple artificial scenario
...@@ -245,7 +228,7 @@ int main( int argc, char** argv ) { ...@@ -245,7 +228,7 @@ int main( int argc, char** argv ) {
#endif #endif
//! number of checkpoints for visualization (at each checkpoint in time, an output file is written). //! number of checkpoints for visualization (at each checkpoint in time, an output file is written).
int l_numberOfCheckPoints = atoi(ARG("output_steps_count")); int l_numberOfCheckPoints = args.getArgument<int>("output-steps-count");
//! number of grid cells in x- and y-direction per process. //! number of grid cells in x- and y-direction per process.
int l_nXLocal, l_nYLocal; int l_nXLocal, l_nYLocal;
......
/**
* @file
* This file is part of SWE.
*
* @author Sebastian Rettenberger (rettenbs AT in.tum.de, http://www5.in.tum.de/wiki/index.php/Sebastian_Rettenberger,_M.Sc.)
*
* @section LICENSE
*
* SWE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SWE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SWE. If not, see <http://www.gnu.org/licenses/>.
*
*
* @section DESCRIPTION
*
* Command line argument parser
*/
#ifndef TOOLS_ARGS_H
#define TOOLS_ARGS_H
#include <getopt.h>
#include <map>
#include <iomanip>
#include <string>
#include <sstream>
namespace tools
{
/**
* Parses command line arguments
*/
class Args
{
private:
struct optionInfo {
std::string longOption; // We need a copy here to get the const char* correct
/** Name of the value in the help command */
std::string value;
std::string description;
bool required;
};
/**
* Convert a long option into an "argument" that is shown in the help message
*/
struct valueConvert {
void operator()(char& c)
{
c = toupper(static_cast<unsigned char>(c));
switch (c) {
case '-':
c = '_';
break;
}
}
};
/** Program description (can be empty) */
const std::string m_description;
/** Automatically add help option */
const bool m_addHelp;
/** The command line arguments */
std::vector<struct option> m_options;
/**
* Additional information for the command line arguments
* required to generate help information
*/
std::vector<optionInfo> m_optionInfo;
/** Maps from short option to index in m_options */
std::map<char, size_t> m_short2option;
/** Contains the arguments after parse was called */
std::map<std::string, std::string> m_arguments;
public:
enum Argument
{
Required = required_argument,
No = no_argument,
Optional = optional_argument
};
enum Result
{
Success = 0,
Error,
/** Help message printed */
Help
};
public:
Args(const std::string &description = "", bool addHelp = true)
: m_description(description),
m_addHelp(addHelp)
{
}
void addOption(const std::string &longOption,
char shortOption = 0,
const std::string& description = "",
Argument argument = Required,
bool required = true)
{
std::string value;
if (shortOption)
m_short2option[shortOption] = m_options.size();
if (argument != No) {
value = longOption;
for_each(value.begin(), value.end(), valueConvert());
}
struct optionInfo i = {longOption, value, description, required};
m_optionInfo.push_back(i);
struct option o = {m_optionInfo.back().longOption.c_str(), argument, 0, shortOption};
m_options.push_back(o);
}
/**
* @return True of options are successfully parsed, false otherwise
*/
Result parse(int argc, char* const* argv, bool printHelp = true)
{
if (m_addHelp)
addOption("help", 'h', "Show this help message", No, false);
std::ostringstream shortOptions;
for (std::vector<struct option>::const_iterator i = m_options.begin();
i != m_options.end(); i++) {
if (i->val != 0) {
shortOptions << static_cast<char>(i->val);
switch (i->has_arg)
{
case required_argument:
shortOptions << ':';
break;
case optional_argument:
shortOptions << "::";
break;
}
}
}
// Add null option
struct option o = {0, 0, 0, 0};
m_options.push_back(o);
while (true) {
int optionIndex = 0;
int c = getopt_long(argc, argv, shortOptions.str().c_str(),
&m_options[0], &optionIndex);
if (c < 0)
break;
switch (c) {
case '?':
if (printHelp)
helpMessage(argv[0], std::cerr);
return Error;
case 0:
// Nothing to do
break;
default:
optionIndex = m_short2option.at(c);
}
if (optarg == 0L)
m_arguments[m_options[optionIndex].name] = "";
else
m_arguments[m_options[optionIndex].name] = optarg;
}
if (m_addHelp && isSet("help")) {
if (printHelp)
helpMessage(argv[0]);
return Help;
}
for (std::vector<optionInfo>::const_iterator i = m_optionInfo.begin();
i != m_optionInfo.end(); i++) {
if (i->required && !isSet(i->longOption)) {
if (printHelp) {
std::cerr << argv[0] << ": option --" << i->longOption << " is required" << std::endl;
helpMessage(argv[0], std::cerr);
}
return Error;
}
}
return Success;
}
bool isSet(const std::string &option)
{
return m_arguments.find(option) != m_arguments.end();
}
template<typename T>
T getArgument(const std::string &option)
{
std::istringstream ss(m_arguments.at(option));
T result;
ss >> result;
return result;
}
template<typename T>
T getArgument(const std::string &option, T defaultArgument)
{
if (!isSet(option))
return defaultArgument;
return getArgument<T>(option);
}
void helpMessage(const char* prog, std::ostream &out = std::cout)
{
// First line with all short options
out << "Usage: " << prog;
for (size_t i = 0; i < m_options.size()-1; i++) {
out << ' ';
if (!m_optionInfo[i].required)
out << '[';
if (m_options[i].val != 0)
out << '-' << static_cast<char>(m_options[i].val);
else
out << "--" << m_options[i].name;
argumentInfo(i, out);
if (!m_optionInfo[i].required)
out << ']';
}
out << std::endl;
// General program description
if (!m_description.empty())
out << std::endl << m_description << std::endl;
// Optional arguments
out << std::endl << "optional arguments:" << std::endl;
for (size_t i = 0; i < m_options.size()-1; i++) {
out << " ";
// Number of characters used for the option
size_t length = 2;
if (m_options[i].val != 0) {
out << '-' << static_cast<char>(m_options[i].val);
out << ", ";
length += 4;
}
out << "--" << m_options[i].name;
length += m_optionInfo[i].longOption.size() + 2;
length += argumentInfo(i, out);
if (length >= 30) {
out << std::endl;
out << std::setw(30) << ' ';
} else
out << std::setw(30-length) << ' ';
out << m_optionInfo[i].description << std::endl;
}
}
private:
/**
* Writes the argument information to out
*
* @param i The index of the option for which the argument should be generated
* @return The number if characters written
*/
size_t argumentInfo(size_t i, std::ostream &out)
{
switch (m_options[i].has_arg) {
case required_argument:
out << ' ' << m_optionInfo[i].value;
return m_optionInfo[i].value.size() + 1;
case optional_argument:
out << " [" << m_optionInfo[i].value << ']';
return m_optionInfo[i].value.size() + 3;
}
return 0;
}
};
template<> inline
std::string tools::Args::getArgument(const std::string &option)
{
return m_arguments.at(option);
}
}
#endif // TOOLS_ARGS_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment