execvp не работает при преобразовании из vector‹string› в vector‹char*› в char**

Переход от вектора строк к вектору char* к char** работал, когда аргумент пришел как char**, но при преобразовании возникла проблема, и я не могу найти разницу.

Есть лучший способ сделать это?

    vector<string> args;

    /* code that correctly parses args from user input */

    pid_t kidpid = fork();
    if (kidpid < 0)
    {
        perror("Internal error: cannot fork.");
        return -1;
    }
    else if (kidpid == 0)
    {
        // I am the child.

        vector<char*>argcs;
        for(int i=1;i<args.size();i++)
        {
            char * temp = new char[args.at(i).length()];
            for(int k=0;k<args.at(i).length();k++)
            {
                temp[k] = args.at(i).at(k);
            }
            argcs.push_back(temp);
        }

        char** argv = new char*[argcs.size() + 1];
        for (int i = 0; i < argcs.size(); i++)
        {
            argv[i] = argcs[i];
        }
        argv[args.size()] = NULL;

        execvp(program, args);

        return -1;
    }

person redux    schedule 06.02.2016    source источник
comment
Позвольте нам узнать, в чем проблема, может помочь.   -  person user4581301    schedule 07.02.2016
comment
извините, пользователь, execvp не смог найти подходящий конструктор   -  person redux    schedule 07.02.2016


Ответы (1)


Во-первых, нет смысла копировать std::string, если следующее, что вы собираетесь сделать, это вызвать execvp.

Если execvp удастся, то он уже никогда не вернется и весь образ памяти испарится в дыму (точнее, заменится совершенно новым образом). В процессе построения нового образа exec* скопирует в него массив argv (и массив окружения). В любом случае деструкторы std::vector и std::string никогда не будут вызываться.

Если, с другой стороны, execvp дает сбой, то переданный в него аргумент не будет изменен. (Posix: «Массивы указателей argv[] и envp[] и строки, на которые указывают эти массивы, не должны изменяться вызовом одной из функций exec, кроме как в результате замены образа процесса».)

В любом случае не было необходимости копировать строки символов. Вы можете использовать std::string::c_str() для извлечения указателя на базовую строку C (как const char*, но см. ниже).

Во-вторых, если вы используете C++11 или более позднюю версию, std::vector удобно поставляется с функцией-членом data(), которая возвращает указатель на базовое хранилище. Итак, если у вас есть std::vector<char*> svec, то svec.data() будет базовым char*[], и это то, что вы хотите передать в execvp.

Таким образом, проблема сводится к созданию std::vector<char*> из std::vector<std::string>, что очень просто:

else if (kidpid == 0) {
    // I am the child.
    std::vector<char*> argc;
    // const_cast is needed because execvp prototype wants an
    // array of char*, not const char*.
    for (auto const& a : args)
        argc.emplace_back(const_cast<char*>(a.c_str()));
    // NULL terminate
    argc.push_back(nullptr);
    // The first argument to execvp should be the same as the
    // first element in argc, but we'll assume the caller knew
    // what they were doing, and that program is a std::string. 
    execvp(program.c_str(), argc.data());
    // It's not clear to me what is returning here, but
    // if it is main(), you should return a small positive value
    // to indicate an error
    return 1;
}
person rici    schedule 06.02.2016
comment
Имейте std::vector<const char*> argc, не нужно постоянное приведение. - person n. 1.8e9-where's-my-share m.; 07.02.2016
comment
@n.m.: Тогда argc.data — это const char*[], что не является правильным прототипом для execvp (TIAS: ошибка: недопустимое преобразование из 'const char**' в 'char* const*' [-fpermissive]) - person rici; 07.02.2016
comment
Мм да. У меня сложилось впечатление, что аргумент execvp равен const char* const*. Но нет, этот прототип не будет работать слишком хорошо в C. Жаль! - person n. 1.8e9-where's-my-share m.; 07.02.2016
comment
Увлекательно... инструментов, которых мне не хватало, были .data() и const_cast, но оказалось, что мой способ сработал бы, но я не понимал, что первым аргументом для execvp должен быть c_str(). Спасибо, что помогли разобраться! - person redux; 07.02.2016