ipcamit ipcamit - 2 months ago 5
Bash Question

How to pass integer vector from shell?

I want to pass an integer array/vector as shell argument? Below is my last attempt:

#include <iostream>
#include <vector>
#include <string>
//using namespace std;

int main(int argc, const char *argv[])
{
char dummy;
std::vector<int> argument(argc-1);
std::cout<<argc<<std::endl;

for (int i = 1; i <= argc; ++i)
{
dummy=*(*(argv+1)+i);
argument[i-1]=std::stoi(dummy);
}

std::cout<<argument.size()<<std::endl;
return 0;
}


I got error:


error: no matching function for call to ‘stoi(char&)’


Is there any simpler way to do it?
basically run program as:


./executable {1, 2, 3, 4, 5}


and intialize an array/vector in programme with those values?

Answer

Resolving the compiler error is a waste of time. OP had the right idea initially using std::string dummy;, so let's stick with that and figure out what went wrong.

First off lets assume 4 arguments: 1 12 123 1234 and examine the range of the 4 loop.

for (int i = 1; i <= argc; ++i)

Because OP starts the loop at 1, I assume they know that the first argument is the command executed. Great start.

But i <= argc will allow the i to range from 1 to 5. argv is valid from 0 to 4. That means undefined behaviour was invoked accessing argv[5] and Crom only knows what happens after that. Very likely dummy got loaded with garbage from argv[5] and std::stoi could not parse this garbage into an int. This is only speculation. Determining the behaviour of undefined behaviour is a waste of time. It might be different next time. It might even look like it worked next time.

So the first thing to do is get rid of the out of bounds read.

for (int i = 1; i < argc; ++i)

Now we have an i with a range of 1 to 4 and everything stays in bounds.

Next, we can clean up

dummy=*(*(argv+1)+i);

since we aren't tying to fit it into a char anymore. The simplest is

std::string dummy(argv[i]);

which declares string dummy and initializes it to contain one command line argument. Can this be done faster? Yeah, but this is stupidly easy to read. Start with easy and only go into the difficult if easy doesn't meet the program requirements. The only change I would make here until forced is change dummy to a more descriptive name.

Even simpler you could

argument[i-1]=std::stoi(argv[i]);

But I prefer the dummy approach at least for now. It has a major debugging advantage: You can print or use the debugger to inspect the value of dummy before std::stoi throws an exception because dummy can't be converted.

The resulting code:

int main(int argc, const char *argv[])
{
    std::vector<int> argument(argc-1);
    std::cout<<argc<<std::endl; 

    for (int i = 1; i < argc; ++i) 
    {
        std::string dummy(argv[i]);
        argument[i-1]=std::stoi(dummy);
    }

    std::cout<<argument.size()<<std::endl;
    return 0;
}

called with ./executable 1 12 123 1234 the output will be

5
4

5 input arguments and 4 items in the vector because the command is discarded.

Figuring out how to handle the curly braces in

./executable {1, 2, 3, 4, 5}

I'm going to leave to OP to figure out.

Comments