Commit 0d49a5bc authored by kulvait's avatar kulvait

Argument parsing improvements

parent eb7576c7
......@@ -11,20 +11,27 @@ public:
Arguments(int argc, char** argv, std::string appName = "");
std::shared_ptr<CLI::App> getCliApp();
// Call this function for parsing
/**
* @brief
*
* @param helpOnError Whether to print a help message on parsing error.
*
* @return
*/
/**
* @brief
*
* @param helpOnError Whether to print a help message on parsing error.
*
* @return
*/
int parse(bool helpOnError = true);
virtual void defineArguments() = 0;
virtual int preParse() = 0;
virtual int postParse() = 0;
void registerOption(std::string optName, CLI::Option* opt);
void registerOptionGroup(std::string optGroupName, CLI::Option_group* og);
CLI::Option_group* getRegisteredOptionGroup(std::string optGroupName);
CLI::Option* getRegisteredOption(std::string optName);
protected:
std::shared_ptr<CLI::App> cliApp;
std::map<std::string, CLI::Option_group*> cliOptionGroups;
std::map<std::string, CLI::Option*> cliOptions;
int argc;
char** argv;
};
......
#pragma once
#include "CLI/CLI.hpp" //Command line parser
#include "PROG/Arguments.hpp"
namespace CTL::util {
//Virtual inheritance to have only one instance of Arguments in the pedigree
class ArgumentsForce : public virtual Arguments
{
public:
ArgumentsForce(int argc, char** argv, std::string prgName)
: Arguments(argc, argv, prgName){};
bool force = false;
protected:
void addForceArgs();
};
} // namespace CTL::util
#pragma once
#include "CLI/CLI.hpp" //Command line parser
#include "PROG/Arguments.hpp"
namespace CTL::util {
//Using virtual inheritance to include only one copy of Arguments class into the pegigree
class ArgumentsFramespec : public virtual Arguments
{
public:
ArgumentsFramespec(int argc, char** argv, std::string prgName)
: Arguments(argc, argv, prgName){};
std::string frameSpecification = "";
uint32_t eachKth = 1;
bool reverseOrder = false;
std::vector<int> frames;
void fillFramesVector(uint32_t numberOfFrames);
protected:
void addFramespecArgs();
private:
static std::vector<int>
processFramesSpecification(std::string frameSpecification, int dimz);
};
} // namespace CTL::util
#pragma once
#include "CLI/CLI.hpp" //Command line parser
#include "PROG/Arguments.hpp"
namespace CTL::util {
class ArgumentsThreading : public virtual Arguments
{
public:
ArgumentsThreading(int argc, char** argv, std::string prgName)
: Arguments(argc, argv, prgName){};
uint32_t threads = 0;
protected:
void addThreadingArgs();
};
} // namespace CTL::util
......@@ -48,4 +48,50 @@ int Arguments::parse(bool helpOnError)
}
std::shared_ptr<CLI::App> Arguments::getCliApp() { return cliApp; }
void Arguments::registerOption(std::string optName, CLI::Option* opt)
{
if(cliOptions.find(optName) != cliOptions.end())
{
std::string err
= io::xprintf("Option with a name %s is already registered!", optName.c_str());
LOGE << err;
throw std::runtime_error(err);
}
cliOptions[optName] = opt;
}
void Arguments::registerOptionGroup(std::string optGroupName, CLI::Option_group* og)
{
if(cliOptionGroups.find(optGroupName) != cliOptionGroups.end())
{
std::string err
= io::xprintf("Option with a name %s is already registered!", optGroupName.c_str());
LOGE << err;
throw std::runtime_error(err);
}
cliOptionGroups[optGroupName] = og;
}
CLI::Option_group* Arguments::getRegisteredOptionGroup(std::string optGroupName)
{
if(cliOptionGroups.find(optGroupName) == cliOptionGroups.end())
{
return nullptr;
} else
{
return cliOptionGroups[optGroupName];
}
}
CLI::Option* Arguments::getRegisteredOption(std::string optName)
{
if(cliOptions.find(optName) == cliOptions.end())
{
return nullptr;
} else
{
return cliOptions[optName];
}
}
} // namespace CTL::util
#include "PROG/ArgumentsForce.hpp" //Command line parser
using namespace CTL;
using namespace CTL::util;
void ArgumentsForce::addForceArgs()
{
registerOption(
"force",
cliApp->add_flag("--force", force,
io::xprintf("Overwrite output files if they exist, defaults to %s.",
force ? "true" : "false")));
}
#include "PROG/ArgumentsFramespec.hpp" //Command line parser
using namespace CTL;
using namespace CTL::util;
void ArgumentsFramespec::addFramespecArgs()
{
registerOption("frames",
cliApp->add_option(
"-f,--frames", frameSpecification,
"Frames to process. Frames numbers are zero based. Input can be a range "
"i.e. 0-20 or individual coma separated frames i.e. 1,8,9. Order does "
"matter and ranges must be from lowest to highest both included. Accepts "
"end literal that means the last frame of the processed file."));
registerOption(
"reverse_order",
cliApp->add_flag("-r,--reverse-order", reverseOrder,
"Output in the reverse order of input or reverse specified frames."));
registerOption(
"each-kth",
cliApp
->add_option(
"-k,--each-kth", eachKth,
"Process only each k-th frame specified by k to output. The frames to output "
"are then 1st specified, 1+kN, N=1...\\infty if such frame exists. Parametter k "
"must be positive integer. This operation takes place after processing frame "
"specification by argument --frames and possible reverse.")
->check(CLI::Range(1, 65535)));
}
void ArgumentsFramespec::fillFramesVector(uint32_t dimz)
{
frames.clear();
frames = processFramesSpecification(frameSpecification, dimz);
if(reverseOrder)
{
std::reverse(frames.begin(), frames.end()); // It really does!
}
if(eachKth > 1)
{
std::vector<int> f;
for(std::size_t i = 0; i != frames.size(); i++)
{
if(i % eachKth == 0)
{
f.push_back(frames[i]);
}
}
frames = f;
}
}
std::vector<int>
ArgumentsFramespec::processFramesSpecification(std::string frameSpecification, int dimz)
{
// Remove spaces
frameSpecification.erase(
std::remove_if(frameSpecification.begin(), frameSpecification.end(), ::isspace),
frameSpecification.end());
frameSpecification = std::regex_replace(frameSpecification, std::regex("end"),
io::xprintf("%d", dimz - 1).c_str());
std::vector<int> frames;
if(frameSpecification.empty())
{
for(int i = 0; i != dimz; i++)
frames.push_back(i);
} else
{
std::list<std::string> string_list;
strtk::parse(frameSpecification, ",", string_list);
auto it = string_list.begin();
while(it != string_list.end())
{
size_t numRangeSigns = std::count(it->begin(), it->end(), '-');
if(numRangeSigns > 1)
{
std::string msg = io::xprintf("Wrong number of range specifiers in the string %s.",
(*it).c_str());
LOGE << msg;
throw std::runtime_error(msg);
} else if(numRangeSigns == 1)
{
std::vector<int> int_vector;
strtk::parse((*it), "-", int_vector);
if(0 <= int_vector[0] && int_vector[0] <= int_vector[1] && int_vector[1] < dimz)
{
for(int k = int_vector[0]; k != int_vector[1] + 1; k++)
{
frames.push_back(k);
}
} else
{
std::string msg
= io::xprintf("String %s is invalid range specifier.", (*it).c_str());
LOGE << msg;
throw std::runtime_error(msg);
}
} else
{
int index = std::stoi(it->c_str());
if(0 <= index && index < dimz)
{
frames.push_back(index);
} else
{
std::string msg = io::xprintf(
"String %s is invalid specifier for the value in the range [0,%d).",
(*it).c_str(), dimz);
LOGE << msg;
throw std::runtime_error(msg);
}
}
it++;
}
}
return frames;
}
#include "PROG/ArgumentsThreading.hpp" //Command line parser
using namespace CTL;
using namespace CTL::util;
void ArgumentsThreading::addThreadingArgs()
{
registerOption(
"threads",
cliApp
->add_option("-j,--threads", threads,
io::xprintf("Number of extra threads that the program can use, default is "
"%d. Zero extra threads means no threading.",
threads))
->check(CLI::Range(0, 65535)));
}
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