C++: From stringstream to char**

。_饼干妹妹 提交于 2019-12-22 01:43:27

问题


I have a class with parse(int argc, char* argv[]) function which I have to use to set a desired state of an object. I'm taking the parameters from the gui using stringstream and then I'm trying to convert them to char** to pass them to the function. Here's what I've got:

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

What am I missing? Is there a better way of achieving this?


回答1:


A problem would be reallocation of the args vector as push_back() will grow the size of the vector if required:

If new size() is not larger than capacity(), no iterators or references are invalidated. Otherwise all iterators and references are invalidated.

The argv vector is storing pointers to the internals of elements in args, so these would be invalidated.

A solution would be to create the args vector first then create the argv vector afterwards:

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);

The for loop that prints out the argv strings is incorrect. This:

&argv[0][i]

is a char* but starts from ith element of the first entry in argv. For example, if the first c-string in argv was "string":

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

change to:

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



回答2:


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

/* ... */

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

Many problems here.

  1. The pointer returned by c_str() is not guaranteed to be valid after any subsequent call of a non-const member function of the same string. The pointer returned from c_str() generally shouldn't be stored and used later, especially if you're not sure if other code will call a non-const member of the string.
  2. You are const_casting the const-nedd away from the pointer returned by c_str(). The cast itself is legal, if not an anti-pattern. But if you then later try to modify the data stored at that pointer, that's Undefined Behavior.

Here is what the Standard has to say about c_str():

21.3.6 basic_string string operations [lib.string.ops]

const charT* c_str() const;

1/ Returns: A pointer to the initial element of an array of length size() + 1 whose first size() elements equal the corresponding elements of the string controlled by *this and whose last element is a null character specified by charT().

2/ Requires: The program shall not alter any of the values stored in the array. Nor shall the program treat the returned value as a valid pointer value after any subsequent call to a non-const member function of the class basic_string that designates the same object as this. const charT* data() const;

3/ Returns: If size() is nonzero, the member returns a pointer to the initial element of an array whose first size() elements equal the corresponding elements of the string controlled by *this. If size() is zero, the member returns a non-null pointer that is copyable and can have zero added to it.

4/ Requires: The program shall not alter any of the values stored in the character array. Nor shall the program treat the returned value as a valid pointer value after any subsequent call to a non- const member function of basic_string that designates the same object as this. allocator_type get_allocator() const;

5/ Returns: a copy of the Allocator object used to construct the string.




回答3:


You've forgotten to initialize the variable i in the loop. And You're trying to print out only the first item in the vector argv.

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



回答4:


You can get rid of const_cast and not worry about parse() method possibly modifying the arguments by doing something like this:

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());


来源:https://stackoverflow.com/questions/11209311/c-from-stringstream-to-char

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!