Fahad Ur Rehman
Fahad Ur Rehman

Reputation: 93

How to put arguments in a function at run time?

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

Answers (2)

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

R Sahu
R Sahu

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

Related Questions