Options System Overview

This page gives you a practical overview of the command line options system in Erbsland Core. You will learn how to register options, how parsing works, how to structure larger option surfaces with sets and modules, and how parsed values, help output, version output, and errors are handled.

The option system is designed for small tools as well as larger command-line applications. A tiny program can register a few options directly on Options. A larger application can let each component return an OptionSet. A command-style tool can use OptionModule to model subcommands such as scan, render, or publish. All of these definitions are parsed by OptionManager and produce an OptionValues object.

How the Pieces Fit Together

At startup, an application builds an option definition tree. The root object is Options. It owns built-in -h, --help, and --version options, global option sets, and optional modules.

Parsing then follows this sequence:

  • The executable path from argv[0] is stored for usage text.

  • If modules are registered, the first command-line argument must select one of them, except for root --help and --version.

  • The active option sets are collected from the root and, if selected, the module.

  • Pre-parsing callbacks may adjust option definitions before command-line values are read.

  • Options and positional arguments are parsed, defaults are applied, required options are checked, validators run, and post-parsing callbacks are called.

  • On success, OptionValues stores the parsed values under every accepted option name and lookup alias.

Application wraps this flow for normal command-line tools. It calls registerCommandLineOptions(), parses the command line, displays help/version/error output when needed, and then calls your application main() only after parsing succeeds. Use OptionManager directly when you need to handle the result yourself.

Command Line Syntax

The parser accepts a small, predictable command-line grammar. Long option names are case-insensitive and use --name or --name=value. Short option names are case-sensitive and use -n or -n=value. Short flags may be grouped, so -abc is equivalent to -a -b -c when all three options are flags.

Values can follow an option as the next argument or be attached with =. Use the attached form for values that start with a dash, for example --count=-1. Separate values beginning with - are rejected even if they were quoted in the shell, because quoting information is not present in argv. Flags are valueless and may repeat as occurrence counters. Boolean options accept the ASCII-case-insensitive ELCL literals true, on, yes, enabled, false, off, no, and disabled. Named scalar value options with OptionFlag::AcceptAsFlag may also occur bare. The argument -- stops option parsing; all later arguments are treated as positional values. The built-in -h, --help[=<name>], and --version requests stop normal parsing before user callbacks are called. Use the attached --help=<name> form for detailed option help. Applications that need one of these names for their own protocol can disable the help or version request individually with OptionParserFlag and then register an ordinary option under the released name:

options->setParserFlag(el::OptionParserFlag::DisableVersion);
options->addOption({"--version"_el, "language-version"_el})
    .setType(el::OptionType::Text);

Applications that only want ordinary help can reject detailed targets and treat help as a pure flag:

options->setParserFlag(el::OptionParserFlag::NoHelpDetails);

Boolean options and ordinary flags are not affected by this setting.

If modules are present, no ordinary options may appear before the module name. This makes module selection unambiguous:

forest-atlas --help
forest-atlas collect --sensor north route-a
forest-atlas publish --format atlas report-a

Invoking a modular command without any arguments displays a reduced module overview before callbacks and validation. Set OptionParserFlag::ErrorOnMissingModule to report a missing-module error instead.

Using Application

Use Application for regular executables. Override initialize() for application metadata and terminal setup, override registerCommandLineOptions() to register the command line interface, and read optionValues() in main(). When the user asks for help or version information, main() is not called. When parsing fails, Application renders the diagnostic and exits with an error code.

/// Derive from `Application` when command line options belong to the executable lifecycle.
///
/// Register options in `registerCommandLineOptions()`, then read the parsed `OptionValues` in `main()`.
/// `Application` handles `--help`, `-h`, `--version`, option errors, and terminal rendering before your main function
/// is called.
class SpectrometerApp final : public el::Application {
public:
    using Application::Application;

protected:
    void initialize() override {
        info().setApplicationName("Prism Laboratory"_el);
        info().setApplicationVersion(el::Version{0, 8, 0});
        info().setAuthorName("Erbsland DEV"_el);
        info().setLicenseText("Apache-2.0"_el);
    }
    void registerCommandLineOptions(const el::OptionsPtr &options) override {
        options->setHelpTitle("Prism Laboratory"_el);
        options->setHelpDescription("Configures a short measurement with a bench spectrometer."_el);
        options->setHelpEpilog(
            "Please use the correct names from the manual and the research datasheet "
            "to select the instruments and samples with the command line options."_el);
        options->addOption({"-i"_el, "--instrument"_el, "instrument"_el})
            .setType(el::OptionType::Text)
            .setRequired()
            .setHelpDescription("Name of the instrument that will perform the measurement."_el);
        options->addOption({"-g"_el, "--gain"_el, "gain"_el})
            .setType(el::OptionType::Integer)
            .setDefaultValue(el::OptionInteger{2})
            .setHelpDescription("Detector gain between 1 and 9."_el);
        options->addOption({"-d"_el, "--dark-frame"_el, "dark-frame"_el})
            .setType(el::OptionType::Flag)
            .setHelpDescription("Captures a dark frame before measuring the sample."_el);
        options->addOption("sample"_el)
            .setRequired()
            .setHelpDescription("Sample placed in front of the instrument."_el);
    }
    [[nodiscard]] auto main() -> el::ExitCode override {
        const auto values = optionValues();
        el::io::printLine("instrument: "_el, values->getText("instrument"_el));
        el::io::printLine("sample: "_el, values->getText("sample"_el));
        el::io::printLine("gain: "_el, values->getInteger("gain"_el));
        el::io::printLine("dark frame: "_el, el::BooleanFormat::yesNo(), values->getFlag("dark-frame"_el));
        return el::ExitCode::success();
    }
};

/// This simple main method creates the application and lets it run the full lifecycle.
auto main(const int argc, char *argv[]) -> int {
    auto app = SpectrometerApp{argc, argv};
    app.enableTerminal();
    return app.run();
}

$ option/option_application --help

Configures a short measurement with a bench spectrometer.

Usage:
  option_application [options] <sample>

Options:
  -d, --dark-frame          Captures a dark frame before measuring the sample.
  -g, --gain <integer>      Detector gain between 1 and 9.
  -h, --help                Display this help.
  -i, --instrument <value>  Name of the instrument that will perform the measurement.
      <sample>              Sample placed in front of the instrument.
      --version             Display version information.

Please use the correct names from the manual and the research datasheet to select the
instruments and samples with the command line options.

$ option/option_application --version

Prism Laboratory 0.8.0
  Author:   Erbsland DEV
  License:  Apache-2.0

$ option/option_application --instrument Prisma-7 --gain 4 cristal-azul

instrument: Prisma-7
sample: cristal-azul
gain: 4
dark frame: no

Using OptionManager Directly

OptionManager is the lower-level parser and display orchestrator. It is the right tool when parsing does not belong to the global application lifecycle, when you are testing option definitions, or when your program needs custom routing for help and errors.

Call parse() when you want an OptionResult and will inspect the result status yourself. Call parseOrThrow() when errors should become OptionError exceptions. Both methods can parse already converted CommandLineArguments or raw argc /argv pairs. The converted-list overloads require mutable lvalues because sensitive option values are replaced with five stars before parsing returns. When Application owns parsing, it also masks the borrowed native argv buffers in place without changing their length.

/// Use `OptionManager` directly when option parsing is only one part of a larger startup flow.
///
/// Manual parsing returns an `OptionResult` instead of immediately exiting or throwing.
/// This is useful for libraries, test tools, embedded command interpreters, or applications that need to route help,
/// version, and error documents through their own output system.
auto createManualOptions() -> el::OptionsPtr {
    auto options = el::Options::create();
    auto info = el::ApplicationInfo{};
    info.setApplicationName("Photometry Notebook"_el);
    info.setApplicationVersion(el::Version{0, 8, 0});
    options->setApplicationInfo(info);
    options->setHelpTitle("Photometry Notebook"_el);
    options->setHelpDescription("Records a short note from a photometer measurement."_el);
    options->addOption({"-q"_el, "--quiet"_el, "quiet"_el}).setHelpDescription("Reduces progress output."_el);
    options->addOption({"-n"_el, "--note"_el, "note"_el})
        .setType(el::OptionType::Text)
        .setRequired()
        .setHelpDescription("Text of the laboratory note."_el);
    options->addOption({"-r"_el, "--repeat"_el, "repeat"_el})
        .setType(el::OptionType::Integer)
        .setDefaultValue(el::OptionInteger{1})
        .setHelpDescription("Number of times to repeat the note in the report."_el);
    return options;
}

auto manualParsing() -> el::ExitCode {
    auto manager = el::OptionManager{createManualOptions()};
    auto args = makeArgs({"fotometria"_el, "--note"_el, "Lectura estable en lámpara azul"_el, "--repeat=2"_el});
    const auto result = manager.parse(args);

    if (result.status() != el::OptionResultStatus::Success) {
        if (result.status() == el::OptionResultStatus::DisplayHelp) {
            el::io::printLine(manager.helpDocument(result.values()->moduleName()).toString());
        } else if (result.status() == el::OptionResultStatus::DisplayVersion) {
            el::io::printLine(manager.versionDocument(result.values()->moduleName()).toString());
        } else if (result.errorContext().has_value()) {
            el::io::printLine(manager.errorDocument(result.errorContext().value()).toString());
        }
        return el::ExitCode::failure();
    }

    const auto values = result.values();
    el::io::printLine("note: "_el, values->getText("note"_el));
    el::io::printLine("repetitions: "_el, values->getInteger("repeat"_el));
    el::io::printLine("quiet: "_el, el::BooleanFormat::yesNo(), values->getFlag("quiet"_el));
    return el::ExitCode::success();
}
note: Lectura estable en lámpara azul
repetitions: 2
quiet: no

Where to Go Next

The remaining topic pages focus on one concept at a time:

  • Option Definitions explains individual option definitions, names, value types, choices, defaults, validation, flags, and help visibility.

  • Option Sets explains how larger applications let individual components provide their own options and callbacks.

  • Option Modules explains command-style tools where the first argument selects a module.

  • Option Values explains the parsed value map returned by the parser.

  • Customizing Help Output explains generated help documents, terminal rendering, custom styles, and display wording.