execv() and const-ness
The Open Group Base Specifications explains why this is: for compatibility with existing C code. Neither the pointers nor the string contents themselves are intended to be changed, though. Thus, in this case, you can get away with const_cast
-ing the result of c_str()
.
Quote:
The statement about
argv[]
andenvp[]
being constants is included to make explicit to future writers of language bindings that these objects are completely constant. Due to a limitation of the ISO C standard, it is not possible to state that idea in standard C. Specifying two levels ofconst
- qualification for theargv[]
andenvp[]
parameters for the exec functions may seem to be the natural choice, given that these functions do not modify either the array of pointers or the characters to which the function points, but this would disallow existing correct code. Instead, only the array of pointers is noted as constant.
The table and text after that is even more insightful. However, Stack Overflow doesn't allow tables to be inserted, so the quote above should be enough context for you to search for the right place in the linked document.
Can I pass a const char* array to execv?
Can I pass an array of const char pointers as the second argument?
Well yes, you already know that you can cast in order to do so.
From the POSIX documentation for execv (halfway through the Rationale section), it looks like the second argument is a char *const array only for backwards compatibility:
I wouldn't put it in those terms, but yes, there is a compatibility aspect to the chosen signature. The section you reference explains that C does not have a wholly satisfactory way to express the degree of const
-ness that POSIX requires execv()
to provide for the arguments. POSIX guarantees that the function will not change either the pointers in argv
or the strings to which they point.
With that being the case, I think it not unreasonable to cast the argv
pointer as you propose to do, though I would leave a comment in my code explaining why doing so is safe.
On the other hand, you should consider simply leaving the const
off of your array declaration:
char *exe_name = "echo", *message = "You ran";
char *exe_args[] = { exe_name, message, argv[0], NULL };
Or, in your simple example, even this would do:
char *exe_args[] = { "echo", message, argv[0], "You ran", NULL };
C string literals correspond to arrays of type char
, not const char
, so this is perfectly legal as far as C is concerned, even though actually trying to modify the contents of those strings might fail.
On the third hand, modern C has array literals, so you could even do this:
execv("/bin/echo", (char *[]) { "echo", "You ran ", argv[0], NULL });
In that last case you don't even have a cast (the thing that resembles one is just part of the syntax for an array literal).
Why does `execvp` take a `char *const argv[]`?
To quote the page you link:
The statement about
argv[]
andenvp[]
being constants is included to
make explicit to future writers of language bindings that these
objects are completely constant. Due to a limitation of the ISO C
standard, it is not possible to state that idea in standard C.
Specifying two levels ofconst
- qualification for theargv[]
and
envp[]
parameters for the exec functions may seem to be the natural
choice, given that these functions do not modify either the array of
pointers or the characters to which the function points, but this
would disallow existing correct code.
Basically the const
qualification on execlp
and execvp
are completely compatible in the sense that they specify identical limitations on the corresponding arguments.
Is it a good idea of maintaining const-ness as much as possible?
(1) and (3) are closely related. A by-value parameter is just a local variable with that name, as is the result of your computation.
Usually it makes little difference in short functions whether you mark local variables const
or not, since you can see their entire scope right in front of you. You can see whether or not the value changes, you don't need or want the compiler to enforce it.
Occasionally it does help, however, since it protects you from accidentally passing them to a function that takes its parameter by non-const reference, without realising that you're modifying your variable. So if you pass the variable as a function argument during its life, then marking it const
can give you more confidence that you know what value it has afterwards.
Very occasionally, marking a variable const
can help the optimizer, since you're telling it that the object is never modified, and sometimes that's true but the compiler can't otherwise prove it. But it's probably not worth doing it for that reason, because in most cases it makes no difference.
(2) is another matter. For built-in types it makes no difference, as others have explained. For class types, do not return by const value. It might seem like a good idea, in that it prevents the user writing something pointless like func_returning_a_string() += " extra text";
. But it also prevents something which is pointful -- C++11 move semantics. If foo
returns a const string, and I write std::string s = "foo"; if (condition) s = foo();
, then I get copy assignment at s = foo();
. If foo
returns a non-const string then I get move assignment.
Similarly in C++03, which doesn't have move semantics, it prevents the trick known as "swaptimization" - with a non-const return value I can write foo().swap(s);
instead of s = foo();
.
(cpp) old conversion from constant string to char *
The warning is legitimate, because assigning a "const char*" to a "char * " is dangerous. The data pointed to can be changed, but it shouldn't.
To build the argument vector using const char*, declare the array as a char const * const[]
To pass the array to execv, cast it to char**.
This version should avoid the warning:
char const * const argVec[] = {
"texworks"
, "temp.tex"
, NULL
};
execvp("texworks", (char**)argVec);
c++ fork exec from command vector
You will want to call 'execv' so that you can make a char*[]
containing the options. "ls" and "-l" each get their own slot in the array.
You'll have to cast away const, or use a char `char const*[]' array and then cast away the const on that to pass it to execv. In general, the declarations for these system calls are mildly unfriendly to C++.
See a stack overflow question on this subject.
There's a reasonable tutorial at http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html.
Roughly speaking:
char * exec_args[1024];
int arg_count = 0;
std::vector<std::string> theArgs;
exec_args[arg_count++] = "/bin/whatever"; // leave command in argv[0]
for (int x = 0; x < theArgs.size(); x++) {
exec_args[arg_count++] = strdup(theArgs[x].c_str());
}
exec_args[arg_count++] = 0; // tell it when to stop!
execv("/bin/whatever", exec_args);
Invalid conversion from 'const char**' to 'char* const*'
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[]
andenvp[]
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;
}
C++ const char* To const char* const
The problem is you cannot pass const variable to function expecting non-const argument.
other word, const char *
is a subset of char *
.
remove the const
/*const*/ char* cmd_left[v.size()+1];
add const_cast
here
cmd_left[i] = const_cast<char *>( v.at(i).c_str() );
other parts of your code look suspicious, but this will make it compile
Related Topics
C++: Pointer to Monomorphic Version of Virtual Member Function
Virtual Dispatch Implementation Details
Using Emit VS Calling a Signal as If It's a Regular Function in Qt
Sqlite3_Exec() Callback Function Clarification
Gcc Error: Explicit Specialization in Non-Namespace Scope
Using Sfinae for Template Class Specialisation
How to Tame the Windows Headers (Useful Defines)
Prolonging the Lifetime of Temporaries
C++ Objects: When Should I Use Pointer or Reference
Serialize and Send a Data Structure Using Boost
Why Use ++I Instead of I++ in Cases Where the Value Is Not Used Anywhere Else in the Statement
C++ Stl: Array VS Vector: Raw Element Accessing Performance
Where in Memory Is Vtable Stored
Type Erasure in C++: How Boost::Shared_Ptr and Boost::Function Work
Templates: Template Function Not Playing Well with Class's Template Member Function
Get a Single Line Representation for Multiple Close by Lines Clustered Together in Opencv