Question

I have the following definition for boost unordered_map

typedef boost::unordered::unordered_map<std::String, CLIENT_STATE> CLIENT_MAP;

Where CLIENT_STATE is an struct defined as follow:

typedef struct{
unsigned char state;

/* socket fd of the client */
int fd;

/* File path requested by the client */
char file_path [255];

/* Current file offset */
unsigned long int offset;

} CLIENT_STATE;

When I try to add an element to CLIENT_MAP using emplace, the CLIENT_MAP creates a separate copy of the element, not related to the original definition. i.e. any modifications to the original element definition, won't do any changes to the element in the unordered_map.

I was told before, that using emplace won't clone the my original element, it will place it directly in the container check the link

So what's the best way to let the changes to the added element in the container affects the original definition and vice versa.

This is what I mean:

    CLIENT_STATE new_client;
    new_client.offset = 5;
    client_map.emplace("Test", new_client);
    new_client.offset = 10;

    cout << client_map["Test"].offest;

The cout won't print 10, it will print 5.

Currently to solve this issue, after an element to the unordered_map I work on the returned std::pair and do the modification, but I think it is not efficient way to do that.

Was it helpful?

Solution

emplace, allows an object to be constructed from the arguments passed into it without needing to create the object to be passed in first, saving an overhead by removing a copy construction which normally happens as the result of creating objects to be inserted.

You can achieve what you want by use of pointers

typedef boost::unordered::unordered_map<std::String, CLIENT_STATE* > CLIENT_MAP;

But using pointers might prove problematic for memory handling, like object deletion, etc.

You can consider using boost::shared_ptr, something like following : (I'm no shared_ptr/boost expert)


include <boost/shared_ptr.hpp>

boost::unordered::unordered_map<std::String, 
                  boost::shared_ptr<CLIENT_STATE> > client_map ;

std::string s ="client1" ;
CLIENT_STATE *c1 = new CLIENT_STATE;
//c1->state, c1->id, etc
my_map[t] = c1 ;

If you needn't make copies of the objects stored in the map, you can use C++11's std::unique_ptr

OTHER TIPS

You don't want to use emplace. What you want is called shared pointer semantics. However, for future visitors to this question, it may be useful to explain how to correctly use emplace with an associative container.

As @gha.st correctly commented, emplace will call the constructor of std::pair<std::string, CLIENT_STATE>, which will make copies of the string and CLIENT_STATE objects.

Since C++11, std::pair has an extra constructor overload taking a first argument of tag type std::piecewise_construct_t which allows the members of the std::pair themselves to be emplace-constructed. Observe:

client_map.emplace(std::piecewise_construct,
                   std::forward_as_tuple("Test"),
                   std::forward_as_tuple(5));

Now emplace will call the tag-overloaded constructor of pair, which in turn will emplace-construct its two data members from the arguments in the forwarded tuples.

In this case, CLIENT_STATE is an aggregate type, so (before C++20) it doesn't have a constructor which we can call with an int like this. But the above is the general technique to use with typical C++ class types. The best we can do for an aggregate before C++20 is to move-construct it from a temporary:

client_map.emplace("Test", CLIENT_STATE{ 0, 0, "", 5 });

In this case, this is of no help because the members of CLIENT_STATE are not move-constructible, so a move is the same as a copy.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top