Files
c_cpp_boilerplate/src/example_cpp/argparser/argparser.hpp
2024-10-24 21:04:00 -03:00

164 lines
5.8 KiB
C++

#ifndef INCLUDE_ARGPARSER_ARGPARSER_HPP_
#define INCLUDE_ARGPARSER_ARGPARSER_HPP_
#include <argparse.h>
#include <cstring>
#include <plog/Log.h>
#include <string>
#include <typeinfo>
#include <vector>
/**
* @class ArgumentParser
* @brief A class to handle command line arguments.
*
* This class provides methods to add, get and manage known arguments from the command line. It users
* (Argparse)[https://github.com/cofyc/argparse] under the hood
*/
class ArgumentParser {
public:
/**
* Constructor with application name, description and requirement of arguments.
*
* @param application_name Name of the application.
* @param description Description of the application.
* @param require_arguments Whether the application requires arguments or not.
*/
ArgumentParser(const std::string &application_name, const std::string &description, bool require_arguments = false,
std::vector<std::vector<std::string>> allowed_arguments = {})
: application_name_(application_name), description_(description), require_arguments_(require_arguments),
allowed_arguments_(allowed_arguments) {};
/**
* Destructor for ArgumentParser.
*/
~ArgumentParser();
/**
* Adds a flag to the list of flags.
*
* @tparam T Type of the flag value (int, float, bool or std::string).
* @param short_name Short name of the flag.
* @param long_name Long name of the flag.
* @param value Pointer to the flag value.
* @param description Description of the flag.
* \todo Support callbacks for flags as in
* https://github.com/cofyc/argparse/blob/682d4520b4bc2b646cdfcf078b2fed00b3d2da30/tests/basic.c#L34
* \todo Implement logic to allow only to choose from a specific set of choices for a variable
*/
template <typename T> void add_flag(char short_name, const std::string &long_name, T *value, const std::string &description) {
const std::type_info &flag_type = typeid(T);
// Clone strings so we dont lose reference to it
char *long_name_local = new char[long_name.size() + 1];
std::strncpy(long_name_local, long_name.c_str(), long_name.size());
long_name_local[long_name.size()] = '\0';
PLOG_VERBOSE << "Adding flag " << long_name_local << " to the list of flags" << std::endl;
char *description_local = new char[description.size() + 1];
std::strncpy(description_local, description.c_str(), description.size());
description_local[description.size()] = '\0';
struct argparse_option flag_opt = {ARGPARSE_OPT_END, short_name, long_name_local, value, description_local, NULL, 0, 0};
if (flag_type == typeid(int)) {
flag_opt.type = ARGPARSE_OPT_INTEGER;
PLOG_VERBOSE << "Flag " << long_name_local << " is of type int" << std::endl;
}
if (flag_type == typeid(uint)) {
flag_opt.type = ARGPARSE_OPT_INTEGER;
PLOG_VERBOSE << "Flag " << long_name_local << " is of type unsigned int" << std::endl;
}
else if (flag_type == typeid(float)) {
flag_opt.type = ARGPARSE_OPT_FLOAT;
PLOG_VERBOSE << "Flag " << long_name_local << " is of type float" << std::endl;
}
else if (flag_type == typeid(bool)) {
flag_opt.type = ARGPARSE_OPT_BOOLEAN;
flag_opt.value = NULL;
flag_opt.callback = opt_boolean_callback;
flag_opt.data = reinterpret_cast<intptr_t>(value);
PLOG_VERBOSE << "Flag " << long_name_local << " is of type bool" << std::endl;
}
else if (flag_type == typeid(std::string)) {
flag_opt.type = ARGPARSE_OPT_STRING;
flag_opt.value = NULL;
flag_opt.callback = opt_string_callback;
flag_opt.data = reinterpret_cast<intptr_t>(value);
PLOG_VERBOSE << "Flag " << long_name_local << " is of type str" << std::endl;
}
options_.push_back(flag_opt);
}
/**
* Parses the command line arguments.
*
* @param argc Number of command line arguments, including binary name.
* @param argv Array of command line arguments, including binary name at argv[0].
* @return True if parsing is successful, false otherwise.
*/
bool parse(int argc, const char **argv);
/**
* @brief Displays the usage message from argparse.
*
* This inline function calls `argparse_usage` with no arguments, printing the
* usage message to stdout.
*
* @note The output is not redirected or buffered; it will be printed directly to the console.
*/
inline void show_usage() { argparse_usage(&argparse_); };
/**
* @brief Returns a copy of the vector of command-line arguments.
*
* This method returns a copy of the internal `arguments_` vector, which is used to store
* the command-line arguments passed to the program. The returned vector is not modified.
*
* @return A copy of the `arguments_` vector.
*/
inline std::vector<std::string> get_arguments() const { return arguments_; };
private:
/**
* Vector of argparse options. Stores all flags/arguments added through add_flag
*/
std::vector<struct argparse_option> options_ = {OPT_HELP()};
/**
* List of command line arguments. Thing that are not flags, i.e. do not begin with -, will be stored here
*/
std::vector<std::string> arguments_;
std::string application_name_;
std::string description_;
bool require_arguments_;
struct argparse argparse_;
std::vector<std::vector<std::string>> allowed_arguments_;
/**
* Callback function for string options.
*
* @param parser Argument parser object.
* @param flag Argument option structure.
* @return 0 on success, non-zero otherwise.
*/
static int opt_string_callback(struct argparse *, const struct argparse_option *);
/**
* Callback function for boolean options.
*
* @param parser Argument parser object.
* @param flag Argument option structure.
* @return 0 on success, non-zero otherwise.
*/
static int opt_boolean_callback(struct argparse *, const struct argparse_option *);
};
#endif // INCLUDE_ARGPARSER_ARGPARSER_HPP_