Итак, я использую execlp в своей программе на С++. execlp имеет вид «int execlp(const char *file, const char *arg0,...,const char *argn)», что означает, что он может принимать произвольное количество аргументов. Я просто хочу знать, есть ли способ помещать аргументы в эту функцию во время выполнения? Поскольку аргументы предоставляются пользователем, я не могу узнать точное количество аргументов. Конечно, я могу с самого начала выбрать смехотворно большое число, но это будет не очень эффективно. Мне нужен более эффективный способ, который позволил бы мне задавать аргументы во время выполнения.
Как поместить аргументы в функцию во время выполнения?
Ответы (2)
Если вам не требуется использовать execlp
, execv
или execvp
, это лучшие функции для ваших требований.
Из http://linux.die.net/man/3/execlp
Функции execv(), execvp() и execvpe() предоставляют массив указателей на строки с завершающим нулем, представляющие список аргументов. доступна для новой программы. Первый аргумент по соглашению должен указывать на имя файла, связанного с исполняемым файлом. Массив указателей должен заканчиваться указателем NULL.
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
может дать сбой, в частности, когда массив аргументов слишком велик; обычно ограничение составляет несколько сотен тысяч элементов, но может быть и намного меньше.