Good morning everyone! I am trying to create a fork/exec call from a parent program via a passed '-e' parameter (e.g. parent -e child key1=val1 ...). As a result I want to copy all the values after the first two in the argv array into a new array child_argv. Something like:
const char *child_argv[10]; // this is actually a global variable
static const char *sExecute;
int I = 0;
const char *Value = argv[1];
sExecute = Value;
for (i=2; i<argc; i++) {
child_argv[I] = argv[i];
I++;
}
child_argv[I] = NULL; // terminate the last array index with NULL
This way I can call the exec side via something like:
execl(sExecute, child_argv);
However I get the error message "error: cannot convert 'const char**' to 'const char*' for argument '2' to 'execl(const char*, const char*, ...)'". I have even tried to use an intermediate step:
const char *child_argv[10]; // this is actually a global variable
static const char *sExecute;
int I = 0;
const char *Value = argv[1];
sExecute = Value;
for (i=2; i<argc; i++) {
const char *Value = argv[i+1];
child_argv[I] = Value;
I++;
}
child_argv[I] = NULL; // terminate the last array index with NULL
But I can't figure this out. Any help would be greatly appreciated!
UPDATE
As was pointed out, I should be using 'execv' instead of 'execl' in this situation. Still getting errors though...
UPDATE 2
I ended up copying the array without the desired parameters of argv. See the post here to see the result How to copy portions of an array into another array
From here: http://linux.die.net/man/3/exec
I think you mean to call "execv" not "execl". Execl seems to take a variable number of arguments, expecting each const char * to be another argument, while execv takes an array of arguments.
You should be using execv. When you convert to execv, for some reason execv expects a array of non-const pointers instead of const pointers, so you either have to just cast the array to (char**) or copy the strings into (char*) pointers.
Related
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 *
I'm trying to call the method
bool someMethod(char const * begin, char const * end, size_t & count);
having only a string.
std::string sString = "test";
if (someMethod(....)) {
std::cout << "working!";
}
But I dont know how to convert the string propertly.
I tried the following:
if (someMethod(&sString[0], &sString[sString.length() - 1], sString.length()) {
std::cout << "working!";
}
But I get the following message:
error: invalid initialization of non-const reference of type ‘size_t& {aka long unsigned int&}’ from an rvalue of type ‘std::basic_string<char>::size_type {aka long unsigned int}’
Would be awesome if someone has a tipp.
Thank you!
This is a "belt and suspenders" function signature, because it asks both for the end pointer and a count of characters.
In case you are doing it as an exercise, here is what you need to know to complete it:
you get the pointer to C string inside std::string by calling c_str()
you get the count by calling size()
with the initial pointer and size in hand, you get the end pointer by adding the two together.
Note that since size reference is non-const, you need to get it into a variable before making the call:
size_t count = s.size();
const char *start = s.c_str();
someFunction(start, start+count, count);
Going from a vector of strings to a vector of char* to a char**, was working when the argument came in as char**, but the conversion seems to have a problem and I'm not able to find the difference.
Is there a better way to do this?
vector<string> args;
/* code that correctly parses args from user input */
pid_t kidpid = fork();
if (kidpid < 0)
{
perror("Internal error: cannot fork.");
return -1;
}
else if (kidpid == 0)
{
// I am the child.
vector<char*>argcs;
for(int i=1;i<args.size();i++)
{
char * temp = new char[args.at(i).length()];
for(int k=0;k<args.at(i).length();k++)
{
temp[k] = args.at(i).at(k);
}
argcs.push_back(temp);
}
char** argv = new char*[argcs.size() + 1];
for (int i = 0; i < argcs.size(); i++)
{
argv[i] = argcs[i];
}
argv[args.size()] = NULL;
execvp(program, args);
return -1;
}
First, there's no point in copying the std::strings if the next thing you are going to do is call execvp.
If the execvp succeeds, then it will never return and the entire memory image will vanish into smoke (or, more accurately, be replaced by a completely new image). In the course of constructing the new image, exec* will copy the argv array (and the environment array) into it. In any event, the std::vector and std::string destructors will never be invoked.
If, on the other hand, the execvp fails, then the argument passed into it will not have been modified. (Posix: "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.")
In either case, there was no need to copy the character strings. You can use std::string::c_str() to extract a pointer to the underlying C string (as a const char*, but see below).
Second, if you're using C++11 or more recent, std::vector conveniently comes with a data() member function which returns a pointer to the underlying storage. So if you have std::vector<char*> svec, then svec.data() will be the underlying char*[], which is what you want to pass into execvp.
So the problem reduces to creating a std::vector<char*> from a std::vector<std::string>, which is straightforward:
else if (kidpid == 0) {
// I am the child.
std::vector<char*> argc;
// const_cast is needed because execvp prototype wants an
// array of char*, not const char*.
for (auto const& a : args)
argc.emplace_back(const_cast<char*>(a.c_str()));
// NULL terminate
argc.push_back(nullptr);
// The first argument to execvp should be the same as the
// first element in argc, but we'll assume the caller knew
// what they were doing, and that program is a std::string.
execvp(program.c_str(), argc.data());
// It's not clear to me what is returning here, but
// if it is main(), you should return a small positive value
// to indicate an error
return 1;
}
I'm trying to use the function with the following declaration:
extern int stem(struct stemmer * z, char * b, int k)1
I'm trying to pass a C++ string to it, so I thought I'd use the c_str() function. It returns const char *. When I try to pass it to the stem() function, I get this error: error: invalid conversion from 'const char*' to 'char*' [-fpermissive].
How can I store the result of c_str() such that I can use it with the stem function?
Here is the code I'm running:
struct stemmer * z = create_stemmer();
char * b = s.c_str();
int res = stem(z, b, s.length()); //this doesn't work
free_stemmer(z);
return s.substr(0,res);
The problem you are having is that c_str() returns a buffer that can not be modified (const), while stem() may modify the buffer you pass in (not const). You should make a copy of the result of c_str() to get a modifiable buffer.
The page http://www.cplusplus.com/reference/string/string/c_str/ has more information on the C++ 98 and 11 versions. They suggest replacing char * b = s.c_str(); with the following:
char * b = new char [s.length()+1];
std::strcpy (b, s.c_str());
You shouldn't try to remove constness of a string returned by c_str():
char * b = s.c_str();
but you can pass an address of std::string's internal buffer directly:
int res = stem(z, static_cast<char*>(&s[0]), s.length());
If stem() is going to modify the string, then make a copy of it:
char * scpy= strdup( s.c_str()) ;
int res = stem(z, scpy, strlen( scpy));
free( scpy) ;
Use const_cast:
int res = stem(z, const_cast<char*>(s.c_str()), s.length()+1);
free_stemmer(z);
return s.substr(0,res);
Note the length+1 expression which might (or might not) be needed. C-style strings (char*) have an additional null terminator (zero byte, equivalent "\0") at the end. Your stem function may (or may not) expect a null terminator at the end of the string - try both variants.
Note also that "stem" function should not try to modify the string, otherwise bad things may happen (warning based on #David Heffernan's comment)
.c_str()
Just returns a pointer to the data, I would update the stem function to accept a 'const char*' unless you are wanting to modify the data in the string, in that case you should pass it as a new string object.
If you can't edit the stem function you can cast it:
int res = stem(z, const_cast<char*>(s.c_str()), s.length());
It's not good to do this, but nothing stops you:
#include <iostream>
#include <string>
using namespace std;
void foo(char *ch)
{
ch[0] = 'B';
}
int main()
{
string str = "helo world";
char *ch = const_cast<char *>(str.c_str());
foo(ch);
// Belo world
cout << str << endl;
return 0;
}
I am currently writing an assignment for my class that is supposed to act as a very basic shell. I am nearly finished, but I am running into an issue with execvp and my character array of parameters. Here is a light snippet of my code.
//Split the left content args
istringstream iss(left);
while(getline(iss, s, ' ')){
v.push_back(s);
}
//Get the split string and put it into array
const char* cmd_left[v.size()+1];
for(unsigned int i = 0; i < v.size(); i++){
cmd_left[i] = v.at(i).c_str();
}
cmd_left[v.size()] = 0;
v.clear();
And this is utilized by...
execvp(cmd_left[0], cmd_left);
My error is
assign3.cxx:96:34: error: invalid conversion from ‘const char**’ to ‘char* const*’ [-fpermissive]
I understand that the problem is that my character array isn't full of constant data, so I need to essentially go from const char* to const char* const. I read something about const_cast, but I wasn't sure if that is what I need to be doing.
If you would be so kind, could you help me get my array of character arrays to be properly accepted by that function? If you need me to post more of my code, let me know.
Thanks
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
Without any const_cast:
istringstream iss(left);
while(getline(iss, s, ' ')){
v.push_back(s);
}
//assuming v is not empty! which you were already
string command = v[0]; //store the command in a separate variable (this makes a copy of the string)
char* cmd_left[v.size()+1]; //not a (const char)*
for(unsigned int i = 0; i < v.size(); i++){
cmd_left[i] = new char[v[i].size()+1];
strcpy(cmd_left[i], v[i].c_str()); //copy contents of each string onto a new buffer
}
cmd_left[v.size()] = NULL;
v.clear(); //if you really want to; not necessary from the code you posted
//...
execvp(command.c_str(), cmd_left);
It is not easy, sometimes not possible to create a const dynamic array of elements because all the elements have to declared within the initializer {}.
But luckily you could tell the compiler that the array you are passing is going to be const at least for the certain duration. You could do the following this would yield
&((char* const) (const_cast<char*>(cmd_left[0]) ))
The const_cast inside would remove the const-ness of the array of characters std::string is owning. So, it is quite possible that function might change the contents of array of characters behind the back of std::string. When behaviour of functions taking such argument is known then this might be ok.
If you want to create a const array of char* without resorting to const_cast or managing memory using new/delete, you could use std::vector > instead of vector of strings.
istringstream iss(left);
while(getline(iss, s, ' ')){
v.push_back(std::vector<char>(s.length()+1));
strcpy(&v.back().front(),s.c_str());
}
//Get the split string and put it into array
char* cmd_left[v.size()+1];
for(unsigned int i = 0; i < v.size(); i++){
cmd_left[i] = &v.at(i).front();
}
cmd_left[v.size()] = 0;
v.clear();
execvp(cmd_left[0], &((char* const)cmd_left[0]));
Hope this helps.