简体   繁体   中英

C++: From stringstream to char**

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?

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 i th 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'.
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_cast ing 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.

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM