Skip to content

Another option parser writen in C++, I hated the other examples I've seen so I made this it's small but powerful

License

Notifications You must be signed in to change notification settings

grizzlysmit/gzzopt

Repository files navigation

This is the gzzopt option parsing library

Author: Francis Grizzly Smit

Contents

Introduction

The library is contained in the files gzzopt.hpp and gzzopt.cpp, (see below for how to create libgzzopt.a and libgzzopt.so) there are also 8 example programs calc, calc2, count, bad, vect, empty, example0, and example.

Example and Example0

example is a complex example of just what complex a set of command lines you can achieve; example0 shows how you can go wrong the gotcha is that example0 tries to have the command line:

...
./example0 install <pacakage>...
./example0 install info <pacakage>...

the problem is install shadows info and info is treated as a package argument where as example uses:

...
./example install <pacakage>...
./example info <pacakage>...

This works fine, try each.

Calc and Calc2

calc and calc2 are the same as each other, except for how there arguments are specified, to wit:

21:26:00 grizzlysmit@rakbat:~/Projects/C++/gzzoption$ ./calc --help
./calc --help|-h
./calc [--help|-h] [--verbose|-v] [--quiet|-q] add|sub|mult|div|pow <x> <y>


where:
	--help|-h            : show this help
	                       
	[--help|-h]          : show this help
	[--verbose|-v]       : proceed verbosely
	[--quiet|-q]         : proceed quietly
	add|sub|mult|div|pow : apply op to x and y
	<x>                  : a double value
	<y>                  : a double value
	                       

21:29:41 grizzlysmit@rakbat:~/Projects/C++/gzzoption$ ./calc2 --help
./calc2 [--help|-h]
./calc2 [--verbose|-v] [--quiet|-q] add <x> <y>
./calc2 [--verbose|-v] [--quiet|-q] sub <x> <y>
./calc2 [--verbose|-v] [--quiet|-q] mult <x> <y>
./calc2 [--verbose|-v] [--quiet|-q] div <x> <y>
./calc2 [--verbose|-v] [--quiet|-q] pow <x> <y>


where:
	[--help|-h]    : show this help
	                 
	[--verbose|-v] : proceed verbosely
	[--quiet|-q]   : proceed quietly
	add            : apply op to x and y
	<x>            : a double value
	<y>            : a double value
	                 
	sub            : apply op to x and y
	                 
	mult           : apply op to x and y
	                 
	div            : apply op to x and y
	                 
	pow            : apply op to x and y
	                 

As you can see calc uses one argument spec add|sub|mult|div|pow where as calc2 does the same thing using one command line per add, through to pow, as a result to avoid duplicate lines in the usage message; I used p.set_compact(true); in calc2.cpp. All of the programs correspond to a .cpp file see the Makefile for details.

Count

count shows a simple example of an incrementing integer option see count.cpp, this is done with an incrementing function, this is the best solution for this problem, I could do it by adding still more helper classes and special constructors and it gets progressively more ugly, so I bailed for something which will have fewer problems, and I think is an eloquent solution. see code below, or read count.cpp:

   
#include "gzzopt.hpp"
#include <string>


int main(int argc, char* argv[]){
    using namespace gzzopts;
    int cnt = 0;
    bool help = false;
    std::string s;

    Opts opt{OptionSpec(help, "show this help", "help", 'h'),
              OptionSpec([&cnt]() -> bool { return ++cnt; },
                      "increment the number of times to repeat",
                                  "count", 'c' ).set_multi(true), 
              strvalue(s, "a string", "s").set_manditory(true),
            };

    OptionParser p(argc, argv, opt);
    // parse away //
    if(!p.parse()){
        p.fullusage();
        return 1;
    }
    /////////////////////////////////////////////////////////////
    //                                                         //
    //             Use the variables that where set            //
    //                                                         //
    /////////////////////////////////////////////////////////////
    if(help){
        p.fullusage();
        return 0;
    }

    for(int i = 0; i < cnt; i++) std::cout << '`' << s << "' ";
    std::cout << std::endl;
    return 0;
}
   
NB

Have to use strvalue to allow arbitary strings, should just been a variant of positional but my compiler goes to the template version instead. :P

A brief explanation of the format of a program using gxxopts

The format of a program is as follows in main(int argc, char *argv[]) or similar, define one or more Opt objects, the elements of the Opt object are OptionSpec objects, these can be entered directly or via the convenience functions positionnal and literal which produce OptionSpec objects with extra options turned on a positional is just a positional argument, and a literal is a literal string to expect, in addition OptionSpec has a initializer_list constructor OptionSpec(std::initializer_list<Opts*> opts) which take a list of pointers to Opt objects (had to be pointers as Opts is an incomplete type at the time) so you can produce tree like structures. The constructor for Opt itself is a initializer_list Opts(std::initializer_list<OptionSpec> lst) so you can specify arbitrarily many elements.

The OptionSpec elements are of the form variable/setter function, description, a long opt name and an optional short opt char, and some optional boolean parameters to set things like positional multi etc use the convenience functions instead there are also setter functions that can be chained, these all take a single bool parameter and set the field their name suggests.

Then when you have combined all this into one Opt object see calc.cpp and call2.cpp; construct a OptionParser object as follows OptionParser p(argc, argv, opt); then you can set a few parameters on the OptionParser object or not; then call p.parse(), this will return true on a error free run and false of error; then if parse returned true just use the variables that where set, the variables can be std::list<T> and std::vector<T> types as well so you can have slurpy arrays ala perl6 too; bellow is some sample code to illustrate:

   
#include "gzzopt.hpp"
#include <string>
#include <cmath>

enum class Opp  { none, add, sub, mult, div, pow };

...

int main(int argc, char *argv[]){
    using namespace gzzopts;
    double x, y;
    Opp opp = Opp::none;
    bool quiet = true, help = false;
    function_str verb = [&quiet](const std::string& val) -> bool
                                                { quiet = false; return !quiet; };
    Opts opt1{OptionSpec(help, "show this help", "help", 'h'),
                      OptionSpec(verb, "proceed verbosely", "verbose", 'v' ), 
                      OptionSpec([&quiet](const std::string& val) -> bool
                              { return quiet = true; },
                                           "proceed quietly", "quiet", 'q' ), 
                      literal([&opp](const std::string& n) -> bool
                              { return set_opp(opp, n); },
                                   "apply op to x and y",
                                   "add|sub|mult|div|pow").set_no_more_opts(true),
                      positional(x, "a double value", "x").set_manditory(true),
                      positional(y, "a double value", "y").set_manditory(true),
                    };
   Opts opt{
                      OptionSpec{&opt1, },
                      OptionSpec(help, "show this help",
                              "help", 'h').set_manditory(true).set_cut(true),
           };
   
    OptionParser p(argc, argv, opt);
    // parse away //
    if(!p.parse()){
        p.fullusage();
        return 1;
    }
    /////////////////////////////////////////////////////////////
    //             Use the variables that where set            //
    /////////////////////////////////////////////////////////////
    if(help){
        p.fullusage();
        return 0;
    }
    switch(opp){
        case(Opp::none):
            std::cout << "nothing to do" << std::endl;
            break;
        case(Opp::add):
            if(!quiet) std::cout << x << " + " << y << " == ";
            std::cout << (x + y) << std::endl;
            break;
        case(Opp::sub):
            if(!quiet) std::cout << x << " - " << y << " == ";
            std::cout << (x - y) << std::endl;
            break;
        case(Opp::mult):
            if(!quiet) std::cout << x << " * " << y << " == ";
            std::cout << (x * y) << std::endl;
            break;
        case(Opp::div):
            if(!quiet) std::cout << x << " / " << y << " == ";
            std::cout << (x / y) << std::endl;
            break;
        case(Opp::pow):
            if(!quiet) std::cout << x << "^" << y << " == ";
            std::cout << std::pow(x, y) << std::endl;
            break;
        default:
            std::cerr << "how in heavens name did I get here" << std::endl;
            return 1;
    }
    return 0;
}
   

Making the Library a Library

To make the library a library on Linux do this

   g++ -shared -fPIC -o libgzzopt.so gzzopt.o 

I shall look at putting this in the make file when I learn to do plaform specific stuff in a make file.

To make a statically linked library of it under Linux etc al do this

   
   ar -cvq libgzzopt.a gzzopt.o
   

NB

The make file Makefile now makes the libraries, for you. and installs them.

Empty

The empty.cpp program shows how to have a program that allows an empty argument set (most of the others here delibrately mmake that an error).

Bad

The program bad bad.cpp is an example of a bad option to wit a ? for the short option this is picked up by the good() method.

Vect

The vect.cpp program is just the example.cpp reworked to use StrVect i.e. string vector vect in place of the string list l.

See Also:

calc.cpp and cacl2.cpp for a full example; and example.cpp for an example of how convoluted you can get. And example0.cpp for an example of a flawed spec reread above for why it's flawed. Also count.cpp, and empty.cpp and fianlly bad.cpp for an exaple of catching a bad option spec, note this only catches bad char specs not other errors.

NB

This code was compiled with the -std=c++1y option to the g++ compiler as I use some of the more recent features of the C++ Programming language.

About

Another option parser writen in C++, I hated the other examples I've seen so I made this it's small but powerful

Resources

License

Stars

Watchers

Forks

Packages

No packages published