tags:

views:

319

answers:

4

I understand that when we insert values into the STL map, a copy is made and stored. I have code that essentially does a find on the map and obtains an iterator.

I then intend to use the iterator to change the value in the map. The results are not what I would expect ie: the value is not changed when accessed from another part of the program. I suspect its because the change I am applying is to a copy of value. the relevant code is as follows.

ObjectMappingType::iterator it = objectMapping_.find(symbol);

if (it == objectMapping_.end()) {
    throw std::invalid_argument("Unknown symbol: " + symbol);
}
get<3>(it->second) = value;

NOTE: I am actually trying to change a value inside a boost::tuple that is stored as the 'value' part of the map.

A: 

get<3>(it->second) = value;

This would seem to make a cast/function/constructor call and then assign to the return value, which would be a temporary. The thing you want is the thing refered to by it->second, so you need something like:

(it->second).xxxx = value;

where xxxx is a suitable field of the thing you are accessing. Unfortunately, I know zip about boost::tuple.

anon
Did you even read the question? The value is a boost tuple. calling the get method on a tuple returns a reference to the element. Which, on a non-const tuple, allows for modification in place.
A. Levy
Did you even read my answer. Like I said, I know nothing about boost::tuple, and from the code it is not obvious that get<3> gas anyting to do with boost.
anon
Guys I really appreciate you taking the time to awnser :).
Pradyot
Neil: Regardless of your ignorance of boost, you should know that return values are not always temporaries. In particular, *references* returned by functions are not temporaries.
Stefan Monov
Stefan: I assumed, from the OP's question, that the thing being returned was a temporary, as this wouldhave explained why the map was not being updated.
anon
A: 

The operator[] on a map will give a reference to the actual contained element, but it has the nasty side-effect of creating a map entry if none existed before. Since you're already checking the result of find() to see if the key exists, you can use it safely.

get<3>(objectMapping_[symbol]) = value;
Mark Ransom
it->second is a reference to "the actual contained element"
anon
Hmm, maybe i have something else going on, neither approach seemed to work.
Pradyot
@Neil: That was my intuition, but the documentation is hard to follow. On re-reading it my conclusion is that (*it) is a reference to an internal map node, and thus it->second is truly a reference to the stored value. But it's not obvious on first reading.
Mark Ransom
In what other way would write access to elements via iterators work if not by providing references on dereferencing?
Georg Fritzsche
@gf: It makes logical sense, and I'm sure I've used it that way in the past. But I went to look for supporting documentation and found it lacking. I couldn't find any guarantee that the iterator provides write access. Operator[] is much easier to follow.
Mark Ransom
+4  A: 

Hmm... both methods seem to work fine for me. Here's the entire example that I used:

#include <iostream>
#include <map>
#include <string>
#include <boost/tuple/tuple.hpp>

typedef boost::tuple<int, std::string> value_type;
typedef std::map<int, value_type> map_type;

std::ostream&
operator<<(std::ostream& os, value_type const& v) {
    os << " number " << boost::get<0>(v)
       << " string " << boost::get<1>(v);
    return os;
}

int
main() {
    map_type m;

    m[0] = value_type(0, "zero");
    m[1] = value_type(0, "one");
    m[2] = value_type(0, "two");

    std::cout
        << "m[0] " << m[0] << "\n"
        << "m[1] " << m[1] << "\n"
        << "m[2] " << m[2] << "\n"
        << std::endl;

    boost::get<0>(m[1]) = 1;

    map_type::iterator iter = m.find(2);
    boost::get<0>(iter->second) = 2;

    std::cout
        << "m[0] " << m[0] << "\n"
        << "m[1] " << m[1] << "\n"
        << "m[2] " << m[2] << "\n"
        << std::endl;

    return 0;
}

The output is exactly what I would have expected.

lorien$ g++ -I/opt/include -gdwarf-2 foo.cpp 
lorien$ ./a.out
m[0]  number 0 string zero
m[1]  number 0 string one
m[2]  number 0 string two

m[0]  number 0 string zero
m[1]  number 1 string one
m[2]  number 2 string two
lorien$
D.Shawley
I am blown away that you went through this trouble! I'll have to verify and post back.
Pradyot
Hehehe... `boost::tuple` has piqued my interest recently and I was in the mood to write some code after reading docs all day ;)
D.Shawley
A: 

Without seeing more of your code I can't be sure of this, but it sounds like you could have a threading issue. Does your program use multiple threads by any chance? Maybe not even explicitly, but perhaps you call a library that does some work in a separate thread? Here is what I would do to start debugging.

  1. Have a check that will re-find the value in the map after you set it, and check that it is the correct new value and throw an exception if it is not.
  2. Reproduce the error by accessing the value from the "other part of the program" and see whether it throws the exception
  3. Step through with a debugger to make sure that the modification is indeed happening BEFORE the access in the other part of the program instead of after.
  4. If there are too many accesses to make it practical to do this by hand, dump a trace to a file. That is, add code to append to a log file every time the map is accessed. Each line should have the time of access to as fine a resolution as your system clock allows, the address of the map (so you know you are modifying the same map), the symbol key, the value, and the new value (if this was a modifying access). This way you can pinpoint exactly what times the map modifications are not showing up in the other part of the program, and whether they are before or after the access.
A. Levy