Reputation: 93
So I am using execlp in my c++ program. execlp is of the form " int execlp(const char *file, const char *arg0,...,const char *argn)" meaning that it can take arbitrary amount of arguments. I just want to know that is there a way I can put arguments inside this function at run time? Since the arguments are provided by the user, there is no way for me to know the exact number of arguments. Of course I can pick a ridiculously large number from the start but that won't be very efficient.I need a more efficient way that would allow me to put arguments at run time.
Upvotes: 3
Views: 342
Reputation: 1
I guess that you are using Linux or some other POSIX system.
You obviously need, as R.Sahu answered, to use functions like execv(3), which takes an array of arguments to execve(2) syscall. You could allocate that array in C dynamic memory with malloc(3) or friends (calloc
). If coding in C++, you would use new
.
For a useless example, here is a chunk of code executing /bin/echo
on an array of arguments 1
, 2
, .... nargs where int nargs;
is strictly positive.
Variant in C99
assert(nargs>0);
char** myargs = malloc ((nargs+2)*sizeof(char*));
if (!myargs) { perror("malloc myargs"); exit(EXIT_FAILURE); };
myargs[0] = "echo";
for (int ix=0; ix<nargs; ix++)
{ char buf[32];
snprintf(buf,sizeof(buf),"%d",ix);
myargs[ix+1] = strdup(buf);
if (!myargs[ix+1]) { perror("strdup"); exit(EXIT_FAILURE); };
}
myargs[nargs+1] = NULL;
execv("/bin/echo", myargs);
perror("exec echo failed");
exit(EXIT_FAILURE);
In C++ you would e.g. code char**myargs = new char*[nargs+2]
;
In general, you need to later free
(in C++, use delete
) heap allocated memory. Here it is not really needed, since execv
does not return. However, in other occasions (e.g. if using fork
before execv
, so the parent process is continuing and would later waitpid
), you need a loop to free
each individual element (result of strdup
), then you need to free
the entire myargs
array.
Regarding the general question of calling an arbitrary (runtime-known) function of arbitrary signature, this is not possible in plain standard C99, but you could use some libraries (with a few assembler or machine specific code inside them) like libffi
In genuine C++11 you still need the array argument to execv
to be an array of char*
. You might consider using (as an intermediate step) some std::vector<std::string>
but you'll need at least to transform it into a std::vector<char*>
then pass the data to execve
. Read about std::string (and its c_str
member function) and std::vector (and its data
member function). You could try something like:
assert (nargs>0);
std::vector<std::string> vecstr;
vecstr.resize(nargs+2);
vecstr[0] = "echo";
for (int ix=0; ix<nargs; ix++) vecstr[ix+1] = std::to_string(ix+1);
std::vector<const char*> vecargs;
vecargs.resize(nargs+2,nullptr);
std::transform(vecstr.begin(), vecargs.begin(),
[](const std::string&s) { return s.c_str(); });
vecargs[nargs+1] = nullptr;
execv("/bin/echo", vecargs.data());
throw std::runtime_error(std::string{"exec failure:"}+strerror(errno));
Notice that execv
can fail, in particular when the array of arguments is too big; usually the limit is a few hundred thousands elements, but it can be much smaller.
Upvotes: 2
Reputation: 206567
If you are not required to use execlp
, execv
or execvp
are better functions for your requirement.
From http://linux.die.net/man/3/execlp
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer.
Upvotes: 3