Reputation: 5002
I have a vector of strings which are changing its contents for no apparent reason. Can't really explain what is going on. Sorry for a long listing, but it's really bugging me.
I have a GUI application which loads some files and uses a reader object which state can be set by using a parse(int argc, char* argv[])
method. The arguments are set in a dialog by checking various boxes and entering values. Here is a struct I use to hold the data from the dialog:
struct PointFilter
{
PointFilter(): argc(0) {};
~PointFilter() {};
int argc;
std::vector<std::string> args;
};
This struct is a member of the dialog class and after pressing ok button its populated with appropriate values. The values are taken from text boxes on the dialog into a stringstream and then pushed back into a std::vector:
class AdvancedLoadDialog
{
public:
AdvancedLoadDialog(const Glib::RefPtr<Gtk::Builder>&);
~AdvancedLoadDialog();
PointFilter get_point_filter()
{
return this->point_filter;
}
private:
PointFilter point_filter;
void on_ok_btn_clicked();
void AdvancedLoadDialog::on_ok_btn_clicked()
{
std::stringstream filter_stream;
// filter_stream << some_values_from_textboxes ...
std::vector<std::string> args;
std::string arg;
// we need a dummy first argument to emulate the command line
args.push_back("filter");
while (filter_stream >> arg)
{
args.push_back(arg);
}
point_filter.argc = args.size() > 1 ? args.size() : 0;
point_filter.args = args;
advanced_load_dialog->hide_all();
}
Everything works fine till this point and we have an AdvancedLoadDialog
object with point_filter
member which holds our arguments. Now in a separate window I take the point_filter object and pass it to a constructor of LoadWorker
class, which loads the files and also has a PointFilter
member.
load_worker = new LoadWorker(..., advanced_load_dialog->get_point_filter())
And then:
LoadWorker::LoadWorker(..., PointFilter pf) :
point_filter (pf)
All well and good. Now in the LoadWorker::run()
function I take the arguments from the point_filter, convert them into std::vector and pass them them to the `parse(int argc, char* argv[]) function I need.
void LoadWorker::run()
{
std::cout << "LoadWorker::file_filter contents: \n"
<< "point_filter.argc: " << point_filter.argc << "\n"
<< "point_filter.args: " << std::endl;
for (int i = 0; i < point_filter.argc; ++i)
std::cout << point_filter.args[i] << std::endl;
// ...
if (point_filter.argc != 0)
{
std::cout << "Using filter: " << std::endl;
std::vector<char*> argv;
for (std::vector<std::string>::const_iterator it = point_filter.args.begin();
it != point_filter.args.end(); ++it)
{
argv.push_back(const_cast<char*>(it->c_str()));
}
argv.push_back(0);
for (int i = 0; i < point_filter.argc; ++i)
{
std::cout << argv[i] << std::endl;
}
if (!lasreadopener.parse(point_filter.argc, &argv[0]))
{
send_message("Error parsing filter parameters.");
sig_fail();
return;
}
}
}
Now this works... once. You can notice that the arguments are printed twice, first as elements of LoadWorker::point_filter.args
vector and then as elements of the vector<char*> argv
. If I set the filter and then press the load button it all works. If I then try to load another file, without changing AdvancedLoadDialog::point_filter
at all, the arguments are disappearing. Here's an example output trying to load two files in a row.
LoadWorker::file_filter contents: point_filter.argc: 6 point_filter.args: filter -clip_z_above 12 -keep_intensity 11 222 Using filter: filter -clip_z_above 12 -keep_intensity 11 222 LoadWorker::file_filter contents: point_filter.argc: 6 point_filter.args: filter clip_z_above 2 keep_intensity 1 22 Using filter: filter
// 6 blank lines here
To make it even more odd, during the second run each string except the first one in point_filter.args
is missing the first character and in the argv
they are all empty.
Any clues whatsoever?
Upvotes: 0
Views: 1991
Reputation: 10605
In AdvancedLoadDialog, try pushing an allocated version of arg rather than an automatic version. You appear to be pushing a reference to a variable that will be destructed upon leaving AdvancedDialogLoad.
Upvotes: 1
Reputation: 6145
Be very, very cautious about doing things involving std::string::c_str
as you are doing here:
argv.push_back(const_cast<char*>(it->c_str()));
If you want to keep the values provided by c_str
, you should copy them into a new container first.
If cplusplus.com is to be trusted (which is not always the case),
the values in this array should not be modified in the program and are only guaranteed to remain unchanged until the next call to a non-constant member function of the string object
Though I can't immediately put my hands on any more useful information from other sources.
Upvotes: 1