Как поместить аргументы в функцию во время выполнения?

Итак, я использую execlp в своей программе на С++. execlp имеет вид «int execlp(const char *file, const char *arg0,...,const char *argn)», что означает, что он может принимать произвольное количество аргументов. Я просто хочу знать, есть ли способ помещать аргументы в эту функцию во время выполнения? Поскольку аргументы предоставляются пользователем, я не могу узнать точное количество аргументов. Конечно, я могу с самого начала выбрать смехотворно большое число, но это будет не очень эффективно. Мне нужен более эффективный способ, который позволил бы мне задавать аргументы во время выполнения.


person Fahad Ur Rehman    schedule 03.05.2015    source источник
comment
Вам не хватает тега Linux или POSIX. Ваш вопрос, вероятно, не имеет смысла в Windows.   -  person Basile Starynkevitch    schedule 03.05.2015


Ответы (2)


Если вам не требуется использовать execlp, execv или execvp, это лучшие функции для ваших требований.

Из http://linux.die.net/man/3/execlp

Функции execv(), execvp() и execvpe() предоставляют массив указателей на строки с завершающим нулем, представляющие список аргументов. доступна для новой программы. Первый аргумент по соглашению должен указывать на имя файла, связанного с исполняемым файлом. Массив указателей должен заканчиваться указателем NULL.

person R Sahu    schedule 03.05.2015
comment
@FahadUrRehman, опубликуйте несколько примеров использования, и я могу ответить на ваш комментарий некоторыми конкретными предложениями. - person R Sahu; 03.05.2015
comment
процесс pid_t; //создать процесс process = fork(); // разветвляем if (process == 0) { execlp(/usr/bin/ls, ls, -la); // в этом случае я передаю два аргумента. Это приведет к выполнению ls -la в оболочке } - person Fahad Ur Rehman; 03.05.2015
comment
Создайте массив (возможно, с malloc) из argc+1 указателей (каждый типа char*) и заполните этот массив. - person Basile Starynkevitch; 03.05.2015

Я предполагаю, что вы используете Linux или какую-либо другую систему POSIX.

Вам, очевидно, нужно, как ответил R.Sahu, использовать такие функции, как execv(3), который принимает массив аргументов в execve(2) системный вызов. Вы можете выделить этот массив в динамической памяти C с помощью malloc(3) или друзей (calloc). Если вы кодируете на C++, вы должны использовать new.

В качестве бесполезного примера приведем фрагмент кода, выполняющего /bin/echo для массива аргументов 1, 2, .... nargs, где int nargs; — строго положительное значение.

Вариант в 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);

В С++ вы бы, например. код char**myargs = new char*[nargs+2];

В общем, вам нужно позже free (в C++ использовать delete) куча выделенной памяти. Здесь он особо и не нужен, так как execv не возвращается. Однако в других случаях (например, если используется fork перед execv, поэтому родительский процесс продолжается и позже будет waitpid), вам нужен цикл для free каждого отдельного элемента (результат strdup), тогда вам нужно free весь массив myargs.

Что касается общего вопроса о вызове произвольной (известной во время выполнения) функции с произвольной сигнатурой, это невозможно в простом стандарте C99, но вы можете использовать некоторые библиотеки (с несколькими кодами на ассемблере или машине внутри них), например libffi

В подлинном C++11 вам по-прежнему нужно, чтобы аргумент массива execv был массив char*. Вы можете подумать об использовании (в качестве промежуточного шага) какого-нибудь std::vector<std::string>, но вам нужно как минимум преобразовать его в std::vector<char*>, а затем передать данные в execve. Прочтите о std::string (и его c_str функции-члене) и std::vector (и его data функция-член). Вы можете попробовать что-то вроде:

 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));

Обратите внимание, что execv может дать сбой, в частности, когда массив аргументов слишком велик; обычно ограничение составляет несколько сотен тысяч элементов, но может быть и намного меньше.

person Basile Starynkevitch    schedule 03.05.2015