jaho
jaho

Reputation: 5002

Disappearing contents of std::vector<std::string>

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

Answers (2)

Les
Les

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

Rook
Rook

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

Related Questions