альтернатива boost::program_options (парсер командной строки), которая может обрабатывать аргументы по порядку

Я разместил следующий вопрос на SO, но, думаю boost::program_options, не могу мне здесь помочь:

У меня есть варианты --foo(краткая форма -f) --bar, которые требуют особого отношения, они повторяемы, и порядок должен иметь значение. Итак, для следующего:

program --foo 1 --z -f 2 --bar 3 --x --foo 4

Я хотел бы составить карту ключевого значения, которая может построить [("foo", 1), ("foo", 2), ("bar", 3), ("foo", 4)].

Обратите внимание на порядок этого массива кортежей, он такой же, как и в командной строке. Я отбросил неважные параметры в массиве, но тем не менее они могут присутствовать в командной строке.

Кажется, единственный способ разрешить повторяющиеся параметры с помощью boost::program_optionsвызова composing()любого заданного параметра, но затем, поскольку каждый из них будет хранить все свои значения в векторе, я теряю порядок, который мне нужен для чередования параметров.

Так может boost::program_optionsпомочь с этим?

источник: https://stackoverflow.com/questions/36973114/

Я ищу библиотеку, которая позволяет мне перемещаться по проанализированным параметрам (если возможно, с их именами, нормализованными к их длинным или коротким именам) в порядке, указанном в командной строке, потому что такой порядок важен для меня.

Я знаю, что это program_optionsможет дать вам все, что не было проанализировано как файл vector<string>. На самом деле, я использую этот уродливый фрагмент кода , чтобы поместить его в карту ключ-значение в проекте, над которым я работаю, и, я думаю, нетрудно адаптировать его к тому, что вам нужно.
А, вот оно: boost::program_options::collect_unrecognized(). Хотя я скажу, что вам, вероятно, не следует предполагать порядок параметров командной строки, т.е. проблема больше в том, что вы пытаетесь сделать это IMO.
@einpoklum нет, я думаю, что в попытке сделать это нет проблем, причина в том, что мне нужно сделать что-то вроде matrix_product_program --matrix="{...}" --matrix="{...}" --matrix="{...}". Есть ли проблемы с необходимостью подогнать эту ситуацию под решение, как объяснено?
Вы ведь знаете, что командная строка — не место для ввода входных данных, верно? Разве вы не можете получить количество матриц из командной строки, а затем просто прочитать их (скажем, построчно) из стандартного ввода? В любом случае, вы получили от меня +1, так как что-то более универсальное, чем boost::program_optionsинтересное.
@einpoklum также, спасибо за эти указатели, возможно, это будет очень полезно, я проверю их.
@einpoklum это было просто для иллюстрации, поскольку матричный продукт не коммутирует. Стандартный ввод для меня не вариант, так как на самом деле мой случай не о матрицах и сложных входных данных, а просто о простых строках.
@einpoklum хорошо, учитывая контекст этого collect_unrecognizedи фрагмент, который вы отправили .... Это выглядит не очень хорошо, как я ожидал. Обратите внимание, что я даже упоминаю о получении нормализованных (подразумевающих, что они были распознаны) имен опций в сочетании с соответствующими значениями.
Ну, я же предупреждал, что это некрасиво...

Ответы (2)

Используя последний мастер в CLI11 (будет в версии 1.1) (теперь и официальный пример):

#include <CLI/CLI.hpp>
#include <iostream>
#include <vector>
#include <tuple>
#include <algorithm>

int main(int argc, char **argv) {
    CLI::App app;

    std::vector<int> foos;
    auto foo = app.add_option("--foo,-f", foos);

    std::vector<int> bars;
    auto bar = app.add_option("--bar", bars);

    app.add_flag("--z,--x"); // Random other flags

    try {
        app.parse(argc, argv);
    } catch(const CLI::ParseError &e) {
        return app.exit(e);
    }

    // I perfer using the back and popping
    std::reverse(std::begin(foos), std::end(foos));
    std::reverse(std::begin(bars), std::end(bars));

    std::vector<std::pair<std::string, int>> keyval;
    for(auto option : app.parse_order()) {
        if(option == foo) {
            keyval.emplace_back("foo", foos.back());
            foos.pop_back();
        }
        if(option == bar) {
            keyval.emplace_back("bar", bars.back());
            bars.pop_back();
        }
    }

    // Prove the vector is correct
    for(auto &pair : keyval) {
        std::cout << pair.first << " : " << pair.second << std::endl;
    }
}

Вывод:

./examples/inter_argument_order --foo 1 --z -f 2 --bar 3 --x --foo 4
foo : 1
foo : 2
bar : 3
foo : 4

Использование Poco, который вызывает обратный вызов для каждого флага, по порядку:

#include <iostream>

#include <Poco/Util/Option.h>
#include <Poco/Util/OptionSet.h>
#include <Poco/Util/Application.h>
#include <Poco/Util/HelpFormatter.h>

using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::Application;
using Poco::Util::HelpFormatter;
using Poco::Util::OptionCallback;

struct SampleApp : Application {
    void defineOptions(OptionSet &options) {
        Application::defineOptions(options);

        options.addOption(Option("help", "h", "display help information")
                              .required(false)
                              .repeatable(false)
                              .callback(OptionCallback<SampleApp>(
                                  this, &SampleApp::handleHelp)));

        options.addOption(Option("foo", "f", "foo option")
                              .required(false)
                              .repeatable(true)
                              .argument("<foo>")
                              .callback(OptionCallback<SampleApp>(
                                  this, &SampleApp::handleOptions)));

        options.addOption(Option("bar", "b", "bar option")
                              .required(false)
                              .repeatable(false)
                              .argument("<bar>")
                              .callback(OptionCallback<SampleApp>(
                                  this, &SampleApp::handleOptions)));

        options.addOption(Option("z", "", "z option")
                              .required(false)
                              .callback(OptionCallback<SampleApp>(
                                  this, &SampleApp::handleOptions)));

        options.addOption(Option("x", "", "x option")
                              .required(false)
                              .callback(OptionCallback<SampleApp>(
                                  this, &SampleApp::handleOptions)));
    }

    void handleHelp(const std::string &, const std::string &) {
        helpRequested = true;
        displayHelp();
        stopOptionsProcessing();
    }

    void handleOptions(const std::string &name, const std::string &value) {
        std::cout << name << " " << value << std::endl;
    }

    void displayHelp() {
        HelpFormatter helpFormatter(options());
        helpFormatter.setCommand(commandName());
        helpFormatter.setUsage("<options>");
        helpFormatter.setHeader("Foo/Bar options.");
        helpFormatter.format(std::cout);
    }

    bool helpRequested = false;
};

POCO_APP_MAIN(SampleApp)

ВЫВОД

❯❯❯ ./sample --help
usage: sample <options>
Foo/Bar options.

-h, --help            display help information
-f<foo>, --foo=<foo>  foo option
-b<bar>, --bar=<bar>  bar option
--z                   z option
--x                   x option
❯❯❯ ./sample --foo 1 --z -f 2 --bar 3 --x --foo 4
foo 1
z 
foo 2
bar 3
x 
foo 4