В начале я написал что-то вроде этого
char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", argv);
Тем не менее, GCC выскочил этот предупреждение, «C ++ запрещает преобразование строковой константы в char*
«.
Затем я изменил свой код в
const char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", argv);
В результате GCC выскочил этот ошибка, «неверное преобразование из const char**
в char* const*
«.
Затем я изменил свой код в
const char* argv[] = { "ls", "-al", ..., (char*)NULL };
execvp("ls", (char* const*)argv);
Наконец-то это работает и компилируется без каких-либо предупреждений и ошибок, но я думаю, что это немного громоздко, и я не могу найти, чтобы кто-нибудь написал что-то подобное в Интернете.
Есть ли лучший способ использовать execvp
в С ++?
Вы столкнулись с реальной проблемой, потому что мы столкнулись с двумя несовместимыми ограничениями:
Один из стандарта C ++, требующий от вас использования const char*
:
В C строковые литералы имеют тип
char[]
и может быть назначен напрямую
к (неконстантному)char*
, В C ++ 03 это тоже разрешено (но не рекомендуется,
как литералы const в C ++). C ++ 11 больше не позволяет такие назначения
без актерского состава.
Другой из старого прототипа функции C, который требует массив (неконстантный) char*
:
int execv(const char *path, char *const argv[]);
Таким образом, должно быть бросать где-то и единственное решение, которое я нашел, это обернуть execvp
функция.
Вот полная демонстрация C ++ этого решения. Неудобство состоит в том, что у вас есть некоторый клейкий код, чтобы написать один раз, но преимущество в том, что вы получаете безопаснее и чище C ++ 11 код (финал nullptr
проверено).
#include <cassert>
#include <unistd.h>
template <std::size_t N>
int execvp(const char* file, const char* const (&argv)[N])
{
assert((N > 0) && (argv[N - 1] == nullptr));
return execvp(file, const_cast<char* const*>(argv));
}
int main()
{
const char* const argv[] = {"-al", nullptr};
execvp("ls", argv);
}
Вы можете скомпилировать это демо с:
g++ -std=c++11 demo.cpp
Вы можете увидеть похожий подход в CPP Справочный пример для std::experimental::to_array
.
Это конфликт между декларацией execvp()
(который не может обещать не изменять свои аргументы для обратной совместимости) и интерпретация строковых литералов в C ++ как массивов констант char
,
Если приведение касается вас, оставшийся вариант — скопировать список аргументов, например так:
#include <unistd.h>
#include <cstring>
#include <memory>
int execvp(const char *file, const char *const argv[])
{
std::size_t argc = 0;
std::size_t len = 0;
/* measure the inputs */
for (auto *p = argv; *p; ++p) {
++argc;
len += std::strlen(*p) + 1;
}
/* allocate copies */
auto const arg_string = std::make_unique<char[]>(len);
auto const args = std::make_unique<char*[]>(argc+1);
/* copy the inputs */
len = 0; // re-use for position in arg_string
for (auto i = 0u; i < argc; ++i) {
len += std::strlen(args[i] = std::strcpy(&arg_string[len], argv[i]))
+ 1; /* advance to one AFTER the nul */
}
args[argc] = nullptr;
return execvp(file, args.get());
}
(Вы можете рассмотреть std::unique_ptr
быть излишним, но эта функция делает правильно очистить, если execvp()
не удается, и функция возвращает).
Демо-версия:
int main()
{
const char *argv[] = { "printf", "%s\n", "one", "two", "three", nullptr };
return execvp("printf", argv);
}
one
two
three
execvpe
требует char *const argv[]
как это второй аргумент. То есть требуется список константных указателей на неконстантные данные. Строковые литералы в C — это постоянная проблема с предупреждениями и приведением argv
в char* const*
взломать как execvp
теперь разрешено писать в строки в вашем argv
, Решение, которое я вижу, состоит в том, чтобы либо выделить буфер записи для каждого элемента, либо просто использовать execlp
вместо этого, который работает с const char*
args и позволяет передавать строковые литералы.