Хм, очень интересно. Звучит как простая задача, но есть несколько предостережений.
Прежде всего, мы должны учесть, что есть как минимум 2 различных реализации execv
.
Одна под Posix / Linux, см. здесь и версию windows: см. здесь и здесь .
Обратите внимание на различные сигнатуры функций:
Linux / POSIX: int execv(const char *path, char *const argv[]);
Windows: intptr_t _execv(const char *cmdname, const char *const *argv);
В этом случае я нахожу версию WIndows немного чище, потому что параметр argv имеет тип const char *const *
. В любом случае, основная проблема заключается в том, что мы должны вызывать устаревший код.
Хорошо, давайте посмотрим.
Для функции execv
требуется массив символов-указателей с NULL-символами в конце с аргументом для вызов функции. Это нам нужно создать.
Начнем с std::string
, содержащего команду. Это должно быть разделено на части. Есть несколько способов, и я добавил разные примеры.
Самый простой способ - это поместить std::string
в std::istringstream
и затем использовать std::istream_iterator
, чтобы разбить его на части. Это типичная короткая последовательность:
// Put this into istringstream
std::istringstream iss(command);
// Split
std::vector parts(std::istream_iterator<std::string>(iss), {});
Мы используем конструктор диапазона для std::vector
. И мы можем определить std::vector
без аргумента шаблона. Компилятор может вывести аргумент из заданных параметров функции. Эта функция называется CTAD («дедукция аргумента шаблона класса»).
Кроме того, вы можете видеть, что я не использую итератор end () явно.
Этот итератор будет создается из пустого инициализатора по умолчанию, заключенного в скобки, с правильным типом, потому что он будет выведен таким же, как тип первого аргумента, поскольку конструктор std :: vector требует этого.
Мы можем избежать использование std::istringstream
и прямое преобразование строки в токены с помощью std::sregex_token_iterator
. Очень прост в использовании. В результате получается одна строка для разделения исходной строки команды:
// Split
std::vector<std::string> parts(std::sregex_token_iterator(command.begin(), command.end(), re, -1), {});
Все это сводится к 6 строкам кода, включая определение переменной и вызов функции execv
:
Пожалуйста, смотрите:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <iterator>
#include <memory>
#include <algorithm>
#include <regex>
const std::regex re{ " " };
// Define Dummy function for _execv (Windows style, eveything const)
// Note: Type of argv decays to " const char* const* "
int _execv(const char* path, const char* const argv[]) {
std::cout << "\n\nPath: " << path << "\n\nArguments:\n\n";
while (*argv != 0) std::cout << *argv++ << "\n";
return 0;
}
// Define Dummy function for _execv (Posix style)
// Note: Type of argv decays to " char* const* "
int execv(const char* path, char* const argv[]) {
std::cout << "\n\nPath: " << path << "\n\nArguments:\n\n";
while (*argv != 0) std::cout << *argv++ << "\n";
return 0;
}
int main() {
{
// ----------------------------------------------------------------------
// Solution 1
// Initial example
char path[] = "path";
const char* const argv[] = { "arg1", "arg2", "arg3", 0 };
_execv(path, argv);
}
{
// ----------------------------------------------------------------------
// Solution 2
// Now, string, with command convert to a handmade argv array
std::string command{ "path arg1 arg2 arg3" };
// Put this into istringstream
std::istringstream iss(command);
// Split into substrings
std::vector parts(std::istream_iterator<std::string>(iss), {});
// create "argv" List. argv is of type " const char* "
std::unique_ptr<const char*[]> argv = std::make_unique<const char*[]>(parts.size());
// Fill argv array
size_t i = 1U;
for (; i < parts.size(); ++i) {
argv[i - 1] = parts[i].c_str();
}
argv[i - 1] = static_cast<char*>(0);
// Call execv
// Windows
_execv(parts[0].c_str(), argv.get());
// Linux / Posix
execv(parts[0].c_str(), const_cast<char* const*>(argv.get()));
}
{
// ----------------------------------------------------------------------
// Solution 3
// Transform string vector to vector of char*
std::string command{ "path arg1 arg2 arg3" };
// Put this into istringstream
std::istringstream iss(command);
// Split
std::vector parts(std::istream_iterator<std::string>(iss), {});
// Fill argv
std::vector<const char*> argv{};
std::transform(parts.begin(), parts.end(), std::back_inserter(argv), [](const std::string& s) { return s.c_str(); });
argv.push_back(static_cast<const char*>(0));
// Call execv
// Windows
_execv(argv[0], &argv[1]);
// Linux / Posix
execv(argv[0], const_cast<char* const*>(&argv[1]));
}
{
// ----------------------------------------------------------------------
// Solution 4
// Transform string vector to vector of char*. Get rid of istringstream
std::string command{ "path arg1 arg2 arg3" };
// Split
std::vector<std::string> parts(std::sregex_token_iterator(command.begin(), command.end(), re, -1), {});
// Fill argv
std::vector<const char*> argv{};
std::transform(parts.begin(), parts.end(), std::back_inserter(argv), [](const std::string& s) { return s.c_str(); });
argv.push_back(static_cast<const char*>(0));
// Call execv
// Windows
_execv(argv[0], &argv[1]);
// Linux / Posix
execv(argv[0], const_cast<char* const*>(&argv[1]));
}
return 0;
}