I'm doing a school assignment where I have to call execvp, whose method signature is the following
execvp(const char* file, char* const argv[])
However my data is in the form:
std::vector<std::string*>
I've been trying to convert said vector into the right format for the second format of execvp(), but inevitably get the following error:
command.cc:120:29: error: invalid conversion from ‘const char**’ to ‘char* const*’ [-fpermissive]
execvp(args[0], argv);
I've tried different variations but they all lead to this error. This error confuses me, since I have no idea what it means by const*. How can you have a const*? I'd consider changing the std::vector to some other type, but this is an assignment and I'm not really allowed to change it. Below is the code I use to try and create a char*[] from the vector:
const size_t numArgs = _simpleCommands[i]->_arguments.size();
std::vector<const char*> args;
for(size_t j = 0; j < numArgs; ++j)
{
args.push_back(strPtrToCharPtr(_simpleCommands[i]->_arguments[i]));
}
const char** argv = new const char*[numArgs];
for(size_t j = 0; j < numArgs; ++j)
{
argv[j] = args[j];
}
execvp(args[0], argv);
The char* const argv[] prototype means that argv is (the address of) an array of pointers to char, that the pointers in the array cannot be modified, but that the strings they point to can be. This is different from a char const **, which is a pointer to a pointer to char whose characters cannot be modified. Since passing it to a function that might modify the strings in the array would violate the const qualifier of const char **, it is not allowed. (You could do it with const_cast, but that would be solving the wrong problem.)
Since execvp() is a very old UNIX function and would not have the same interface today, it doesn’t have any parameter to tell the OS how many arguments there are, nor does it promise not to modify the contents of the strings in the array. You terminate the array by setting the final element to NULL.
It’s a similar format to the argv parameter of main(). In fact, it becomes the argv parameter of the main() function of the program you run, if it was written in C.
This isn’t a complete solution, since this is a homework assignment and you want to solve it on your own, but you have to create that array yourself. You can do this by creating a std::vector<char *> argv( args.size() + 1 ), setting each element but the last to the .data() pointer from the corresponding element of args, and setting the last element to NULL. Then, pass argv.data() to execvp().
Note that the POSIX.1-2008 standard says,
The argv[] and envp[] arrays of pointers and the strings to which those arrays point shall not be modified by a call to one of the exec functions, except as a consequence of replacing the process image.
Therefore, you ought to be able to get away with casting away the const-ness of the strings in the array, this once, if you don’t mind living dangerously. Normally, you would need to make a modifiable copy of each constant string in the array.
Update
Enough time has passed that I’m not giving out answers to homework. A commenter claimed that my answer did not work on g++8, which means that they didn’t implement the same algorithm I was thinking of. Therefore, posting the complete solution will be helpful.
This actually solves the closely-related problem of how to convert a std::vector<std::string> for use with execvp(). (A std::vector<std::string*> is basically never correct, and certainly not here. If you really, truly want one, change the type of s in the for loop and dereference.)
#define _XOPEN_SOURCE 700
// The next three lines are defensive coding:
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_VERSION 700
#define _XOPEN_UNIX 1
#include <errno.h>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <vector>
int main()
{
const std::vector<std::string> cmdline{ "ls", "-al" };
std::vector<const char*> argv;
for ( const auto& s : cmdline ) {
argv.push_back( s.data() );
}
argv.push_back(NULL);
argv.shrink_to_fit();
errno = 0;
/* Casting away the const qualifier on the argument list to execvp() is safe
* because POSIX specifies: "The argv[] [...] arrays of pointers and the
* strings to which those arrays point shall not be modified by a call to
* one of the exec functions[.]"
*/
execvp( "/bin/ls", const_cast<char* const *>(argv.data()) );
// If this line is reached, execvp() failed.
perror("Error executing /bin/ls");
return EXIT_FAILURE;
}
Another twist on this would be to write a conversion function that returns the std::vector<const char*> containing the command-line arguments. This is equally efficient, thanks to guaranteed copy elision. I normally like to code using RIIA and static single assignments, so I find it more elegant to return an object whose lifetime is managed automatically. In this case, the elements of argv are weak references to the strings in cmdline, so cmdline must outlive argv. Because we used C-style pointers as weak references, RIIA does not quite work here and we still need to pay attention to object lifetimes.
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_VERSION 700
#define _XOPEN_UNIX 1
#include <errno.h>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <vector>
std::vector<const char*> make_argv( std::vector<std::string>const& in )
{
std::vector<const char*> out;
out.reserve( in.size() + 1 );
for ( const auto& s : in ) {
out.push_back( s.data() );
}
out.push_back(NULL);
out.shrink_to_fit();
return out; // Benefits from guaranteed copy elision.
}
int main()
{
const std::vector<std::string> cmdline{ "ls", "-al" };
errno = 0;
/* Casting away the const qualifier on the argument list to execvp() is safe
* because POSIX specifies: "The argv[] [...] arrays of pointers and the
* strings to which those arrays point shall not be modified by a call to
* one of the exec functions[.]"
*/
execvp( "/bin/ls", const_cast<char* const *>(make_argv(cmdline).data()) );
// If this line is reached, execvp() failed.
perror("Error executing /bin/ls");
return EXIT_FAILURE;
}
I don't understand the std::vector<std::string *> part (are you sure you don't need a std::vector<std::string>?), anyway...
Rule for const: it's applied to the element on the left; if there is non element on the left, it's applied to the element on the right.
So a const char** (or char const **, if you prefer) is a pointer to a pointer to a constant char. I mean: the constant part is the char pointed, not the pointers.
And char * const * is a pointer to a constant pointer to a char; in this case the constant part is one of the two pointers, not the char pointed.
In your case the function
execvp(const char* file, char* const argv[])
expect, as second parameter, a char * const argv[] (a C-style array of constant pointers to a char) that you can see as a char * const *.
But you call
execvp(args[0], argv);
where argv is a char const **, that is different to a char * const *.
So the error: the function expect to be able to modify the pointed char's and you pass a pointer to a pointer to not modifiable char's
And you can't define argv as a char * const *
char * cont * argv = new char * const [numArgs]; // <-- WRONG
because you can't modify it.
So, to solve the problem, I suppose you can define argv as a char **
char** argv = new char* [numArgs];
for(size_t j = 0; j < numArgs; ++j)
argv[j] = args[j];
execvp(args[0], argv);
There ins't problem if you pass a not-constant object to a function that require a constant one (the contrary can be a problem), so you can pass a char ** to a function that expect a char * const *.
1) You don't need to have const * (const pointer) because pointer is automatically converted to const pointer if needed;
2) But you do need to supply char* (not const char* !) array as a second argument of execvp, i.e. your string characters should be modifiable. By having such a signature, execvp reserve its right to modify the supplied argument strings (yes it seems strange - but a process does have right to change its argument - note that main() routine may have (non-const) char** argv arguments!). Thus, you need to get rid of const char* in your piece of code and replace them by char *
Related
When I try to compile this code, an error appears :
#include<iostream>
using namespace std;
int main()
{
char* p = "Hello";
return 0;
}
error C2440: 'initializing': cannot convert from 'const char [6]' to 'char *'
This error is fixed when I add the word const in the declaration of p.
This code compiles and runs:
#include<iostream>
using namespace std;
int main()
{
const char* p = "Hello";
return 0;
}
So my question is : How is the main() function able to take char *argv[] (as a parameter) and not const char *argv[] (as a parameter)?
int main (int argc, char *argv[])
"Hello" is a string literal, which has a type of const char[6]. You can't set a non-const char* pointer to point at a const char[], hence the error. Doing so would grant the caller access to mutate read-only data.
The char* pointers in the argv[] parameter of main() are pointing at char[] arrays which are allocated and filled dynamically at runtime, when the program's startup code parses the calling process's command line parameters before calling main(). They are not pointing at string literals, and thus do not need to be const char*.
TL;DR: You can actually mutate argv, string literals are immutable in c++.
eg:
#include <iostream>
int main(int, char** argv) {
char* p = new char[3];
p[0] = 'H'; p[1] = 'i'; p[2] = 0;
argv[0][0] = ':';
argv[0][1] = 'P';
argv[0][2] = '\n';
std::cout << p << argv[0];
delete[] p;
}
Same code on Compiler Explorer
This is, as far as I know, valid c++, with well-defined behavior.
char* x = "An immutable char const[]."; is not.
You can probably cast const away with const_cast.
But any attempt to modify the string pointed to by x would cause undefined behavior.
Let's see what is happening in your example on case by case basis:
Case 1
Here we consider the statement:
char* p = "Hello";
On the right hand side of the above statement, we've the string literal "Hello" which is of type const char[6]. There are two ways to understand why the above statement didn't work.
In some contexts, const char[6] decays to a const char* due to type decay. This basically means that on the right hand side we will have a const char* while on the left hand side we have a char*. Note also that this means that on the right hand side we've a low-level const but on the left hand side we don't have any low-level const. So, the given statement won't work. For the statement to work we've to make sure that the left hand side should've either same or greater low-level const qualifier than the right hand side.
A few example would illustrate the point:
int arr1[] = {1,2,3};
int* ptr1 = arr1; //this works because arr1 decays to int* and both sides have no low level const
const int arr2[] = {1,2,3};
int* ptr2 = arr2; //won't work, right hand side will have a low level const(as arr2 decays to const char*) while the left hand side will not have a low level const
const int* ptr3 = arr2; //this works, both side will have a low level const
The second way(which is basically equivalent to the 1st) to understand this is that since "Hello" is of type const char[6], so if we are allowed to write char* p = "Hello"; then that would mean that we're allowed to change the elements of the array. But note that the type const char[6] means that the char elements inside the array are immutable(or non-changable). Thus, allowing char* p = "Hello"; would allow changing const marked data, which should not happen(since the data was not supposed to change as it was marked const). So to prevent this from happening we have to use const char* p = "Hello"; so that the pointer p is not allowed to change the const marked data.
Case 2
Here we consider the declaration:
int main (int argc, char *argv[])
In the above declaration, the type of the second parameter named argv is actually a char**. That is, argv is a pointer to a pointer to a char. This is because a char* [] decays to a char** due to type decay. For example, the below given declarations are equivalent:
int main (int argc, char *argv[]); //first declaration
int main (int argc, char **argv); //RE-DECLARATION. Equivalent to the above declaration
In other words, argv is a pointer that points to the first element of an array with elements of type char*. Moreover, each elements argv[i] of the array(with elements of type char*) itself point to a character which is the start of a null terminated character string. That is, each element argv[i] points to the first element of an array with elements of type char(and not const char). Thus, there is no need for const char*. A diagram is given for illustration purposes:
I have a bluez header file get_opt.h where argv is an argument:
extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv,.....
which requires char* const* for argv. Because the unit is called from a different file I was trying to emulate argv but whatever permutation of declaring char, const and * is used I get unqualified-id or the compiler moans about not converting to char* const*. I can get const char * easily, of course. char const * argv = "0"; compiles OK in Netbeans with C++14 but
char * const * argv = "0";
produces
"error: cannot convert 'const char *' to 'char * const *'" in initialisation (sic).
How do you declare char* const* or am I mixing C with C++ so I'm up the wrong tree?
Let's review the pointer declarations:
char * -- mutable pointer to mutable data.
char const * -- mutable pointer to constant data.
char * const -- constant pointer to mutable data.
char const * const -- constant pointer to constant data.
Read pointer declarations from right to left.
Looking at the descriptions, which kind of pointer do you want?
Because void main(void) is in a different file I was trying to emulate
argv
void main(void) is not the correct main function declaration. The main function has to have int return value.
Simply change the void main(void) to int main(int argc, char **argv) in that "other" file.
It does not require char const * to be passed to. Do not change the type of the main function arguments.
int getopt_long (int ___argc, char * const *___argv);
int main(int argc, char **argv)
{
printf("%d\n", getopt_long(argc, argv));
}
Both language compile fine:
https://godbolt.org/z/5To5T931j
Credit #JaMiT above
[I don't know how to accept a commment]
Also, you might want to notice how you can remove the call to getopt_long() from that example while retaining the error (it's motivation for your declaration, but not the source of your issue). If you think about your presentation enough, you might realize that your question is not how to declare a char* const* but how to initialize it. Due to arrays decaying to a pointer, you could declare an array of pointers (char* const argv[]) instead of a pointer to pointer (char* const * argv). See The 1D array of pointers way portion of A: 2D Pointer initialization for how to initialize such a beast. Beware: given your setup, the "2D array way" from that answer is not an option. Reminder: The last pointer in the argument vector is null; argv[argc] == nullptr when those values are provided by the system to main().
At the beginning, I wrote something like this
char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", argv);
However, GCC popped up this warning, "C++ forbids converting a string constant to char*."
Then, I changed my code into
const char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", argv);
As a result, GCC popped up this error, "invalid conversion from const char** to char* const*."
Then, I changed my code into
const char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", (char* const*)argv);
It finally works and is compiled without any warning and error, but I think this is a bit cumbersome, and I cannot find anyone wrote something like this on the Internet.
Is there any better way to use execvp in C++?
You hit a real problem because we are facing two incompatible constraints:
One from the C++ standard requiring you that you must use const char*:
In C, string literals are of type char[], and can be assigned directly
to a (non-const) char*. C++03 allowed it as well (but deprecated it,
as literals are const in C++). C++11 no longer allows such assignments
without a cast.
The other from the legacy C function prototype that requires an array of (non-const) char*:
int execv(const char *path, char *const argv[]);
By consequence there must be a const_cast<> somewhere and the only solution I found is to wrap the execvp function.
Here is a complete running C++ demonstration of this solution. The inconvenience is that you have some glue code to write once, but the advantage is that you get a safer and cleaner C++11 code (the final nullptr is checked).
#include <cassert>
#include <unistd.h>
template <std::size_t N>
int execvp(const char* file, const char* const (&argv)[N])
{
assert((N > 0) && (argv[N - 1] == nullptr));
return execvp(file, const_cast<char* const*>(argv));
}
int main()
{
const char* const argv[] = {"-al", nullptr};
execvp("ls", argv);
}
You can compile this demo with:
g++ -std=c++11 demo.cpp
You can see a similar approach in the CPP Reference example for std::experimental::to_array.
This is a conflict between the declaration of execvp() (which can't promise not to modify its arguments, for backwards compatibility) and the C++ interpretation of string literals as arrays of constant char.
If the cast concerns you, your remaining option is to copy the argument list, like this:
#include <unistd.h>
#include <cstring>
#include <memory>
int execvp(const char *file, const char *const argv[])
{
std::size_t argc = 0;
std::size_t len = 0;
/* measure the inputs */
for (auto *p = argv; *p; ++p) {
++argc;
len += std::strlen(*p) + 1;
}
/* allocate copies */
auto const arg_string = std::make_unique<char[]>(len);
auto const args = std::make_unique<char*[]>(argc+1);
/* copy the inputs */
len = 0; // re-use for position in arg_string
for (auto i = 0u; i < argc; ++i) {
len += std::strlen(args[i] = std::strcpy(&arg_string[len], argv[i]))
+ 1; /* advance to one AFTER the nul */
}
args[argc] = nullptr;
return execvp(file, args.get());
}
(You may consider std::unique_ptr to be overkill, but this function does correctly clean up if execvp() fails, and the function returns).
Demo:
int main()
{
const char *argv[] = { "printf", "%s\n", "one", "two", "three", nullptr };
return execvp("printf", argv);
}
one
two
three
execvpe requires an char *const argv[] as it's second argument. That is, it requires a list of const pointers to non-const data. String literals in C are const hence the problem with warnings and casting your argv to char* const* is a hack as execvp is now allowed to write to the strings in your argv. Te solution I see is to either allocate a writeble buffer for each item or just use execlp instead which works with const char* args and allows for passing string literals.
I'm very new to C++. I'm trying to call a function that takes in char**:
bool func(char** a) {
//blablabla
}
So it takes in an array of c-strings. I need to create a char**, but nothing works.
char** a = char[255][255]; // error: type name is not allowed
char** a = new char[255][255]; // error: a value of type "char (*)[255]" cannot be used to initialize an entity of type "char **"
char a[][] = {"banana", "apple"};
char** b = &a; // error: a value of type "<error-type> (*)[2]" cannot be used to initialize an entity of type "char **"
At the end I need to do:
char* a[] = {"banana", "apple"};
Why the first few didn't work and why the last one worked?
Thanks in advance.
There's a lot wrong in your code.
char** a = char[255][255]; // error: type name is not allowed
First of all this is not even valid C++ (or C for that matter). Maybe you meant:
char a[255][255];
In any case always remember that the type of a bi-dimensional dynamically allocated array is not ** but (*)[N] which is very different.
char** a = new char[255][255]; // error: a value of type "char (*)[255]" cannot be used to initialize an entity of type "char **"
The error message you provide in the comment explains exactly what I said earlier.
char a[][] = {"banana", "apple"};
In the above code the correct type of the variable a should be char* a[]. Again, arrays and pointer (for what the type is concerned) are very different things. A char array may decay to pointer (if NULL terminated), but for the rest, except with explicit casts, you can't use pointers and arrays like you are doing.
The last one worked because, like I said earlier, char* [] is the correct type for an array of C-strings.
Anyway, if you just doing homework, it is ok to learn this things. But in future development using C++: try not to use "features" that start with C-, like C-strings, C-arrays, etc. C++'s standard library gives you std::string, std::array, std::vector and such for free.
If you really need to allocate dynamic memory (with new and delete, or new[] and delete[]) please use smart pointers, like std::shared_ptr or std::unique_ptr.
You say you are working in C++. Then you can easily ignore const char* and char** and focus about what you can use:
#include <string>
#include <vector>
std::vector<std::string> arrayOfStrings;
arrayOfStrings.push_back("foo");
bool func(const std::vector<std::string>>& a) {
..
}
If you know the size at compile time you can even use std::array:
std::array<255, std::string> fixedArrayOfStrings
EDIT: since you need to build an array of C strings in any case you can easily do it starting from the vector:
const char **arrayOfCstrings = new const char*[vector.size()];
for (int i = 0; i < vector.size(); ++i)
arrayOfCstrings[i] = vector[i].c_str();
func(arrayOfCstrings);
delete [] arrayOfCstrings;
char**
is ambiguous - it can mean:
pointer to pointer
array of c-strings - experienced programmer would write char* arr[] instead
In the first case it is quite simple:
char* niceString = GetNiceString();
func(&niceString);
however in the second case it is slightly more complex. The function will not know the length of the array so you need to end it explicitly with a NULL, just like for example environ is:
char* a[3] = { "One", "Two", NULL }; /* note that this is possibly dangerous
because you assign const char* (READ-ONLY) to char* (WRITABLE) */
func(a); // char*[] gets downgraded to char** implicitly
so char** a = char[255][255]; it's weird to C and C++
and if you want a static 2d array just
char a[255][255];
char ** is a scalar type, you have to cast to (char *[]), try this :
char **temp = (char *[]){"abc", "def","fg"};
I have found that the easiest way to build my program argument list is as a vector of strings. However, execv expects an array of chars for the second argument. What's the easiest way to get it to accept of vector of strings?
execv() accepts only an array of string pointers. There is no way to get it to accept anything else. It is a standard interface, callable from every hosted language, not just C++.
I have tested compiling this:
std::vector<string> vector;
const char *programname = "abc";
const char **argv = new const char* [vector.size()+2]; // extra room for program name and sentinel
argv [0] = programname; // by convention, argv[0] is program name
for (int j = 0; j < vector.size()+1; ++j) // copy args
argv [j+1] = vector[j] .c_str();
argv [vector.size()+1] = NULL; // end of arguments sentinel is NULL
execv (programname, (char **)argv);
The prototype for execv is:
int execv(const char *path, char *const argv[]);
That means the argument list is an array of pointers to null-terminated c strings.
You have vector<string>. Find out the size of that vector and make an array of pointers to char. Then loop through the vector and for each string in the vector set the corresponding element of the array to point to it.
I stumbled over the same problem a while ago.
I ended up building the argument list in a std::basic_string<char const*>. Then I called the c_str() method and did a const_cast<char* const*> on the result to obtain the list in a format that execv accepts.
For composed arguments, I newed strings (ordinary strings made of ordinary chars ;) ), took their c_str() and let them leak.
The const_cast is necessary to remove an additional const as the c_str() method of the given string type returns a char const* const* iirc. Typing this, I think I could have used std::basic_string<char*> but I guess I had a reason...
I am well aware that the const-casting and memory leaking looks a bit rude and is indeed bad practise, but since execv replaces the whole process it won't matter anyway.
Yes, it can be done pretty cleanly by taking advantage of the internal array that vectors use. Best to not use C++ strings in the vector, and const_cast string literals and string.c_str()'s to char*.
This will work, since the standard guarantees its elements are stored contiguously (see https://stackoverflow.com/a/2923290/383983)
#include <unistd.h>
#include <vector>
using std::vector;
int main() {
vector<const char*> command;
// do a push_back for the command, then each of the arguments
command.push_back("echo");
command.push_back("testing");
command.push_back("1");
command.push_back("2");
command.push_back("3");
// push NULL to the end of the vector (execvp expects NULL as last element)
command.push_back(NULL);
// pass the vector's internal array to execvp
execvp(command[0], const_cast<char* const*>(command.data()));
return 1;
}
Code adapted from: How to pass a vector to execvp
Do a const_cast to avoid the "deprecated conversion from string constant to 'char*'". String literals are implemented as const char* in C++. const_cast is the safest form of cast here, as it only removes the const and does not do any other funny business. execvp() will not edit the values anyway.
If you want to avoid all casts, you have to complicate this code by copying all the values to char* types not really worth it.
Although if the number of arguments you want to pass to execv/execl is known, it's easier to write this in C.
You can't change the how execv works (not easily anyway), but you could overload the function name with one that works the way you want it to:
int execv(const string& path, const vector<string>& argv) {
vector<const char*> av;
for (const string& a : argv) {
av.push_back(a.c_str());
av.push_back(0);
return execv(path.c_str(), &av[0]);
}
Of course, this may cause some confusion. You would be better off giving it a name other than execv().
NB: I just typed this in off the top of my head. It may not work. It may not even compile ;-)