Git Product home page Git Product logo

flags's Introduction

⛳ flags

Build Status

Simple, extensible, header-only C++17 argument parser released into the public domain.

why

Other argument parsers are:

  • bloated
  • non-extensible
  • not modern
  • complicated

requirements

GCC 7.0 or Clang 4.0.0 at a minimum. This library makes extensive use of optional, nullopt, and string_view.

api

flags::args exposes seven methods:

get

std::optional<T> get(const std::string_view& key) const

Attempts to parse the given key on the command-line. If the string is malformed or the argument was not passed, returns nullopt. Otherwise, returns the parsed type as an optional.

get (with default value)

T get(const std::string_view& key, T&& default_value) const

Functions the same as get, except if the value is malformed or the key was not provided, returns default_value. Otherwise, returns the parsed type.

get_multiple

std::vector<std::optional<T>> get_multiple(const std::string_view& option) const

Get all values passed for an option. If no value is specified (--foo --bar) or the value is malformed, nullopt will be used. Values will be in the order they were passed.

get_multiple (with default value)

std::vector<T> get_multiple(const std::string_view& option, T&& default_value) const

Functions the same as get_multiple, except if the value is malformed or no value is provided, default_value will be used.

get (positional)

std::optional<T> get(size_t positional_index) const

Get an argument from the positional arguments at a specified index. If the value is malformed or the index is invalid, nullopt is returned.

get (positional with default value)

T get(size_t positional_index, T&& default_value) const

Functions the same as positional get, except if the value is malformed or the index is invalid, returns default_value. Otherwise, returns the parsed type.

positional

const std::vector<std::string_view>& positional() const

Returns all of the positional arguments from argv in order.

usage

just the headers

Just include flags.h from the include directory into your project.

Using CMake

CMake Installation

Flags can be built and installed using [CMake], e.g.

$ mkdir build
$ cd build
$ cmake ..
$ make
$ make install

The above will install Flags into the standard installation path on a UNIX system, e.g. /usr/local/include/. To change the installation path, use:

$ cmake .. -DCMAKE_INSTALL_PREFIX=../install

in the above.

find_package

Installation creates a flags-config.cmake which allows CMake projects to find Flags using find_package:

find_package(flags)

This exports the flags target which can be linked against any other target. Linking against flags automatically sets the include directories and required flags for C++17 or later. For example:

add_executable(myexe mysources...)
target_link_libraries(myexe PRIVATE flags)

add_subdirectory

The Flags can also be added as a dependency with add_subdirectory:

add_subdirectory(path/to/flags)

This also exports the flags target which can be linked against any other target just as with the installation case.

example

#include "flags.h" // #include <flags.h> for cmake
#include <iostream>

int main(int argc, char** argv) {
  const flags::args args(argc, argv);

  const auto count = args.get<int>("count");
  if (!count) {
    std::cerr << "No count supplied. :(\n";
    return 1;
  }
  std::cout << "That's " << *count << " incredible, colossal credits!\n";

  if (args.get<bool>("laugh", false)) {
    std::cout << "Ha ha ha ha!\n";
  }
  return 0;
}
$ ./program
> No count supplied. :(
$ ./program --count=5 --laugh
> That's 5 incredible, colossal credits!
> Ha ha ha ha!

another example

#include "flags.h" // #include <flags.h> for cmake
#include <iostream>
#include <string>

int main(int argc, char** argv) {
  const flags::args args(argc, argv);
  const auto& files = args.positional();
  const auto verbose = args.get<bool>("verbose", false);
  if (verbose) {
    std::cout << "I'm a verbose program! I'll be reading the following files:\n";
    for (const auto& file : files) {
      std::cout << "* " << file << '\n';
    }
  }
  // read files(files);
  return 0;
}
$ ./program /tmp/one /tmp/two /tmp/three --verbose
> I'm a verbose program! I'll be reading the following files:
> * /tmp/one
> * /tmp/two
> * /tmp/three
$ ./program /tmp/one /tmp/two /tmp/three --noverbose
>%

extensions

flags simply uses the istream operator to parse values from argv. To extend the parser to support your own types, just supply an overloaded >>.

example

struct Date {
  int day;
  int month;
  int year;
};

// Custom parsing code.
std::istream& operator>>(std::istream& stream, Date& date) {
  return stream >> date.day >> date.month >> date.year;
}

int main(int argc, char** argv) {
  const flags::args args(argc, argv);
  if (const auto date = args.get<Date>("date")) {
    // Output %Y/%m/%d if a date was provided.
    std::cout << date->year << ":" << date->month << ":" << date->day << '\n';
    return 0;
  }
  // Sad face if no date was provided or if the input was malformed.
  std::cerr << ":(\n";
  return 1;
}
$ ./program --date="10 11 2016"
> 2016:11:10
$ ./program
> :(

command line details

flags's primary goal is to be simple to use for both the user and programmer.

key formatting

A key can have any number of preceding -s, but must have more than 0. The following are valid keys:

  • -key
  • --key
  • -------------key

value assignment

A value can be assigned to a key in one of two ways:

  • $ ./program --key=value
  • $ ./program --key value

bools

booleans are a special case. The following values make an argument considered false-y when parsed as a bool:

  • f
  • false
  • n
  • no
  • 0

If none of these conditions are met, the bool is considered true.

testing

flags uses both bfg9000 and mettle for unit-testing. After installing both bfg9000 and mettle, run the following commands to kick off the tests:

  1. 9k build/
  2. cd build
  3. ninja test

contributing

Contributions of any variety are greatly appreciated. All code is passed through clang-format using the Google style.

flags's People

Contributors

charleywright avatar jparr721 avatar nkwacker avatar ricop avatar sailormoon avatar yahiaetman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flags's Issues

Boolean options return false if no value was specified in the command line

Steps to reproduce:

  1. Copy the example to a file named src.cpp
#include "flags.h"
#include <iostream>

int main(int argc, char** argv) {
  const flags::args args(argc, argv);

  const auto count = args.get<int>("count");
  if (!count) {
    std::cerr << "No count supplied. :(\n";
    return 1;
  }
  std::cout << "That's " << *count << " incredible, colossal credits!\n";

  if (args.get<bool>("laugh", false)) {
    std::cout << "Ha ha ha ha!\n";
  }
  return 0;
}
  1. Add flags.h to the same folder.
  2. Compile using clang src.cpp -std=c++17 -o program.exe
  3. Run using .\program.exe --count 5 --laugh

Expected result:

That's 5 incredible, colossal credits!
Ha ha ha ha!

Actual result:

That's 5 incredible, colossal credits!

Environment:

  • OS: Windows 10
  • Compiler: Clang 9.0.0

Reason behind the issue:

template <>
std::optional<bool> get(const argument_map& options,
                        const std::string_view& option) {
  if (const auto value = get_value(options, option)) {
    return std::none_of(falsities.begin(), falsities.end(),
                        [&value](auto falsity) { return *value == falsity; });
  }
  return std::nullopt;
}

In the aforementioned code, the function cannot differentiate between the two scenarios:

  1. .\program.exe --count 5 --laugh
  2. .\program.exe --count 5

Since in both cases, args.get<bool>("laugh") will return std::nullopt.

Suggested fix:
Check if the option exists in the options map.

template <>
std::optional<bool> get(const argument_map& options,
                        const std::string_view& option) {
  if (const auto value = get_value(options, option)) {
    return std::none_of(falsities.begin(), falsities.end(),
                        [&value](auto falsity) { return *value == falsity; });
  }
  if (options.find(option) != options.end())
      return true;
  return std::nullopt;
}

Argument validation + shortcuts

Now that there are some basic unit tests, flag validation and multiple arguments should be added.

Currently thinking of a syntax similar to:

const flags::args args(argc, argv);
if (const auto opt = args.get_multiple<bool>("flag", "f", "no-flag", "other")) {
  const auto [argument, value] = *opt;
  // do something -- *argument will be the first of "flag", "f", or "no-flag" that matched
  // and *value will be its value.
}

for multiple arguments.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.