All Projects → mmahnic → Argumentum

mmahnic / Argumentum

Licence: mpl-2.0
C++ command line parsing library

Programming Languages

cpp
1120 projects
cpp17
186 projects

Projects that are alternatives of or similar to Argumentum

Argparse.jl
Package for parsing command-line arguments to Julia programs.
Stars: ✭ 131 (+42.39%)
Mutual labels:  command-line, argument-parser, options
Typin
Declarative framework for interactive CLI applications
Stars: ✭ 126 (+36.96%)
Mutual labels:  command-line, parser, library
Clipp
easy to use, powerful & expressive command line argument parsing for modern C++ / single header / usage & doc generation
Stars: ✭ 687 (+646.74%)
Mutual labels:  command-line, argument-parser, options
Structopt
Parse command line arguments by defining a struct
Stars: ✭ 323 (+251.09%)
Mutual labels:  command-line, library, argument-parser
Argparse
Argument Parser for Modern C++
Stars: ✭ 680 (+639.13%)
Mutual labels:  library, argument-parser
Clap
Create your command-line parser, with all of the bells and whistles, declaratively or procedurally.
Stars: ✭ 7,174 (+7697.83%)
Mutual labels:  command-line, argument-parser
Argh
Argh! A minimalist argument handler.
Stars: ✭ 752 (+717.39%)
Mutual labels:  command-line, argument-parser
Go Deb Version
A golang library for parsing deb package versions
Stars: ✭ 21 (-77.17%)
Mutual labels:  parser, library
Compiler
The Hoa\Compiler library.
Stars: ✭ 458 (+397.83%)
Mutual labels:  parser, library
Badx12
A Python Library for parsing ANSI ASC X12 files.
Stars: ✭ 25 (-72.83%)
Mutual labels:  parser, library
Anglesharp.css
👼 Library to enable support for cascading stylesheets in AngleSharp.
Stars: ✭ 27 (-70.65%)
Mutual labels:  parser, library
Papis
Powerful and highly extensible command-line based document and bibliography manager.
Stars: ✭ 636 (+591.3%)
Mutual labels:  command-line, library
Nve
Run any command on specific Node.js versions
Stars: ✭ 531 (+477.17%)
Mutual labels:  command-line, library
Fast Xml Parser
Validate XML, Parse XML to JS/JSON and vise versa, or parse XML to Nimn rapidly without C/C++ based libraries and no callback
Stars: ✭ 1,021 (+1009.78%)
Mutual labels:  command-line, parser
Kong
Kong is a command-line parser for Go
Stars: ✭ 481 (+422.83%)
Mutual labels:  command-line, parser
Text Minimap
Generate text minimap/preview using Braille Patterns
Stars: ✭ 21 (-77.17%)
Mutual labels:  command-line, library
Sharpmath
A small .NET math library.
Stars: ✭ 36 (-60.87%)
Mutual labels:  parser, library
Anglesharp.js
👼 Extends AngleSharp with a .NET-based JavaScript engine.
Stars: ✭ 68 (-26.09%)
Mutual labels:  parser, library
Internettools
XPath/XQuery 3.1 interpreter for Pascal with compatibility modes for XPath 2.0/XQuery 1.0/3.0, custom and JSONiq extensions, XML/HTML parsers and classes for HTTP/S requests
Stars: ✭ 82 (-10.87%)
Mutual labels:  parser, library
Fire Hpp
Fire for C++: Create fully functional CLIs using function signatures
Stars: ✭ 395 (+329.35%)
Mutual labels:  library, argument-parser

Build Status Language grade: C/C++

Argumentum / Argparse

Argumentum is a C++ library for writing command-line program interfaces. The parameters that a program supports are registered in an instance of arument_parser, the main library class. argument_parser processes the input arguments, checks that they are valid and converts them to C++ variables. It also generates help and usage messages when requested.

The parsed values are stored in normal C++ variables. Support for numeric types, std::string, std::vector and std::optional is built into the parser. Any type that can be converted from string with a constructor or an assignment operator can also be used. Other types of values are supported with parsing actions. The library requires a C++17 compiler.

The library is loosely based on the Python argparse module. It covers most functionality of Python argparse.

Building

The library can be built as a static library or used as header-only. See Building and consuming the library.

A basic example

In this example the program accepts integers and finds the largest one. If the option --sum is passed to the program, the numbers are summed, instead. (Compare with Python argparse.)

#include <climits>
#include <argumentum/argparse.h>
#include <numeric>
#include <vector>

using namespace std;
using namespace argumentum;

int main( int argc, char** argv )
{
   vector<int> numbers;
   bool isSum = false;

   auto parser = argument_parser{};
   auto params = parser.params();
   parser.config().program( argv[0] ).description( "Accumulator" );
   params.add_parameter( numbers, "N" ).minargs( 1 ).metavar( "INT" ).help( "Integers" );
   params.add_parameter( isSum, "--sum", "-s" )
         .nargs( 0 )
         .help( "Sum the integers (default: find the max)" );

   if ( !parser.parse_args( argc, argv, 1 ) )
      return 1;

   auto mmax = []( auto&& a, auto&& b ) { return max( a, b ); };
   auto acc = isSum ? accumulate( numbers.begin(), numbers.end(), 0 )
                    : accumulate( numbers.begin(), numbers.end(), INT_MIN, mmax );
   cout << acc << "\n";
   return 0;
}

The basic example can be run like this:

$ basic 1 3 2
3

$ basic --sum 1 3 2
6

$ basic
usage: basic [--sum] [--help] INT [INT ...]

Accumulator

positional arguments:
  INT        Integers

optional arguments:
  -s, --sum  Sum the integers (default: find the max)
  -h, --help Display this help message and exit.

The program can read command line arguments from one or more files. Each line of the file contains one argument and the files can be nested.

$ cat numbers.opt
1
2
3
4

$ basic @numbers.opt
4

$ basic --sum @numbers.opt @numbers.opt
20

$ cat summany.opt
--sum
@numbers.opt
@numbers.opt
@numbers.opt

$ basic @summany.opt
30

Target values

The parser parses input strings and stores the parsed results in target values which are ordinary C++ variables. The target values must outlive the parser because the parser stores target value references. On the other hand, the parser can be destroyed after parsing is done.

The supported types of target values are:

  • C++ numeric types, bool, std::string,
  • any type that has a constructor that accepts std::string,
  • any type that has an operator= that accepts std::string,
  • any type T for which a converter argumentum::from_string<T>::convert exists,
  • std::vector of simple target values.

If information about whether a value was set or not is needed, std::optional can be used:

   std::optional<std::string> str;
   params.add_parameter( str, "-s" ).maxargs( 1 );

   // ... parser.parse_args ...

   if ( !str )
      std::cout << "The option -s was not used.\n";
   else
      std::cout << "The option -s was set with value '" << *str << "' .\n";

Additional types can be supported through parsing actions like in the examples below.

Variants of the basic example

The same example implemented with an action and a default value:

#include <climits>
#include <argumentum/argparse.h>
#include <numeric>
#include <vector>

using namespace std;
using namespace argumentum;

int main( int argc, char** argv )
{
   vector<int> numbers;
   auto max = []( int a, int b ) { return std::max( a, b ); };
   auto sum = []( int a, int b ) { return a + b; };
   std::pair<std::function<int( int, int )>, int> operation;

   auto parser = argument_parser{};
   auto params = parser.params();
   parser.config().program( argv[0] ).description( "Accumulator" );
   params.add_parameter( numbers, "N" ).minargs( 1 ).metavar( "INT" ).help( "Integers" );
   params.add_parameter( operation, "--sum", "-s" )
         .nargs( 0 )
         .absent( std::make_pair( max, INT_MIN ) )
         .action( [&]( auto& target, const std::string& value ) {
            target = std::make_pair( sum, 0 );
         } )
         .help( "Sum the integers (default: find the max)" );

   if ( !parser.parse_args( argc, argv, 1 ) )
      return 1;

   auto acc = accumulate( numbers.begin(), numbers.end(), operation.second, operation.first );
   cout << acc << "\n";
   return 0;
}

In simple programs target variables can be declared in the function where parameters are defined and arguments parsed. In larger programs it is more convenient to store target variables in one or more structures. This is the same example rewritten so that it stores options in a structure:

#include <climits>
#include <argumentum/argparse.h>
#include <numeric>
#include <vector>

using namespace std;
using namespace argumentum;

class AccumulatorOptions : public argumentum::Options
{
public:
   vector<int> numbers;
   std::pair<std::function<int( int, int )>, int> operation;

protected:
   void add_parameters( ParameterConfig& params ) override
   {
      auto max = []( int a, int b ) { return std::max( a, b ); };
      auto sum = []( int a, int b ) { return a + b; };

      params.add_parameter( numbers, "N" ).minargs( 1 ).metavar( "INT" ).help( "Integers" );
      params.add_parameter( operation, "--sum", "-s" )
            .nargs( 0 )
            .absent( std::make_pair( max, INT_MIN ) )
            .action( [&]( auto& target, const std::string& value ) {
               target = std::make_pair( sum, 0 );
            } )
            .help( "Sum the integers (default: find the max)" );
   }
};

void execute( AccumulatorOptions& opt )
{
   auto acc = accumulate(
         opt.numbers.begin(), opt.numbers.end(), opt.operation.second, opt.operation.first );
   cout << acc << "\n";
}

int main( int argc, char** argv )
{
   auto parser = argument_parser{};
   auto params = parser.params();
   parser.config().program( argv[0] ).description( "Accumulator" );

   auto pOptions = std::make_shared<AccumulatorOptions>();
   params.add_parameters( pOptions );

   if ( !parser.parse_args( argc, argv, 1 ) )
      return 1;

   execute( *pOptions );
   return 0;
}

When a program becomes even more complex it can be subdivided into commands that often act as independent programs. We can rewrite the above example with commands. The main change is that the class AccumulatorOptions is now derived from CommandOptions which has the method execute that we use to execute the selected command.

#include <climits>
#include <argumentum/argparse.h>
#include <numeric>
#include <vector>

using namespace std;
using namespace argumentum;

class AccumulatorOptions : public argumentum::CommandOptions
{
public:
   vector<int> numbers;
   std::pair<std::function<int( int, int )>, int> operation;

   void execute( const ParseResults& res )
   {
      auto acc = accumulate( 
         numbers.begin(), numbers.end(), operation.second, operation.first );
      cout << acc << "\n";
   }

protected:
   void add_parameters( ParameterConfig& params ) override
   {
     // ... same as above
   }
};

class CmdEchoOptions : public argumentum::CommandOptions
{
public:
   vector<int> numbers;

public:
   void execute( const ParseResult& res ) override
   {
      for ( auto n : numbers )
         cout << n << " ";
      cout << "\n";
   }

protected:
   void add_parameters( ParameterConfig& params ) override
   {
      params.add_parameter( numbers, "N" ).minargs( 1 ).metavar( "INT" ).help( "Integers" );
   };
};


int main( int argc, char** argv )
{
   auto parser = argument_parser{};
   auto params = parser.params();
   parser.config().program( argv[0] ).description( "Accumulator" );
   params.add_command<CmdAccumulatorOptions>( "fold" ).help( "Accumulate integer values." );
   params.add_command<CmdEchoOptions>( "echo" ).help( "Echo integers from the command line." );

   auto res = parser.parse_args( argc, argv, 1 );
   if ( !res )
      return 1;

   auto pcmd = res.commands.back();
   if ( !pcmd )
      return 1;

   pcmd->execute( res );
   return 0;
}

Command options are instantiated and added to the parser only when the appropriate command is selected with an argument. The chain of instantiated subcommands is stored in ParseResults::commands. Typically we execute only the last instantiated (the "deepest") subcommand.

If a program has global options that we want to access from a command, we have to instantiate command options in advance and set a link in command options to global options. In this case it is most convenient to store global options in an Options structure. Even though we add an instance of command options to the parser, the actual options will be registered with the parser only when the command is activated by the arguments.

In the following example AccumulatorOptions need access to global options. Only the added and modified methods are shown:

class GlobalOptions : public argumentum::Options
{
public:
   int logLevel = 0;
   void add_parameters( ParameterConfig& params ) override
   {
      params.add_parameter( logLevel, "--loglevel" ).nargs( 1 );
   }
};

class AccumulatorOptions : public argumentum::CommandOptions
{
   std::shared_ptr<GloblaOptions> mpGlobal;
public:
   AccumulatorOptions( std::string_view name, std::shared_ptr<GloblaOptions> pGlobal )
      : CommandOptions( name )
      , mpGlobal( pGlobal )
  {}

  void execute( const ParseResults& res )
  {
     if ( mpGlobal && mpGlobal->logLevel > 0 )
       cout << "Accumulating " << numbers.size() << " numbers\n";

     auto acc = accumulate(
        numbers.begin(), numbers.end(), operation.second, operation.first );
     cout << acc << "\n";
  }
};

int main( int argc, char** argv )
{
   auto parser = argument_parser{};
   auto params = parser.params();
   parser.config().program( argv[0] ).description( "Accumulator" );

   auto pGlobal = std::make_shared<GlobalOptions>();
   auto pAccumulator = std::make_shared<CmdAccumulatorOptions>( "fold", pGlobal );

   params.add_parameters( pGlobal );
   params.add_command( pAccumulator ).help( "Accumulate integer values." );
   params.add_command<CmdEchoOptions>( "echo" ).help( "Echo integers from the command line." );

   auto res = parser.parse_args( argc, argv, 1 );
   if ( !res )
      return 1;

   auto pcmd = res.commands.back();
   if ( !pcmd )
      return 1;

   pcmd->execute( res );
   return 0;
}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].