summaryrefslogblamecommitdiffstats
path: root/include/diargs.hpp
blob: ee5cf63b5d635e37255d1c8740bb9550580a22c9 (plain) (tree)
















































































































































































                                                                                
/**
 * diargs.hpp
 * Copyright (c) 2022 Jon Santmyer <jon@jonsantmyer.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * diargs - An alternative to GNU getopt for modern C++
 * Provides tools to parse command line arguments for C++17 programs
 * 
 * Usage: Instantiate an ArgumentParser class, passing in a fallback
 *        error function, an ArgumentList struct, and an argc,argv pair.
 *
 * Example:
 *
 * void printusage(int err) {
 *     std::cout << "-h --help : Print this message" << std::endl;
 *     std::exit(err);
 * }
 *
 * int main(int argc, char **argv) {
 *    bool helpflag;
 *
 *    ArgsPair args{argc, argv};
 *    ArgumentList argslist(
 *          ToggleArgument("help", 'h', helpflag, true)
 *    );
 *    ArgumentParser parser(printusage, argslist, args);
 *    return 0;
 * }
 */
#ifndef DIARGS_HPP
#define DIARGS_HPP 1

#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <optional>
#include <algorithm>

namespace diargs {

    /*Passed to ArgumentParser*/
    struct ArgsPair {
        int argc {};
        char **argv {};
    };

    /*Interface for argument types. Makes the whole system tick*/
    struct IArgument
    {
        std::string longform {}; /*Name parsed when passed through with '--'*/
        char shortform {}; /*Character parsed when passed through with '-'*/
        
        IArgument() = default;
        IArgument(const std::string &Longform, char Shortform) : 
            longform(Longform), shortform(Shortform) {}
        virtual ~IArgument() = default;

        virtual std::optional<std::vector<std::string_view>::iterator> parse(
                std::vector<std::string_view> &argv,
                std::vector<std::string_view>::iterator arg) = 0;
    };

    template<typename T>
    struct MultiArgument : public IArgument
    {
        T *flag; /*Value set when encountered. Taken from next argument
                   i.e. --arg [VALUE]*/
        
        MultiArgument(T &Flag) : flag(&Flag) {}
        MultiArgument(const std::string &Longform, T &Flag) :
            IArgument(Longform, 0), flag(&Flag) {}
        MultiArgument(const std::string &Longform, char Shortform, T &Flag) :
            IArgument(Longform, Shortform), flag(&Flag) {}
        ~MultiArgument() {}

        std::optional<std::vector<std::string_view>::iterator> parse(
                std::vector<std::string_view> &argv,
                std::vector<std::string_view>::iterator arg) {
            if(++arg == argv.end()) return std::nullopt; /*Not having the
            value is an error case*/
            std::stringstream ss((*arg).data());
            ss >> std::noskipws >> *flag;
            return arg;
        }
    };

    template<typename T>
    struct ToggleArgument : public IArgument
    {
        T *flag;    /*Value set to [set] when encountered*/
        T set;      /*Value that [flag] is set to when parsed*/

        ToggleArgument(T &Flag, const T Set) : 
            flag(&Flag), set(Set) {}
        ToggleArgument(const std::string &Longform, T &Flag, const T Set) :
            IArgument(Longform, 0), flag(&Flag), set(&Set) {}
        ToggleArgument(const std::string &Longform, char Shortform, 
                       T &Flag, const T Set) :
            IArgument(Longform, Shortform), flag(&Flag), set(&Set) {}
        ~ToggleArgument() {}

        std::optional<std::vector<std::string_view>::iterator> parse(
                std::vector<std::string_view> &argv,
                std::vector<std::string_view>::iterator arg) {
            *flag = set;
            return arg;
        }
    };

    /*Type for values not denoted with '-' or '--'*/
    template<typename T>
    struct OrderedArgument : public IArgument
    {
        T *flag; /*Value set when passed arg is not any other type*/
        std::optional<T> dflt;
        OrderedArgument(T &Flag) :
            flag(&Flag), dflt(std::nullopt) {}
        OrderedArgument(T &Flag, T Dflt) :
            flag(&Flag), dflt(Dflt) {}
        ~OrderedArgument() {}
        std::optional<std::vector<std::string_view>::iterator> parse(
                std::vector<std::string_view> &argv,
                std::vector<std::string_view>::iterator arg) {
            std::stringstream ss((*arg).data());
            ss >> std::noskipws >> *flag;
            return arg;
        }       
    };

    /*Container for argument checking*/
    struct ArgumentList
    {
        std::vector<std::unique_ptr<IArgument>> arguments;

        void addArgument() {}
        template<class T, class... ArgumentPack>
        void addArgument(T arg, ArgumentPack... args) {
            arguments.push_back(std::make_unique<T>(arg));
            addArgument(args...);
        }

        template<class... ArgumentPack>
        ArgumentList(ArgumentPack... arguments) {
            addArgument(arguments...);
        }
    };

    /*Instantiation results in parsed arguments*/
    class ArgumentParser
    {
    public:
        ArgumentParser(
                void (*failfunc)(int),
                ArgumentList &arguments,
                ArgsPair argcv);
    };
}

#endif