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.
- The pointer returned by
c_str()
is not guaranteed to be valid after any subsequent call of a non-const
member function of the samestring
. The pointer returned fromc_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 thestring
. - You are
const_cast
ing theconst
-nedd away from the pointer returned byc_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());
来源:https://stackoverflow.com/questions/11209311/c-from-stringstream-to-char