Fire for C++: Create fully functional CLIs using function signatures

Overview

Fire for C++

Fire for C++, inspired by python-fire, is a single header library that creates a command line interface from a function signature. Here's the whole program for adding two numbers with command line:

#include <iostream>
#include <fire-hpp/fire.hpp>

int fired_main(int x = fire::arg("-x"), int y = fire::arg("-y")) {
   std::cout << x + y << std::endl;
   return 0;
}

FIRE(fired_main)

That's all. Usage:

$ ./add -x=1 -y=2
3

As you likely expect,

  • --help prints a meaningful message with required arguments and their types.
  • an error message is displayed for incorrect usage.
  • the program runs on Linux, Windows and Mac OS.

See examples for other kinds of arguments.

Why yet another CLI library?!

With most libraries, creating a CLI roughly follows this pattern:

  1. define arguments
  2. call parse(argc, argv);
  3. check whether errors are detected by parse(), print them and return (optional)
  4. check for -h and --help, print the help message and return (optional)
  5. for each argument:
    1. get argument from the map and if necessary convert to the right type
    2. try/catch for errors in conversion and return (optional)

That's a non-trivial amount of boilerplate, especially for simple scripts. Because of that, programmers (and a lot of library examples) tend to skip the optional parts, however this incurs a significant usability cost. Also, many libraries don't help at all with the conversion step.

With fire-hpp, you only call FIRE(fired_main) and define arguments as function parameters. When fired_main() scope begins, all steps have already been completed.

What's covered?

Q. Quickstart

Q.1 Requirements

  • Using fire.hpp: C++11 compatible compiler.
  • Compiling examples: CMake 3.1+.
  • Compiling/running tests: CMake 3.11+ and Python 3.5+. GTest is downloaded, compiled and linked automatically.

Q.2 Running examples

Steps to run examples:

  • Clone repo: git clone https://github.com/kongaskristjan/fire-hpp
  • Create build and change directory: cd fire-hpp && mkdir build && cd build
  • Configure/build: cmake .. && cmake --build . (or substitute the latter command with appropriate build system invocation, eg. make -j8 or ninja)
  • If errors are encountered, clear the build directory and disable pedantic warnings as errors with cmake -D DISABLE_PEDANTIC= .. (you are encouraged to open an issue).
  • Run: ./examples/basic --help or ./examples/basic -x=3 -y=5

T. Tutorial

Let's go through each part of the following example.

int fired_main(int x = fire::arg("-x"), int y = fire::arg("-y")) { // Define and convert arguments
    std::cout << x + y << std::endl; // Use x and y, they're ints!
    return 0;
}

FIRE(fired_main) // call fired_main()
  • FIRE(function name) FIRE(fired_main) expands into the actual main() function that defines your program's entry point and fires off fired_main(). fired_main is called without arguments, thus compiler is forced to use the default fire::arg values.

  • fire::arg(identifiers[, default value]) A constructor that accepts the name/shorthand/description/position of the argument. Use a brace enclosed list for several of them (eg. fire::arg({"-x", "--longer-name", "description of the argument"}) or fire::arg({0, "zeroth element"}). The library expects a single dash for single-character shorthands, two dashes for multi-character names, and zero dashes for descriptions. fire::arg objects should be used as default values for fired function parameters. See documentation for more info.

  • int fired_main(arguments) This is what you perceive as the program entry point. All arguments must be bool, integral, floating-point, fire::optional<T>, std::string or std::vector<T> type and default initialized with fire::arg objects (Failing to initialize properly results in undefined behavior!). See conversions to learn how each of them affects the CLI.

D. Documentation

D.1 FIRE(fired_main[, program_description]) and variants

  • FIRE(fired_main[, program_description]) creates the main function that parses arguments and calls fired_main.
  • FIRE_NO_EXCEPTIONS(...) is similar, but can be used even if compiler has exceptions disabled. However, this imposes limitations on what the library can parse. Specifically, it disallows space assignment, eg. -x 1 must be written as -x=1.
  • FIRE_ALLOW_UNUSED(...) is similar to FIRE(...), but allows unused arguments. This is useful when raw arguments are accessed (eg. for another library).

Program description can be supplied as the second argument:

FIRE(fired_main, "Hello there")

D.2 fire::arg(identifiers[, default_value]])

D.2.1 Identifiers

Identifiers are used to find arguments from command line and provide a description. In general, it's a brace enclosed list of elements (braces can be omitted for a single element):

  • "-s" shorthand name for argument
  • "--multicharacter-name"
  • 0 index of a positional argument
  • "<name of the positional argument>"
  • any other string: "description of any argument"
  • variadic arguments: fire::variadic()

  • Example: int fired_main(int x = fire::arg("-x"));

    • CLI usage: program -x=1
  • Example: int fired_main(int x = fire::arg({"-x", "--long-name"}));

    • CLI usage: program -x=1
    • CLI usage: program --long-name=1
  • Example: int fired_main(int x = fire::arg({0, "<name of argument>", "description"}));

    • CLI usage: program 1
    • <name of argument> and description appear in help messages
  • Example: int fired_main(vector<int> x = fire::arg(fire::variadic()));

    • CLI usage: program 1 2 3

D.2.2 Default value (optional)

Default value if no value is provided through command line. Can be either std::string, integral or floating-point type and fire::arg must be converted to that same type. This default is also displayed on the help page.

  • Example: int fired_main(int x = fire::arg({"-x", "--long-name"}, 0));
    • CLI usage: program -> x==0
    • CLI usage: program -x=1 -> x==1

For an optional argument without a default, see fire::optional.

D.3 fire::arg conversions

To conveniently obtain arguments with the right type and automatically check the validity of input, fire::arg class defines several implicit conversions.

D.3.1 std::string, integral, or floating point

Converts the argument value on command line to the respective type. Displays an error if the conversion is impossible or default value has wrong type.

  • Example: int fired_main(std::string name = fire::arg("--name"));

    • CLI usage: program --name=fire -> name=="fire"
  • Example: int fired_main(double x = fire::arg("-x"));

    • CLI usage: program -x=2.5 -> x==2.5
    • CLI usage: program -x=blah -> Error: value blah is not a real number

D.3.2 fire::optional

Used for optional arguments without a reasonable default value. This way the default value doesn't get printed in a help message. The underlying type can be std::string, integral or floating-point.

fire::optional is a tear-down version of std::optional, with compatible implementations for has_value(), value_or() and value().

  • Example: int fired_main(fire::optional<std::string> name = fire::arg("--name"));
    • CLI usage: program -> name.has_value()==false, name.value_or("default")=="default"
    • CLI usage: program --name="fire" -> name.has_value()==true and name.value_or("default")==name.value()=="fire"

D.3.3 bool: flag argument

Boolean flags are true when they exist on command line and false when they don't. Multiple single-character flags can be packed on command line by prefixing with a single hyphen: -abc <=> -a -b -c

  • Example: int fired_main(bool flag = fire::arg("--flag"));
    • CLI usage: program -> flag==false
    • CLI usage: program --flag -> flag==true

D.3.4 std::vector: variadic argument

A method for getting all positional arguments as a vector. The fire::arg object can be converted to std::vector<std::string>, std::vector<integral type> or std::vector<floating-point type>. Using variadic argument forbids extracting positional arguments with fire::arg(index).

In this case, identifier should be fire::variadic(). Description can be supplied in the usual way.

  • Example: int fired_main(vector<std::string> params = fire::arg({fire::variadic(), "description"}));
    • CLI usage: program abc xyz -> params=={"abc", "xyz"}
    • CLI usage: program -> params=={}

D.4 Accessing raw arguments

Some third party libraries require access to raw argc/argv. This is gained through fire::raw_args (of type fire::c_args), which has argc() and argv() methods for accessing the arguments.

Examples:

  • Usage without modification:
int argc = fire::raw_args.argc();
char ** argv = fire::raw_args.argv();
non_modifying_call(argc, argv);
  • Usage with modification:
fire::c_args raw_args_copy = fire::raw_args; 
int& argc = raw_args_copy.argc();
char ** argv = raw_args_copy.argv();
modifying_call(argc, argv);
// Once out of scope, raw_args_copy releases argv strings

You also need FIRE_ALLOW_UNUSED(...) if the third party library processes it's own arguments.

CMake integration

Fire can easily be used by other C++ CMake projects.

You may use Fire from a folder in your project (typically a git submodule).

cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
project(foo)
set(CMAKE_CXX_STANDARD 11)

add_subdirectory(fire_folder)

add_executable(bar bar.cpp)
target_link_libraries(bar fire-hpp::fire-hpp)

Alternatively, you can also use the more modern FetchContent.

cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project(foo)
set(CMAKE_CXX_STANDARD 11)

include(FetchContent)
FetchContent_Declare(
  fire
  GIT_REPOSITORY https://github.com/kongaskristjan/fire-hpp
)
FetchContent_MakeAvailable(fire)

add_executable(bar bar.cpp)
target_link_libraries(bar fire)

Fire can also be installed and found through find_package():

cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
project(foo)
set(CMAKE_CXX_STANDARD 11)

find_package(fire-hpp REQUIRED)
add_executable(bar bar.cpp)
target_link_libraries(bar fire-hpp::fire-hpp)

Conan integration

Fire can be packaged for consumption through Conan by running conan create . fire-hpp/[email protected]/channel from the root directory of this repository. It can then be consumed as in the find_package() example above if using cmake_find_package generator.

Development

This library uses extensive testing. Unit tests are located in tests/, while examples/ are used as integration tests. The latter also ensures examples are up-to-date. Before committing, please verify python3 ./build/tests/run_standard_tests.py succeed. Releases are also tested on many platforms with python3 ./tests/run_release_tests.py.

v0.2 release is tested on:

  • Arch Linux: gcc==10.2.0, clang==10.0.1: C++11, C++14, C++17, C++20
  • Ubuntu 18.04: gcc=={4.8, 4.9}: C++11 and gcc=={5.5, 6.5, 7.5, 8.4}: C++11, C++14, C++17
  • Ubuntu 18.04: clang=={3.5, 3.6, 3.7, 3.8, 3.9, 4.0}: C++11, C++14 and clang=={5.0, 6.0, 7.0, 8.0, 9.0}: C++11, C++14, C++17
  • Windows 10: MSVC=={19.26} (2019 Build Tools): C++11, C++14, C++17
  • Mac OS: XCode=={12.0} (requires CMake 3.18.1+): C++11, C++14, C++17

Roadmap:

Current state

v0.2 release

  • Sufficient information for contributing
    • Create a document describing the internals of this project
    • Document the code
    • Add contribution guidelines
    • Add issue and PR templates
    • Thoroughly describe each task in the roadmap
  • Subcommands (eg. git add and git show, which may have different flags/options)
  • Possibility to raise errors and print help in fired_main()
  • Host documentation on readthedocs.io

v0.3 release

  • Solve Windows non-ascii character input
  • Self-defined objects with string streams
  • Argument requirements
    • fire::arg(identifier).require(condition, msg)
    • Specialized requirements, eg. bounds(min, max), one_of([option1], [option2], ...)
  • fire::arg(identifier).save([condition]), which will save the program from exiting even if not all required arguments are present or correct (eg. for --version)

v0.4 release

  • Ensure API user gets an error message when using required positional arguments after optional positional arguments
  • Allow converting named fire::arg to std::vector or std::map (allows multiple occurrences of an argument, like cmake's -D)
  • Automatic testing for error messages

v1.0 release

Issues
  • Standard cpp structure

    Standard cpp structure

    Move fire.hpp to include/fire/fire.hpp in order to make it consistent with the inofficial standard. Introduce a fire target to simplify the test and example compilation.

    opened by cxkoda 15
  • Added CMake install commands and a local conanfile.py.

    Added CMake install commands and a local conanfile.py.

    Added install commands for fire.hpp into CMakeLists.txt. It's not super likely people will make install a single-header lib onto their system, but they might need to install to some prefix as part of a superbuild or something, I dunno. The real reason for my adding it though is to better support package managers. Having the library provide a suggested install layout helps keep deployments uniform since the recipe author doesn't need to manually copy files and decide on a layout of their own. It also makes, say, migrating from one package manager to another easier if everyone just uses the suggested layout and you don't need to fix header paths to do so. (Speaking from experience, this sucks.)

    Which, speaking of, I also included a conanfile.py which installs the checked out copy as a Conan package. I'm sure someone will find that handy.

    PS: This all came about because I use Conan for my package management and wanted to submit a package recipe for this library to conan-center, and having the install instructions in the library instead of in the package recipe is good for the C++ ecosystem for the aforementioned reasons. I could just release/package off my fork, but that didn't really seem right, so I figured I'd rather try to push these changes upstream.

    opened by eigenwhat 8
  • make argc/argv accessible through fire

    make argc/argv accessible through fire

    I've been using fire-hpp in production, and it's an awesome library, thank you for creating this!

    One thing I've noticed is that a lot of frameworks with which I'd want to use fire (Qt, various loggers) require access to argc and argv. My workaround has been to break open the FIRE macro and copy argc/argv into file scoped variables, but I think that's undesirable, especially as the macro continues to get more complex with additional development.

    Although in many cases you shouldn't need them, I think it makes sense for fire to provide access to argc/argv through some static variables or functions in the fire namespace, so that users will be able to use the fire macros as intended even when mixing frameworks.

    This PR represents a possible implementation.

    opened by nholthaus 5
  • Cmake integrability

    Cmake integrability

    Improve the integrability in other CMake projects by exporting a fire target and disabling the compilation of tests and examples if included as sub-project.

    opened by cxkoda 4
  • The description string does not work as documented

    The description string does not work as documented

    When I provide a help description string, it is used as a default instead of a description:

    int fired_main(std::string file = fire::arg(0, "<first>"))
    

    When I run this as of commit 2a562ff, I get:

    $ ./build/colormapper.exe --help
    
    Usage:
      C:\git\maph\submodules\colormapper\build\colormapper.exe [<0>]
    
    Options:
      [<0> STRING]   [default: <first>]
    

    <first> becomes the default value instead of the description. I tried this with an int positional argument as well std::string and it has the same problem.

    Thank you for the header! This is an interesting project and it looks promising to reduce boilerplate.

    opened by JeffIrwin 3
  • Downloading the project (gtest) is unsafe unless you store and verify the cryptographic hash

    Downloading the project (gtest) is unsafe unless you store and verify the cryptographic hash

    Here: https://github.com/kongaskristjan/fire-hpp/blob/master/tests/CMakeLists.txt#L10

    git protocol doesn't guarantee that actual downloaded file corresponds to the requested tag. Malicious git server can easily return a malicious file instead.

    Please either ensure that the cryptographic hash (fingerprint) is verified, or use a pre-installed gtest.

    opened by yurivict 1
  • licensing

    licensing

    This is such a great repo, really love what you're doing! Even in the early stages this project is useful and ready for certain production environments.

    It's difficult to consume though without understanding what the licensing requirements will be.

    opened by nholthaus 1
  • What about

    What about "--" to separate positional arguments?

    Ideally a command line parsing library should support -- to separate positional arguments from options., to support CLI interface like this:

    $ touch -- -actual-file-with-dash-prefix
    $ ls -l -- -actual-file-with-dash-prefix
    -rw-r--r-- 1 bronek users 0 Aug  8 19:03 -actual-file-with-dash-prefix
    $ rm -- -actual-file-with-dash-prefix
    

    BTW, this is lovely library, thank you for doing it!

    opened by Bronek 1
Releases(v0.2)
  • v0.2(Nov 7, 2020)

    v0.2 is mostly a matured version of v0.1, in a sense that not many new features were added, however existing features were improved. In summary, a lot of breaking changes and a lot of internal structural changes. The rationale is to make most of the breaking changes early in development, so that new features can be added to a "more final" version of the library.

    Release notes

    Breaking changes:

    • Include fire as <fire-hpp/fire.hpp> not <fire.hpp>.
    • Hyphens are also written when specifying argument names. So instead of fire::arg("x") and fire::arg("long-name") you write fire::arg("-x") and fire::arg("--long-name").
    • Name/position and description are now both the first argument. Eg. now it's fire::arg({name, description}, default value) instead of fire::arg(name, description, default value).
    • Variadic arguments are now specified as fire::arg(fire::variadic()) instead of fire::arg::vector().
    • No more FIRE_NO_SPACE_ASSIGNMENT(...).

    New library features:

    • FIRE(...) now works with positional/variadic argument, previously needed FIRE_NO_SPACE_ASSIGNMENT(...). This was a HUGE change internally, had to implement a totally new argument introspection algorithm.
    • fire.hpp now compiles without exception support, but you need to use FIRE_NO_EXCEPTIONS(...) instead of FIRE(...), which has limitations.
    • Program descriptions.
    • Positional arguments can be supplied with custom names.

    Improvements:

    • In program -- --some-text --some-text is interpreted as positional, per convention.
    • Much cleaner help messages. Clarified a few error messages too.
    • Accept arguments in a manner similar to make -j8 <=> make -j 8 if unambiguous.
    • Misc bug fixes.

    Integration

    • CMake integration. (thanks cxkoda).
    • Conan package (https://conan.io/center/fire-hpp) (thanks axalon900).
    Source code(tar.gz)
    Source code(zip)
  • v0.1(Jul 3, 2020)

    Features:

    • Flags, named, positional, vector parameters
    • Optional parameters, default values
    • Conversions to arithmetic types
    • Parameter descriptions
    • All of this using just function signatures
    Source code(tar.gz)
    Source code(zip)
Owner
Kristjan Kongas
Kristjan Kongas
A wrapper of C++ std::vector for functional programming

Say hello to functional C++ vectors A wrapper for C++ std::vector geared towards functional programming and fluent APIs. The primary focus is readabil

null 17 Feb 18, 2022
A simple command line application in order to create new Code workspaces.

mkcws Summary A simple command line application in order to create new Code workspaces. License This project's license is GPL 2. The whole license tex

Kevin Matthes 0 Apr 1, 2022
Free open-source modern C++17 / C++20 framework to create console, forms (GUI like WinForms) and unit test applications on Microsoft Windows, Apple macOS and Linux.

xtd Modern C++17/20 framework to create console (CLI), forms (GUI like WinForms) and tunit (unit tests like Microsoft Unit Testing Framework) applicat

Gammasoft 325 Jul 4, 2022
Simple command line tools to create/extract X4 .cat+.dat files

x4cat Simple command line tools to to create/extract X4 .cat+.dat files x4encat Usage: x4encat <archive name> Looks for a directory named <archive nam

Alexander Sago 1 Oct 31, 2021
A little UNIX-inspired terminal application for the Numworks Calculator (not using escher).

L.E. Terminal (let for short) is a little UNIX-inspired terminal for the Numworks Calculator.

Cacahu├Ęte Sans Sel 18 Apr 27, 2022
CLI Application that provides the Freedesktop Secret Service using Pass as its backend!

pass-secrets CLI Application that provides the Freedesktop Secret Service using Pass as its backend! Status Currently working to store secrets with pr

null 18 Apr 7, 2022
Command-Based Text Editor written in cpp using Linked Lists and Stack

Command Based Text Editor Our goal in this project is to write a command-based text editor in cpp using linked lists and stack. This text editor will

bedirhanbardakci 3 Jun 9, 2021
Simple command line tool that processes image files using the FidelityFX Super Resolution (FSR) or Contrast Adaptive Sharpening (CAS) shader systems.

Simple command line tool that processes image files using the FidelityFX Super Resolution (FSR) or Contrast Adaptive Sharpening (CAS) shader systems.

GPUOpen Effects 174 Jun 26, 2022
Encrypted shellcode injector with basic virtual machine evasion using C++

C++ PE Injector Overview Fully undetectable shellcode injector written in C++ with customizable XOR payload encryption/decryption and basic antivirus

Kampourakis Emmanouil 5 Apr 5, 2022
This is a terminal made using C language.

CommandConsole As the name suggests this is a terminal like software. Like a normal terminal in linux or command prompt in windows, it also works like

Shreejan Dolai 9 Feb 14, 2022
A math parser made in 1 hour using copilot.

An entire math parser made with Copilot Copilot wrote 91% of the code in this, amazing isn't it? It supports all normal mathematical expressions excep

Duckie 4 Dec 7, 2021
This repository contains the source code of the project(StereoCraft) that we have developed for the Mixed Reality Hackathon organized by Microsoft using StereoKit SDK

StereoCraft - A block-building like experience built using StereoKit This repository contains the source code of the project that we have developed fo

G Bhanuteja 2 Dec 23, 2021
A giant list of any useful signatures for developing CSGO cheats.

csgo-signatures A giant list of any useful signatures for developing CSGO cheats. If one of them doesn't work or is incorrect, please let me know and

KittenPopo 67 Mar 16, 2022
A fully-functional low-cost reversion of Mutable instrument's Peaks

$peaks, a fully-functional low-cost reversion of Mutable instrument's Peaks Motivation Where I am from it is sometimes very difficult to source compon

null 32 Feb 14, 2022
A fully-functional open source and open hardware mechanical USB computer keyboard with only three keys!

threeboard threeboard is a fully-functional open source and open hardware mechanical USB computer keyboard with only three keys. It supports multiple

Conor Taylor 97 May 23, 2022
This is a c++ implement of yolov5 and fire/smoke detect.

A C++ implementation of Yolov5 to detect fire or smoke in the wild in Jetson Xavier nx and Jetson nano This repository uses yolov5 and deepsort to fol

null 10 May 23, 2022
Take Damage hook hook made to increase weapon damage, the game I made is Free Fire in version 1.65

Take-Damage Simple Take Damage hook hook made to increase weapon damage, the game I made is Free Fire in version 1.65 Bool bool isTakeDemageBool = fal

Master Games 3 Jan 1, 2022
The module for my life story archive that gives data and statistics for the family Kindle Fire.

By: Top README.md Read this article in a different language Sorted by: A-Z Sorting options unavailable ( af Afrikaans Afrikaans | sq Shqiptare Albania

Sean P. Myrick V19.1.7.2 1 Dec 23, 2021
web server & client. Fully C++/WebAssembly. Server runs on google cloud function. Client uses a C++ virtual dom.

Starter project. A web server and client fully made with C++/WebAssembly. A simple CMake configuration describes how to build and run everything.

null 3 Aug 6, 2021
Create a fully undetectable backdoor with simple steps.

?? Generate FUD backdoor with a Python Crypter ?? Follow the steps bellow to generate a crypted shellcode that can be used on a C++ executable. Clone

Dimitris Kalopisis 42 Jun 27, 2022
Collection of DLL function export forwards for DLL export function proxying

dll-exports Collection of DLL function export forwards for DLL export function proxying. Typical usecase is for backdooring applications for persisten

Magnus Stubman 34 Jun 26, 2022
C-function for traversing files/directories effectively and calling a given function with each encountered file and a void-pointer as parameters

C-function for traversing files/directories effectively and calling a given function with each encountered file and a void-pointer as parameters

null 1 Jun 27, 2022
The goal of this project is to create the function get_next_line.c which

The goal of this project is to create the function get_next_line.c which, when called in a loop, will then allow the available text in the file descriptor to be read one line at a time until the end of the file. The program must compile with the flag-D BUFFER_SIZE=xx which will be used as the buffer size for the read calls in get_next_line.

REDA JAANIT 4 Dec 24, 2021
Create a calculator of any kind in any language, create a pr.

calculators Create a calculator of any kind in any language, create a pr. Create a calculator of any type using the programming language of your choic

Akshay Gautam 2 Dec 1, 2021
This tool allow you to create / load / edit models used for create a cinematic in game for World of Warcraft 3.3.5 version

CameraCinematic - Discord Introduction This tool allow you to create / load / edit models used for create a cinematic in game for World of Warcraft 3.

Intemporel 9 Mar 14, 2022
Functional Programming Library for C++. Write concise and readable C++ code.

FunctionalPlus helps you write concise and readable C++ code. Table of contents Introduction Usage examples Type deduction and useful error messages T

Tobias Hermann 1.6k Jun 24, 2022
:computer: C++ Functional Terminal User Interface. :heart:

FTXUI Functional Terminal (X) User interface A simple C++ library for terminal based user interface. Demo: Feature Functional style. Inspired by [1] a

Arthur Sonzogni 3.3k Jul 3, 2022
Functional programming style pattern-matching library for C++

Mach7: Pattern Matching for C++ by Yuriy Solodkyy, Gabriel Dos Reis, Bjarne Stroustrup Abstract Pattern matching is an abstraction mechanism that can

Yuriy Solodkyy 1.2k Jun 24, 2022
[WIP] Demo of a minimal but functional Dawn-based WebGPU client and server

dawn client-server example The goal of this demo is to create a minimal but functional Dawn-based WebGPU client and server with the following traits:

Rasmus 15 Nov 20, 2021