C++: от строкового потока до символа **

У меня класс с parse(int argc, char* argv[]) функция, которую я должен использовать, чтобы установить желаемое состояние объекта. Я беру параметры из графического интерфейса, используя stringstream а затем я пытаюсь преобразовать их в char**, чтобы передать их в функцию. Вот что у меня есть:

std::stringstream sstream;

sstream << "-clip" << " " << min_x_entry.get_text()
        << " " << max_x_entry.get_text(); // etc.

std::cout << sstream.str();    // All looks good here

std::vector<std::string> args;
std::vector<char*> argv;
std::string arg;

while (sstream >> arg)
{
    args.push_back(arg);
    argv.push_back(const_cast<char*>(args.back().c_str()));
}
argv.push_back(0);

int argc = args.size();

for (int i = 0; i < argc; ++i)
    std::cout << &argv[0][i];    // This outputs garbage

my_object.parse(argc, &argv[0])  // And this fails

Что мне не хватает? Есть ли лучший способ добиться этого?

4 ответа

Решение

Проблемой будет перераспределение args вектор как push_back() увеличит размер вектора, если потребуется:

Если новый размер () не больше емкости (), никакие итераторы или ссылки не будут признаны недействительными. В противном случае все итераторы и ссылки становятся недействительными.

argv вектор хранит указатели на внутренние элементы в args, так что они будут признаны недействительными.

Решение было бы создать args сначала вектор, а затем создать argv вектор потом:

while (sstream >> arg) args.push_back(arg);

for (auto i = args.begin(); i != args.end(); i++)
{
    argv.push_back(const_cast<char*>(i->c_str()));
}
argv.push_back(0);

for цикл, который распечатывает argv строки неверны. Это:

&argv[0][i]

это char* но начинается с i элемент первой записи в argv, Например, если первая c-строка в argv было "string":

&argv[0][1] is "tring"
&argv[0][2] is "ring"

изменить на:

for (int i = 0; i < argc; i++)
    std::cout << argv[i] << std::endl; // Added 'endl' to flush 'cout'.
std::vector<std::string> args;
std::vector<char*> argv;

/* ... */

    argv.push_back(const_cast<char*>(args.back().c_str()));

Здесь много проблем.

  1. Указатель возвращается c_str() не гарантируется быть действительным после любого последующего вызоваconst функция-член того же string, Указатель возвращается из c_str() обычно не следует хранить и использовать позже, особенно если вы не уверены, будет ли другой код вызыватьconst член string,
  2. Вы const_castв const-ndd от указателя, возвращенного c_str(), Сам актерский состав является законным, если не анти-шаблон. Но если позже вы попытаетесь изменить данные, хранящиеся в этом указателе, это неопределенное поведение.

Вот что говорит Стандарт о c_str():

21.3.6 Строковые операции basic_string [lib.string.ops]

const charT* c_str() const;

1 / Возвращает: указатель на начальный элемент массива длины size() + 1, чьи первые элементы size () равны соответствующим элементам строки, управляемой *this, и чей последний элемент является нулевым символом, заданным charT(),

2/ Требуется: программа не должна изменять ни одно из значений, хранящихся в массиве. Кроме того, программа не должна обрабатывать возвращаемое значение как действительное значение указателя после любого последующего вызова неконстантной функции-члена класса basic_string, которая обозначает тот же объект, что и этот. const charT * data () const;

3 / Returns: Если size () не равен нулю, элемент возвращает указатель на начальный элемент массива, чьи первые элементы size () равны соответствующим элементам строки, управляемой *this. Если size () равен нулю, элемент возвращает ненулевой указатель, который можно скопировать и к которому можно добавить ноль.

4 / Требуется: программа не должна изменять какие-либо значения, хранящиеся в массиве символов. Также программа не должна обрабатывать возвращаемое значение как допустимое значение указателя после любого последующего вызова неконстантной функции-члена basic_string, которая обозначает тот же объект, что и этот. allocator_type get_allocator () const;

5 / Returns: копия объекта Allocator, используемого для построения строки.

Вы забыли инициализировать переменную i в петле. И вы пытаетесь распечатать только первый элемент в векторе argv,

for (int i = 0; i < argc; ++i)
    std::cout << argv[i];

Вы можете избавиться от const_cast и не беспокоиться о parse() Метод, возможно, изменяет аргументы, делая что-то вроде этого:

std::vector<std::vector<char>> args;

std::for_each(std::istream_iterator<std::string>(sstream),
              std::istream_iterator<std::string>(),
              [&args](const std::string& str)
              {
                  std::vector<char> temp(str.begin(), str.end());
                  temp.push_back('\0');
                  args.push_back(temp);
              });

std::vector<char*> argv(args.size());

for (auto& v : args) argv.push_back(v.data());

my_object.parse(argv.size(), argv.data());
Другие вопросы по тегам