Help string generation done.

Boolean parsing done.
This commit is contained in:
Jerome 2021-12-10 23:39:26 +01:00
commit 1cb3ecd9a0
6 changed files with 494 additions and 0 deletions

72
Makefile Normal file
View file

@ -0,0 +1,72 @@
CXX ?= g++
# path #
SRC_PATH = src
BUILD_PATH = build
BIN_PATH = $(BUILD_PATH)/bin
# executable #
BIN_NAME = argParseCpp
# extensions #
SRC_EXT = cpp
# code lists #
# Find all source files in the source directory, sorted by
# most recently modified
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' | sort -k 1nr | cut -f2-)
# Set the object file names, with the source directory stripped
# from the path, and the build path prepended in its place
OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o)
# Set the dependency files that will be used to add header dependencies
DEPS = $(OBJECTS:.o=.d)
# flags #
COMPILE_FLAGS = -std=c++11 -Wall -Wextra -g
INCLUDES = -I include/ -I /usr/local/include
# Space-separated pkg-config libraries used by this project
LIBS =
.PHONY: default_target
default_target: release
.PHONY: release
release: export CXXFLAGS := $(CXXFLAGS) $(COMPILE_FLAGS)
release: dirs
@$(MAKE) all
.PHONY: dirs
dirs:
@echo "Creating directories"
@mkdir -p $(dir $(OBJECTS))
@mkdir -p $(BIN_PATH)
.PHONY: clean
clean:
@echo "Deleting $(BIN_NAME) symlink"
@$(RM) $(BIN_NAME)
@echo "Deleting directories"
@$(RM) -r $(BUILD_PATH)
@$(RM) -r $(BIN_PATH)
# checks the executable and symlinks to the output
.PHONY: all
all: $(BIN_PATH)/$(BIN_NAME)
@echo "Making symlink: $(BIN_NAME) -> $<"
@$(RM) $(BIN_NAME)
@ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
# Creation of the executable
$(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
@echo "Linking: $@"
$(CXX) $(OBJECTS) -o $@ ${LIBS}
# Add dependency files, if they exist
-include $(DEPS)
# Source file rules
# After the first compilation they will be joined with the rules from the
# dependency files to provide header dependencies
$(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT)
@echo "Compiling: $< -> $@"
$(CXX) $(CXXFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@

114
include/ArgParseCpp.hpp Normal file
View file

@ -0,0 +1,114 @@
#ifndef DEF_ArgParseCpp
#define DEF_ArgParseCpp
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <cstdlib>
#include <utils.hpp>
std::vector<std::string> SplitString(const std::string & str, char sep);
std::string RemovePrefixDashes(const std::string & str);
namespace ArgParseCpp
{
/// Used to describe the parser parameter type.
enum ParameterType
{
StandaloneParam, // Represents all parameters such as "--param"
SingleValueParam, // Represents all parameters such as "--param value"
EqualParam // Represents all parameters such as "--param=value"
// ListParam // Represents all parameters such as "--param=value1,value2,..,valueN"
};
/// Used to describe the parser parameter value type.
enum ParameterValueType
{
Bool,
Int,
Double,
String
};
/// Converts a ParameterValueType into a string for display.
std::string ParameterValueType2Str(const ParameterValueType & paramValueType);
/// This class defines an argument parser parameter, with its short and long name, its type, its value type, and its docstring.
class ParserParam
{
public:
/// Builds a new parser param object with the specified parameters.
ParserParam(const std::string & shortName_, const std::string & longName_, ParameterType type_, ParameterValueType valueType_ = ArgParseCpp::Bool, const std::string & docString_ = "");
/// Generates automatically a doc string based on the parameter type and value type, and repaces the current doc string with the newly generated one.
const std::string & GenerateDocString();
/// If no doc string has been specified, it is automatically generated.
void AutoGenerateDocString();
/// Returns either the full name without prefix dashes, or the short name without prefix dashes.
std::string SimpleName() const;
/// Returns the help entry string : "-p --param value Parameter "param" with value "value_param" of type integer number"
std::string GetHelpEntryStr() const;
public:
std::string shortName;
std::string longName;
ParameterType type;
ParameterValueType valueType;
std::string docString;
};
/// This class implements a simple argument parser in C++. It can be used to parse a variety of input arguments to programs.
class Parser
{
public:
/// Creates an empty parser.
Parser();
/// Adds a parameter to be parsed.
void AddParameter(ParserParam param);
/// Parses the arguments passed as parameters.
void Parse(int argc, char **argv);
/// Parses the arguments passed as a single string.
void Parse(const std::string & argString);
/// Parses the arguments passed as parameters.
void Parse(std::vector<std::string> args);
/// Prints the custom help message if it has been defined, or an automatically constructed one.
void PrintHelp() const;
/// Prints the parsed arguments and their associated values.
void PrintParsedArguments() const;
/// Returns the value of the specified argument. A conversion is done between the input text and the output value type.
template<typename T> T GetArgumentValue(const std::string & argName)
{
}
protected:
/// Cleans the argument list passed as parameter.
void CleanArgs(std::vector<std::string> & args) const;
protected:
std::vector<ParserParam> parserParams;
std::map<std::string, bool> boolParameters;
std::map<std::string, int> intParameters;
std::map<std::string, double> doubleParameters;
std::map<std::string, std::string> stringParameters;
std::string customHelpMessage;
};
} // namespace ArgParseCpp
#endif

10
include/utils.hpp Normal file
View file

@ -0,0 +1,10 @@
#ifndef DEF_Utils
#define DEF_Utils
#include <string>
#include <vector>
#define PRINT_VAR(v); std::cout << #v << "\t = "; std::cout << (v) << "\n";
#define PRINT_VEC(v); std::cout << #v << "\t =\n"; for(unsigned int i_PRINT_VEC = 0 ; i_PRINT_VEC < (v).size() ; i_PRINT_VEC++) { std::cout << (v)[i_PRINT_VEC] << "\n"; }
#endif

226
src/ArgParseCpp.cpp Normal file
View file

@ -0,0 +1,226 @@
#include <ArgParseCpp.hpp>
std::vector<std::string> SplitString(const std::string & str, char sep)
{
std::vector<std::string> elems;
std::string elem;
for(unsigned int i = 0 ; i < str.size() ; i++)
{
if(str[i] != sep)
elem += str[i];
else
{
elems.push_back(elem);
elem = "";
}
}
if(elem.size())
elems.push_back(elem);
return elems;
}
std::string RemovePrefixDashes(const std::string & str)
{
std::string res;
bool inPrefix = true;
for(unsigned int i = 0 ; i < str.size() ; i++)
{
if(str[i] == '-')
continue;
else
inPrefix = false;
if(!inPrefix)
res += str[i];
}
return res;
}
// ------------------- ArgParseCpp -------------------
namespace ArgParseCpp
{
std::string ParameterValueType2Str(const ParameterValueType & paramValueType)
{
if(paramValueType == ArgParseCpp::Bool)
return "bool";
else if(paramValueType == ArgParseCpp::Int)
return "integer number";
else if(paramValueType == ArgParseCpp::Double)
return "floating-point number";
else if(paramValueType == ArgParseCpp::String)
return "string";
return "";
}
// ------------------- ArgParseCpp::ParserParam -------------------
ParserParam::ParserParam(const std::string & shortName_, const std::string & longName_, ParameterType type_, ParameterValueType valueType_, const std::string & docString_)
: shortName(shortName_),
longName(longName_),
type(type_),
valueType(valueType_),
docString(docString_)
{}
const std::string & ParserParam::GenerateDocString()
{
if(type == ArgParseCpp::StandaloneParam)
docString = "Activates the option \"" + SimpleName() + "\" when present.";
else if(type == ArgParseCpp::SingleValueParam || type == ArgParseCpp::EqualParam)
docString = "Sets the parameter \"" + SimpleName() + "\" at the value \"value_" + SimpleName() + "\" of type " + ParameterValueType2Str(valueType) + ".";
return docString;
}
void ParserParam::AutoGenerateDocString()
{
if(!docString.size())
GenerateDocString();
}
std::string ParserParam::SimpleName() const { return RemovePrefixDashes((longName.size()) ? longName : shortName); }
std::string ParserParam::GetHelpEntryStr() const
{
std::string res;
if(type == ArgParseCpp::StandaloneParam)
res = " " + shortName + "\t" + longName + "\t" + docString + "\n";
else if(type == ArgParseCpp::SingleValueParam)
res = " " + shortName + "\t" + longName + " value_" + SimpleName() + "\t" + docString + "\n";
else if(type == ArgParseCpp::EqualParam)
res = " " + shortName + "\t" + longName + "=value_" + SimpleName() + "\t" + docString + "\n";
return res;
}
// ------------------- ArgParseCpp::Parser -------------------
Parser::Parser() {}
void Parser::AddParameter(ParserParam param)
{
param.AutoGenerateDocString();
parserParams.push_back(param);
}
void Parser::Parse(int argc, char **argv)
{
std::vector<std::string> args;
for(int i = 0 ; i < argc ; i++)
args.push_back(argv[i]);
Parse(args);
}
void Parser::Parse(const std::string & argString) { Parse(SplitString(argString, ' ')); }
void Parser::Parse(std::vector<std::string> args)
{
CleanArgs(args);
if(args.size() <= 1)
{
std::cerr << "Error : no arguments passed to the program.\n";
PrintHelp();
return;// exit(1);
}
else
{
// DEBUG
PRINT_VAR(args.size());
PRINT_VEC(args);
// Prepare all the boolean arguments
for(ParserParam const& param : parserParams) // loop over all parameters
if(param.type == ArgParseCpp::StandaloneParam) // if the parameter is a boolean type
boolParameters[param.SimpleName()] = false;
for(std::string const& arg : args)
{
if(arg == "-h" || arg == "--help")
{
PrintHelp();
return;// exit(1);
}
for(unsigned int iParam = 0 ; iParam < parserParams.size() ; iParam++) // loop over all parameters
{
ParserParam const& param = parserParams[iParam];
PRINT_VAR(arg);
PRINT_VAR(param.shortName);
PRINT_VAR(param.longName);
std::cout << "----\n";
if(arg == param.shortName || arg == param.longName) // if the argument corresponds to this parameter
{
if(param.type == ArgParseCpp::StandaloneParam)
boolParameters[param.SimpleName()] = true;
else if(param.type == ArgParseCpp::SingleValueParam)
{
if(iParam+1 >= parserParams.size())
{
std::cerr << "Error : parameter " << arg << " must be followed by a single value.";
return;
}
if(param.valueType == ArgParseCpp::Int)
intParameters[param.SimpleName()] = atoi(parserParams[++iParam]);// read next parameter as the value of this argument, and skip the next iteration, as the next argument has already been processed
if(param.valueType == ArgParseCpp::Double)
doubleParameters[param.SimpleName()] = strtod(parserParams[++iParam]);// read next parameter as the value of this argument, and skip the next iteration, as the next argument has already been processed
if(param.valueType == ArgParseCpp::String)
stringParameters[param.SimpleName()] = parserParams[++iParam];// read next parameter as the value of this argument, and skip the next iteration, as the next argument has already been processed
}
}
}
}
}
}
void Parser::PrintHelp() const
{
if(customHelpMessage.size())
std::cout << customHelpMessage << "\n";
else
{
// Generate a help message
std::cout << "Usage : \n";
std::cout << " -h, -help\t\tPrints this help message and exits.\n";
for(ParserParam param : parserParams)
std::cout << param.GetHelpEntryStr();
}
}
void Parser::PrintParsedArguments() const
{
std::cout << "\nBoolean parameters\n------------------\n";
for(auto const& arg : boolParameters)
std::cout << arg.first << "\t= " << arg.second << "\n";
std::cout << "\nInteger parameters\n------------------\n";
for(auto const& arg : intParameters)
std::cout << arg.first << "\t= " << arg.second << "\n";
std::cout << "\nFloating-point parameters\n-------------------------\n";
for(auto const& arg : doubleParameters)
std::cout << arg.first << "\t= " << arg.second << "\n";
std::cout << "\nString parameters\n------------------\n";
for(auto const& arg : stringParameters)
std::cout << arg.first << "\t= " << arg.second << "\n";
}
void Parser::CleanArgs(std::vector<std::string> & args) const
{
for(std::vector<std::string>::iterator it = args.end()-1 ; it != args.begin() ; --it)
{
if((*it).size() == 0) // remove empty arguments
args.erase(it);
else // clean individual arguments
{
char c = (*it)[(*it).size()-1];
while(c == ' ' || c == '\t' || c == '\n')
{
(*it).erase(*((*it).end()));
c = (*it)[(*it).size()-1];
}
}
}
}
} // namespace ArgParseCpp

71
src/test_ArgParseCpp.cpp Normal file
View file

@ -0,0 +1,71 @@
#include <iostream>
#include <ArgParseCpp.hpp>
#include <utils.hpp>
using ArgListT = std::vector<std::string>;
using ArgParseCpp::Parser;
template<typename T> std::vector<T> & operator<<(std::vector<T> &vec, const T & val)
{
vec.push_back(val);
return vec;
}
int main(int argc, char *argv[])
{
// Test the parser on the arguments provided in main()
if(argc > 1)
{
Parser parser;
parser.Parse(argc, argv);
}
// Test the parser in various predefined scenarios
else
{
if(0)
{// automatic help message
Parser parser;
parser.AddParameter(ArgParseCpp::ParserParam("-v", "--verbose", ArgParseCpp::StandaloneParam, ArgParseCpp::Bool, "Makes the program more verbose."));
parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::StandaloneParam, ArgParseCpp::Bool, "Does all the things."));
parser.AddParameter(ArgParseCpp::ParserParam("-n", "--nope", ArgParseCpp::StandaloneParam));
parser.AddParameter(ArgParseCpp::ParserParam("-d", "--decimate", ArgParseCpp::SingleValueParam, ArgParseCpp::Int, "Decimates the signal by a factor d."));
parser.AddParameter(ArgParseCpp::ParserParam("-b", "--babar", ArgParseCpp::SingleValueParam, ArgParseCpp::Int));
parser.AddParameter(ArgParseCpp::ParserParam("-c", "", ArgParseCpp::SingleValueParam, ArgParseCpp::Double));
parser.AddParameter(ArgParseCpp::ParserParam("-o", "", ArgParseCpp::SingleValueParam, ArgParseCpp::String));
parser.AddParameter(ArgParseCpp::ParserParam("-e", "", ArgParseCpp::EqualParam, ArgParseCpp::Int));
parser.PrintHelp();
}
if(0)
{// sanitize the input arguments and display help whenever -h or --help is seen
std::string argString = "programName -h blabla hehe hoho lalaland";
Parser parser;
parser.Parse(argString);
}
if(0)
{// parse some boolean arguments
std::string argString = "programName -v --all";
Parser parser;
parser.AddParameter(ArgParseCpp::ParserParam("-v", "--verbose", ArgParseCpp::StandaloneParam, ArgParseCpp::Bool, "Makes the program more verbose."));
parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::StandaloneParam));
parser.AddParameter(ArgParseCpp::ParserParam("-b", "--brief", ArgParseCpp::StandaloneParam));
parser.AddParameter(ArgParseCpp::ParserParam("-z", "", ArgParseCpp::StandaloneParam));
parser.PrintHelp();
parser.Parse(argString);
parser.PrintParsedArguments();
}
// if(0)
{// parse single value arguments
std::string argString = "programName -a 5 -b 12.654 -s blabla_mon_negro hehe";
Parser parser;
parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::SingleValueParam, ArgParseCpp::Int));
parser.AddParameter(ArgParseCpp::ParserParam("-b", "--brief", ArgParseCpp::SingleValueParam, ArgParseCpp::Double));
parser.AddParameter(ArgParseCpp::ParserParam("-s", "", ArgParseCpp::SingleValueParam, ArgParseCpp::String));
parser.PrintHelp();
parser.Parse(argString);
parser.PrintParsedArguments();
}
}
return 0;
}

1
src/utils.cpp Normal file
View file

@ -0,0 +1 @@
#include <utils.hpp>