• Uncategorized

About c++ : execv-and-const-ness

Question Detail

I often use the execv() function in C++, but if some of the arguments are in C++ strings, it annoys me that I cannot do this:

const char *args[4];

args[0] = "/usr/bin/whatever";
args[1] = filename.c_str();
args[2] = someparameter.c_str();
args[3] = 0;

execv(args[0], args);

This doesn’t compile because execv() takes char *const argv[] which is not compatible with const char *, so I have to copy my std::strings to character arrays using strdup(), which is a pain.

Does anyone know the reason for this?

Question Answer

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[] and envp[] 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 of const– qualification for the argv[] 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. 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.

const is a C++ thing – execv has taken char * arguments since before C++ existed.

You can use const_cast instead of copying, because execv doesn’t actually modify its arguments. You might consider writing a wrapper to save yourself the typing.

Actually, a bigger problem with your code is that you declared an array of characters instead of an array of strings.

Try:
const char* args[4];

This is just a situation where C / C++ style const doesn’t work very well. In reality, the kernel is not going to modify the arguments passed to exec(). It’s just going to copy them when it creates a new process. But the type system is not expressive enough to really deal with this well.

A lot of people on this page are proposing making exec take “char**” or “const char * const[]”. But neither of those actually works for your original example. “char**” means that everything is mutable (certainly not not true for the string constant “/usr/bin/whatever”). “const char *const[]” means that nothing is mutable. But then you cannot assign any values to the elements of the array, since the array itself is then const.

The best you could do is have a compile-time C constant like this:

const char * const args[] = {
  "/usr/bin/whatever",
  filename.c_str(),
  someparameter.c_str(),
  0};

This will actually work with the proposed type signature of “const char *const[]”. But what if you need a variable number of arguments? Then you can’t have a compile-time constant, but you need a mutable array. So you’re back to fudging things. That is the real reason why the type signature of exec takes “const char **” for arguments.

The issues are the same in C++, by the way. You can’t pass a std::vector < std::string > to a function that needs a std::vector < const std::string >. You have to typecast or copy the entire std::vector.

I have usually hacked this with:

#define execve xexecve
#include <...>
#include <...>
#include <...>
#undef execve

// in case of c++
extern "C" {
    int execve(const char * filename, char ** argvs, char * const * envp);
}

;/

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.