cxxopts icon indicating copy to clipboard operation
cxxopts copied to clipboard

Conflicts between repeated arguments and vector arguments

Open bsergean opened this issue 4 years ago • 7 comments

Hi,

I have an argument that can be repeated multiple time (let's call it --location). So I declare it as a vector of string.

--location paris --location london

I believe my problem is when there is a comma in the location. I will butcher New Jersey and write it with a comma in it. Now my argument count will be 2, but when I cast to a vector of string the vector size is 3.

--location paris --location new,jersey

In practice we have big json blobs with lots of comma in those options.

Repro program

#include <cxxopts.hpp>

int main(int argc, char * argv[])
{
    cxxopts::Options options( "vector_test" );

    options.add_options()
        ("location", "Location", cxxopts::value<std::vector<std::string>>())
    ;

    auto result = options.parse(argc, argv);

    auto cnt = result.count("location");
    std::cerr << "result.count " << cnt << std::endl;

    if (cnt)
    {
        auto locations = result["location"].as<std::vector<std::string>>();
        std::cerr << "locations size " << locations.size() << std::endl;
    }

    return 0;
}

Results

$ vector_test --location paris --location london      
result.count 2
locations size 2
$ vector_test --location paris --location 'new,jersey'
result.count 2
locations size 3

Notice that before 2.2.0 got released with the comma arg separator, I was getting a count of 2 for both. We are using 2.1.2 right now as a workaround.

bsergean avatar Oct 16 '20 16:10 bsergean

The comma is used as a separator for writing vector arguments in one option. If comma is an acceptable character you can redefine the separator to something else by #defineing CXXOPTS_VECTOR_DELIMITER.

jarro2783 avatar Oct 18 '20 23:10 jarro2783

Ok, so if we want to disable this feature for having json or anything in one option, maybe a non-printable character would work, such a the nul character, which according to this table is equal to 0 in ascii.

~$ cat foo.cpp 
#include <string>
#include <sstream>

#ifndef CXXOPTS_VECTOR_DELIMITER
#define CXXOPTS_VECTOR_DELIMITER ','
#endif

int main()
{
    std::stringstream in("caca");
    std::string token;
    std::getline(in, token, CXXOPTS_VECTOR_DELIMITER);

    return 0;
}

But I can't make it work, when passing a macro my compiler wants a printable ascii char.

~$ g++ -DCXXOPTS_VECTOR_DELIMITER=0 foo.cpp
foo.cpp:12:5: error: no matching function for call to 'getline'
    std::getline(in, token, CXXOPTS_VECTOR_DELIMITER);
    ^~~~~~~~~~~~

Should we have an option like CXXOPTS_VECTOR_DELIMITER_NONE to disable that feature ?

    template <typename T>
    void
    parse_value(const std::string& text, std::vector<T>& value)
    {
#ifndef CXXOPTS_VECTOR_DELIMITER_NONE
      std::stringstream in(text);
      std::string token;
      while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
        T v;
        parse_value(token, v);
        value.emplace_back(std::move(v));
      }
#endif
    }

bsergean avatar Oct 18 '20 23:10 bsergean

That might be reasonable. Although what happens if you define it to '\0x00', or maybe you could use newline?

jarro2783 avatar Oct 18 '20 23:10 jarro2783

The first one does not compile, but the newline does compile.

// #define CXXOPTS_VECTOR_DELIMITER '\0x00' #define CXXOPTS_VECTOR_DELIMITER '\n'

Maybe there is even another \ thing that can be used like \a or something really 'invisible'.

On Oct 18, 2020, at 4:54 PM, jarro2783 [email protected] wrote:

That might be reasonable. Although what happens if you define it to '\0x00', or maybe you could use newline?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/jarro2783/cxxopts/issues/262#issuecomment-711442966, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC2O6UOQ5OYBHCM4VMFFPXDSLN52BANCNFSM4STRFCTQ.

bsergean avatar Oct 18 '20 23:10 bsergean

You could simply use '\0' which denotes the zero-byte, i.e. what was probably intended with '\0x00'.

This should work fine because your command line arguments usually aren't supposed to include zero bytes.

gsauthof avatar Nov 01 '20 11:11 gsauthof

That's a good tip, however I remember trying it and it lead to a compiler error or warning but I should give it another try.

I think the best way in my opinion would be to have a proper (static) function to call to do this, or doing it in the constructor of the option class, as opposed to a preprocessor macro. I hope I can find the time to make a PR for it.

On Nov 1, 2020, at 3:26 AM, Georg Sauthoff [email protected] wrote:

You could simply use '\0' which denotes the zero-byte, i.e. what was probably intended with '\0x00'.

This should work fine because your command line arguments usually aren't supposed to include zero bytes.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/jarro2783/cxxopts/issues/262#issuecomment-720073427, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC2O6UIT7QPC64TZVM5GPODSNVAU7ANCNFSM4STRFCTQ.

bsergean avatar Nov 02 '20 18:11 bsergean

Yeah I agree that it shouldn't be a preprocessor macro. If you want to do a PR for it then that would be great.

jarro2783 avatar Nov 02 '20 22:11 jarro2783