How to append a value to the array of command line arguments?
Asked Answered
A

4

6

My application has entry point

int main(int argc, char *argv[])
{

}

I need to extend *argv array to n+1 and append a value. For example, I need to append "-app_ver".

I'm a newcomer in C++ (with Java background). I know that I can't change array size, so I need any solution (any approach copying array, etc.)

Anglicism answered 12/4, 2017 at 14:20 Comment(5)
Why would you want to do it?Ronni
Have you considered pushing all the values into a std::vector<std::string> or std::vector<std::string_view> and then appending your new value where you want?Sympathin
Just allocate an array of char * of size argc + 2, copy the original argc strings over from argv[], then add your extra value and a final NULL in the last two elements.Foundation
Sounds like a "XY problem", as in your solution doesn't sound like the correct one to any sensible programming problem. What is the core problem that you are actually trying to solve?Kwok
Yeah, because at first it's a bit difficult to figure out concept of arrays in c++Anglicism
P
2

Like cbuchart says, you have to create a new array or maybe a vector. Using vector and string object can be more simple than char* and array.

Exemple :

#include <vector>
#include <string>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    vector<string> list;
    for ( int i = 1 ; i < argc ; i++){
        string tmp (argv[i]);
        list.push_back(tmp); // add all arguments to the vector
    }

    cout << "number of given arguments : " << list.size() << endl;

    list.push_back("-app_ver"); // add one string to the vector

    for ( int i = 0 ; i < list.size() ; i++){
        cout << list[i] << endl; // acces data of the vector
    }

}
Pollypollyanna answered 12/4, 2017 at 14:37 Comment(1)
Right, I tried to keep with the original data types since we don't know the use. As I mentioned in another comment, some APIs, such as Qt and QCoreApplication require the char**, which cannot be obtained from the std::vector<std::string> directly. Otherwise, the one described here is a dynamic and safe solution.Parted
E
17

To duplicate your argv, you can use a std::vector of pointers to char.

std::vector<const char*> new_argv(argv, argv + argc);

Then add the new element to it:

new_argv.push_back("-app_ver");
new_argv.push_back(nullptr); // or NULL if you are using an old compiler

Then replace argv with the new array:

argv = new_argv.data(); // or &new_argv[0] if you are using an old compiler
argc = argc + 1;

Note: at the end of normal arguments, there should be a null pointer. It's rarely used (though it's required by the Standard); if you are sure your further code doesn't use it, and you only want to add one element to your argv array, you can just overwrite the null pointer, and not use any replacement for argv. That is, disregard all the above code, and just do this:

argv[argc++] = "-app_ver";

However, this is dangerous - it will crash if you ever decide to add one more element, and it might crash if some code requires the presence of a null pointer after the last argument.

Extremity answered 12/4, 2017 at 14:56 Comment(1)
This is by far the most expressive and C++-idiomatic way to solve this problem. Minimal allocation that is automatically managed, and it still can be easily used with C APIs.Thumbscrew
P
2

Like cbuchart says, you have to create a new array or maybe a vector. Using vector and string object can be more simple than char* and array.

Exemple :

#include <vector>
#include <string>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    vector<string> list;
    for ( int i = 1 ; i < argc ; i++){
        string tmp (argv[i]);
        list.push_back(tmp); // add all arguments to the vector
    }

    cout << "number of given arguments : " << list.size() << endl;

    list.push_back("-app_ver"); // add one string to the vector

    for ( int i = 0 ; i < list.size() ; i++){
        cout << list[i] << endl; // acces data of the vector
    }

}
Pollypollyanna answered 12/4, 2017 at 14:37 Comment(1)
Right, I tried to keep with the original data types since we don't know the use. As I mentioned in another comment, some APIs, such as Qt and QCoreApplication require the char**, which cannot be obtained from the std::vector<std::string> directly. Otherwise, the one described here is a dynamic and safe solution.Parted
E
0

The argv is fixed-size block of memory. You have to copy whole content (all the pointers) to bigger array in order to extend it. Or just use std::vector<std::string> args; and manipulate this variable as it will do it for you.

But to be honest - redesigning your solution could be a good idea - the copying operation might not be necessary. Maybe something like std::vector<std::string> additional_parameters; will meet your needs?

Erratum answered 12/4, 2017 at 14:30 Comment(1)
It depends on the use of the program parameters. The use of std::vector<std::string> simplifies the copy but some API's require the char** (such as the QCoreApplication in Qt), and which cannot be directly obtained from the vector. If the original char** is not required, yours is a nice approach.Parted
P
0

You must construct a new array. Here an example:

int new_argc = argc + 2;
char** new_argv = new char*[new_argc];
new_argv[argc + 1] = nullptr;
for (int ii = 0; ii < argc; ++ii) {
  new_argv[ii] = argv[ii];
}
new_argv[argc] = new char[strlen("-app_ver") + 1]; // extra char for null-terminated string
strcpy(new_argv[argc], "-app_ver");

// use new_argc and new_argv

delete[] new_argv[argc];
delete[] new_argv; 

See that I've allocated only the new parameters for the sake of optimization. To create a full copy you'll have to allocate the memory for the previously existing parameters.

Just be careful with those manual memory allocations: delete them when finishing. You only have to delete the ones you've created.

On the other hand, as argv and argc are usually used in the main function it is not a big deal (the memory-leak).

Remarks

As I don't know the future use of these new command line arguments, this solution tries to keep with the original data types of the main function. If it is passed to Boost or Qt, then the char** cannot be obtained from more direct options such as when using std::vector<std::string>. To start with, std::string::c_str() returns a const char*, and those pointers are not consecutive in memory.

A safer option (no manual deletes) can be:

int new_argc = argc + 1;
std::vector<std::unique_ptr<char>> new_argv_aux(new_argc); // automatically destroyed
for (int ii = 0; ii < argc; ++ii) {
  new_argv_aux[ii].reset(new char[strlen(argv[ii]) + 1]);
  strcpy(new_argv_aux[ii].get(), argv[ii]);
}
new_argv_aux[argc].reset(new char[strlen("-app_ver") + 1]);
strcpy(new_argv_aux[argc].get(), "-app_ver");

// Now the actual double pointer is extracted from here
std::vector<char*> new_argv(new_argv_aux.size());
for (size_t ii = 0; ii < new_argv_aux.size(); ++ii) {
  new_argv[ii] = new_argv_aux[ii].get(); // soft-reference
} // last element is null from std::unique_ptr constructor

use_here(new_argc, new_argv.data());
Parted answered 12/4, 2017 at 14:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.