2

I've posted the following question on SO, but I guess boost::program_options can't help me here:

I have options --foo (short form -f) and --bar that need special treatment, they are repeatable and order should matter. So, for the following:

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

I'd like to range a key value map being able to construct [("foo", 1), ("foo", 2), ("bar", 3), ("foo", 4)].

Please notice the order of this array of tuples, it's the same as that in the command line. I've discarded non-important options in the array, but they may be present in the command line nonetheless.

It seems the sole way to allow repeatable options with boost::program_options is calling composing() for any given option, but then, since each will store all their values in a vector, I lose the order I need for interlacing options.

So, can boost::program_options help with this?

source: https://stackoverflow.com/questions/36973114/

I'm looking for a library that allows me to range through the parsed options (if possible with their names normalized to their long or short names) in the order it's given in the command line because such order is important for me.

  • I know that program_options can give you everything it didn't parse itself as a vector<string>. In fact, I use this ugly piece of code to put it into a key-value map in a project I'm working on, and it shouldn't be difficult to adapt to what you need I guess. – einpoklum May 2 '16 at 23:03
  • Ah, there it is: boost::program_options::collect_unrecognized(). Although I will say you probably shouldn't be assuming order for command-line options, i.e. the problem is more in the fact that you're trying to do this IMO. – einpoklum May 2 '16 at 23:05
  • @einpoklum no, there's no issue I think in trying to do this, the reason is that I need to do something like matrix_product_program --matrix="{...}" --matrix="{...}" --matrix="{...}". Any problem with having to fit this situation with a solution as explained? – pepper_chico May 2 '16 at 23:10
  • You do know the command-line is not the place to put input data, right? Can't you get the number of matrices from the command line, then just read them (say line-by-line) from standard input? Anyway, you got a +1 from me since something more versatile than boost::program_options is interesting. – einpoklum May 2 '16 at 23:12
  • @einpoklum also, thanks for these pointers, probably it's going to be of great help, I'll check them out. – pepper_chico May 2 '16 at 23:12
1

Using Poco, which calls a callback per flag, in order:

#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)

OUTPUT

❯❯❯ ./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
1

Using the latest master in CLI11 (will be in version 1.1) (now an official example as well):

#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;
    }
}

Output:

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

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.