Accessing the values of map from its key using pointer to map - c++

I want to dynamically allocate an array of pointers to an unordered_map in C++. The std::unordered map has been typedef as 'dictionary'.
dict_array= ( dictionary **) calloc(input_size, sizeof(dictionary*));
Now I want to access the individual hashmaps, and for each individual hashmap (mydict), I want to access the values using some key. like below:
for (int i=0; i< input_size, i++){
dictionary *mydict= dict_array[i];
mydict[some_key]++; /*access the value against 'some_key' and increment it*/
}
But this above line to access the value against the key generates a compilation error. What would be the correct way to access it?

In your example, you haven't actually allocated any dictionary or (std::unordered_map) objects yet.
The dict_array[i] is simply a null pointer. Thus the assignment to mydict also results in a null pointer. You would need to construct a dictionary first by invoking dict_array[i] = new dictionary();.
The expression mydict[some_key]++ doesn't mean what you think it does because mydict is a dictionary * and not a dictionary. Thus you would need to actually dereference it first before having access to a valid dictionary object:
(*my_dict)[some_key]++
But again, before this would work, you need to initialize the underlying pointers.
Also, it's generally a bad idea (which often leads to undefined behavior) to mix C allocation with C++ standard objects.

Why on earth are you messing around with pointers like this?
If you really want an array of pointers, then you'll have to dereference to access the map itself:
(*my_dict)[some_key]++;
assuming you've correctly set up each pointer to point to a valid dictionary.
Or use a less insane data structure
std::vector<dictionary> dict_array(input_size);
dict_array[i][some_key]++;

use operator[]():
mydict->operator[](some_key)++;

Related

Is there any way to give different names to the members of an array?

For example I am making a Pacman game and I am using a struct array to represent the ghosts.
But is there any way to change the ghost[0] expression to Inky ?
It seems you are looking for associative array. Associative array is an array that can be accessed with something like association. For example, if you want to access your fruits array and get the apple, you could do: fruits["apple"] and get the value, instead of wondering which index the apple was.
If this is what you are really looking for, then in C++ it is called a map. The map is the same as associative array. Take a look onto how to use it.
https://en.cppreference.com/w/cpp/container/map
You can define a reference to any item in your struct array.
Example:
Ghost &inky = ghosts[0];
Ghost &blinky = ghosts[1];
Ghost &pinky = ghosts[2];
Ghost &clyde = ghosts[3];
To respect C++ convention, I recommand that all these references are defined with a name that begin with a lower case.
You can then use all these references as normal variables using . to call member's functions or to read or assign member's variables.
Example:
inky.setBackColor(blue);
inky.remove();
clyde.size += 2;

Loop over objects with similar names

I have a set of common objects in c++ named object1, object2, object3, ....
These objects are constructed from a class, that provides some functions, like Add() which "merges" two or more objects.
What I want to do is loop over the object and merge them all together. If I use
for (int i = 1 ; i <= 30 ; i++){
TString::Format("object%d",i)->Add();
}
obviously it cannot work, because I am trying to access an object using a wrong pointer to call it.
First of all, I would like to know if there is a way to loop over the objects and secondly if there is a specific name for this procedure.
EDIT I thought of using an array or a vector and access it from there, but in order to fill the array I have to give many push_back commands, which is not convenient if you have 100 objects.
This is not possible, because variable names are compile-time artifacts in C++. Apart from the debugger which gets additional information "on the side", a program cannot access a variable by its name.
If you need to associate an object with a name or an index, put the desired objects in a container. If you need access by name, use map<string,YourObjectType> or unordered_map<string,YourObjectType>. If you need access by index, use vector<YourObjectType>:
vector<YourObjectType*> objectPtrs;
objectPtrs.push_back(&object0);
objectPtrs.push_back(&object1);
objectPtrs.push_back(&object2);
...
objectPtrs.push_back(&object29);
for (int i = 1 ; i <= 30 ; i++){
TString::Format(*objectPtrs[i])->Add();
}
The example above makes a container of pointers so that the loop accesses the original objects, not their copies stored in a vector. You could eliminate the original object variables by storing them in a vector, and accessing them by index rather than by name.
If you have access to the code which creates the objects in the first place:
Build-up a key-value pair, during their creation/construction and enter them into a map.
The key is going to be "Object1", "Object2" etc. Value will be the pointer to the object allocated/created just now.
Push these pairs into a map.
When it is time to iterate over a set of objects, recreate that name ("ObjectN") and lookup in the map, to get back the pointer to the object.
[
["Object2", ptr-to-Object2]
["Object7", ptr-to-Object7]
...
]
This won't assume anything about the indices etc. You build a name-to-object association and look it up when you iterate.

Address of map value

I have a settings which are stored in std::map. For example, there is WorldTime key with value which updates each main cycle iteration. I don't want to read it from map when I do need (it's also processed each frame), I think it's not fast at all. So, can I get pointer to the map's value and access it? The code is:
std::map<std::string, int> mSettings;
// Somewhere in cycle:
mSettings["WorldTime"] += 10; // ms
// Somewhere in another place, also called in cycle
DrawText(mSettings["WorldTime"]); // Is slow to call each frame
So the idea is something like:
int *time = &mSettings["WorldTime"];
// In cycle:
DrawText(&time);
How wrong is it? Should I do something like that?
Best use a reference:
int & time = mSettings["WorldTime"];
If the key doesn't already exist, the []-access will create the element (and value-initialize the mapped value, i.e. 0 for an int). Alternatively (if the key already exists):
int & time = *mSettings.find("WorldTime");
As an aside: if you have hundreds of thousands of string keys or use lookup by string key a lot, you might find that an std::unordered_map<std::string, int> gives better results (but always profile before deciding). The two maps have virtually identical interfaces for your purpose.
According to this answer on StackOverflow, it's perfectly OK to store a pointer to a map element as it will not be invalidated until you delete the element (see note 3).
If you're worried so much about performance then why are you using strings for keys? What if you had an enum? Like this:
enum Settings
{
WorldTime,
...
};
Then your map would be using ints for keys rather than strings. It has to do comparisons between the keys because I believe std::map is implemented as a balanced tree. Comparisons between ints are much faster than comparisons between strings.
Furthermore, if you're using an enum for keys, you can just use an array, because an enum IS essentially a map from some sort of symbol (ie. WorldTime) to an integer, starting at zero. So then do this:
enum Settings
{
WorldTime,
...
NumSettings
};
And then declare your mSettings as an array:
int mSettings[NumSettings];
Which has faster lookup time compared to a std::map. Reference like this then:
DrawText(mSettings[WorldTime]);
Since you're basically just accessing a value in an array rather than accessing a map this is going to be a lot faster and you don't have to worry about the pointer/reference hack you were trying to do in the first place.

Google's dense_hash_map crashing in set_empty_key() function

I am trying to use google dense_hash_map to store key value data instead of std:map.
When I tested with (int, int ) pair, I set the set_empty_key(mymap, -2) and it worked.
But, now when I use it with my (hash, value) pair, I set the set_empty_key (mymap -2) or set_empty_key(mymap, some_random_hash), in both the cases my program crashes in set_empty_key();.
Anyone can guide me with this? How can I fix this crash?
Thanks.
I don't know the exact reason of crash you've got, but, based on your description I see at least two potential mistakes.
First. Check that both key_type and data_type types are POD types and don't contain pointers to itself. More specifically (original):
Both key_type and data_type must be
plain old data. In addition, there should
be no data structures that point
directly into parts of key or value,
including the key or value itself (for
instance, you cannot have a value like
struct {int a = 1, *b = &a}. This is
because dense_hash_map uses malloc()
and free() to allocate space for the
key and value, and memmove() to
reorganize the key and value in
memory.
Second. Concerning using dense_hash_map. You need to set up some special "empty" key value which will never be used for real elements stored in your collection. Moreover if you are going to use erase() you need to specify special key for deleted items which also will never be used as key for real stored items.
That is perfectly described here:
dense_hash_map requires you call
set_empty_key() immediately after
constructing the hash-map, and before
calling any other dense_hash_map
method. (This is the largest
difference between the dense_hash_map
API and other hash-map APIs. See
implementation.html for why this is
necessary.) The argument to
set_empty_key() should be a key-value
that is never used for legitimate
hash-map entries. If you have no such
key value, you will be unable to use
dense_hash_map. It is an error to call
insert() with an item whose key is the
"empty key." dense_hash_map also
requires you call set_deleted_key()
before calling erase(). The argument
to set_deleted_key() should be a
key-value that is never used for
legitimate hash-map entries. It must
be different from the key-value used
for set_empty_key(). It is an error to
call erase() without first calling
set_deleted_key(), and it is also an
error to call insert() with an item
whose key is the "deleted key."

How to store the values of element values but it is referencing final address

when i am storing data into an array after performing parsing an xml file we have to store it in an array but here it stores like the final address of xml file i.e. it stores all the values but the problem is it all values are referencing to one address so i used vector now for getting all the values, so is there any possibility to get all the values without using any predefined methods.
My code is like,
while(attr){
if(!xmlStrcmp(attr->name,(const xmlChar *)"user")){
sprintf((char *)UserName.c_str(),"%s",attr->children->content);
std::cout<<"UserName: "<<UserName.c_str()<<"\t\t";
UN.push_back(UserName.c_str());
}
if(!xmlStrcmp(attr->name,(const xmlChar *)"password")){
sprintf((char *)Password.c_str(),"%s",attr->children->content);
std::cout<<"Password: "<<Password.c_str()<<std::endl;
PWD.push_back(Password.c_str());
}
attr=attr->next;
}
even vectors also, i am getting same problem so how can i solve this.
I think the problem is that you are storing values somewhere in a vector that aren't supposed to be stored permanently. In particular, this line:
UN.push_back(UserName.c_str());
Seems to be storing the result of UserName.c_str() into a vector<const char*>. If you do this, then you'll run into trouble as soon as you modify the UserName string, or when that string goes out of scope. The value of c_str() is pretty fragile - it's not valid after doing just about anything to the source string - and exists primarily so that you can take string data and pass it into C code that needs a const char* as an argument.
To fix this, I would suggest either explicitly copying the strings before inserting them into the vector:
UN.push_back(strdup(UserName.c_str());
(You don't have to use strdup here; it's just an example)
Alternatively, consider storing std::strings in the vector, which own the string resource they point at and don't have this problem.
Hope this helps!