I'm working on a project that needs unique keys and values so I decided to use maps. Everything works expect for the case where someone may want to change the key value. I'm not sure why, but it causes a fragmentation fault. Can I not do this?
void Journal::set_id(int id){ // journal class
if(join.count(id)){ // join is: static map <int,string> join
cout<<"Journal ID is already being used. Try again."<<endl;
}
else {
join.erase (join.find(id));
join.insert(pair<int,string>(id,name));
}
}
Your logic is flawed.
void Journal::set_id(int id){
if(join.count(id)){
cout<<"Journal ID is already being used. Try again."<<endl;
}
else {
// When you hit this block of the code, there is nothing
// in the map corresponding to 'id'.
// join.find(id) returns the same iterator as join.end()
// Calling erase() with that iterator is causing you the
// problem.
// Don't call erase. Just insert the new item.
// join.erase (join.find(id));
join.insert(pair<int,string>(id,name));
}
}
You have just checked to make sure that id is not being used as a key in the map. If it is, you issue an error. So now you know that id is not in the map. If id is not in the map, join.find(id) will return join.end(), so you really didn't need to call find at all. But more importantly, you then call join.erase(join.end()), which is an error.
See documention for std::map::erase() in cppreference:
The iterator pos must be valid and dereferenceable. Thus the end() iterator (which is valid, but is not dereferencable) cannot be used as a value for pos.
Rather than check whether the key is present, and insert it only if not found, you can simplify the code by just inserting the item, and then checking the return value to see if the insertion succeeded (which it won't if that key was already present).
void Journal::set_id(int id){
if (!(join.insert(std::make_pair(id, name)).second))
cout<<"Journal ID is already being used. Try again."<<endl;
}
This should also improve speed, since it only searches the tree once whereas code doing a count then an insert has to search it twice.
Related
I am currently using tbb's concurrent hash map to perform concurrent insertions into a hash map. Each key is a string and a value is a vector of integers. I would like to achieve the following: during insertions, if the key does not exist, I insert it and add the value to its vector. If it exists, I simply add the value to its vector.
After inspecting the tbb concurrent hash map API, I noticed that both the find and insert functions return booleans only. So how can I return a pointer to the key if it exists?
There are methods which require an accessor in theirs arguments. The accessor is basically a pointer coupled with a scoped_lock protecting concurrent access to the element. Without the lock, an element can be modified concurrently resulting in a data-race. So, never use a pointer to element in concurrent_hash_map directly (unless protected by the accessor).
Also, you don't need a find() method for your task since insert() methods create the element if it does not exist.
According to the Reference manual, the hash map has the following methods which will likely satisfy your needs:
bool insert( accessor& result, const Key& key ); // creates new element by default
bool insert( accessor& result, const value_type& value );// creates new element by copying
Here is an example:
{
hash_map_t::accessor a;
hash_map.insert( a, key ); // creates by default if not exists, acquires lock
a->second.my_vector.push_back( value ); // new or old entry, add to vector anyway
} // the accessor's lock is released here
During insertions, if the key does not exist then key inserted and added the value to its vector.
If it exists, return false and I simply add the value to its vector.
{
hash_map_t::accessor accessor;
bool result = hash_map.insert(accessor, std::make_pair(key, {value})); // creates by default if not exists, acquires lock
if(result == false)
accessor->second.push_back(value); // if key exists
} // the accessor's lock is released here
the search function will take in a key value which is a const string& and it will look in the hashtable to see if the key value item is found or not..
my code here works except for a few cases (for example when i pass in "ZZ0" in to the function).
not sure if its because i'm using == instead of strcmp
But i can't see why it would not work simply because of that. can someone please explain if i'm missing something here?
void HT::search(const string& item)
{
int index=hash(item);
bool found=false;
for(unsigned int i=0;i<hsize;i++)
{
if(hTable[(index+i)%hsize].key==item)
{
found=true;
cout<<"item found";
break;
}
}
if(!found)
{
cout<<" : Item not found!"<<endl;
}
cout<<endl;
}
I see you use open address for collision and I don't see any problem here. Are you sure that your string "ZZO" has been inserted into the hash table correctly? I suggest you do a unit test to check that. Just insert the string and iterate the hashtable see whether it exists.
BTW, I suggest you use Separate chaining to implement the hashtable because it's easy to implemented and generally more efficient(Well definitely depends on your hash function). And it could be further extended to be thread-safe.
Even without seeing your insert function code -- I suggest to you that your 'insert' function is the problem -- it most likely does not handle hash collisions and simply override some values/elements -- why don't you post your code for the insert function as well?
I'd like to know whether there is a way of checking if an element exists or not in the stack.
Assume that the stack interface has push, pop, isEmpty, getTop, member functions.
I know we can do it, if we get the top, compare it with that element and pop it, till it gets empty. But this method would be costy as we'd have to create another stacks to store the pop-ed elements and restore it again.
Here's some pseudo-code for a method that checks for whether or not an element is in the stack:
template<class T>
bool find (stack<T> source, T value)
{
while (!source.isEmpty() && source.top() != value)
source.pop();
if (!source.isEmpty())
return true;
return false;
}
It's critical that the source stack is passed by value, so that it isn't modified. Also, realize that this solution probably isn't as efficient as using a different container than stack and simply calling a method which checks for a value.
Can anyone tell me why my list iterator hates .end()-1 ? I want to iterato to one before the list end, I was under the impression you could always do this !
Code on request
std::list<Hammer::shared_ptr<Hammer::Actor>> collisionActorsList;
std::list<Hammer::shared_ptr<Hammer::Actor>>::iterator _actorUpdateIter = collisionActorsList.begin();
while(_actorUpdateIter != (collisionActorsList.end()-1)) // ERROR HERE
{
// check against every other actor
std::list<Hammer::shared_ptr<Hammer::Actor>>::iterator _otherActorsUpdateIter = _actorUpdateIter+1; // ERROR HERE TOO
while(_otherActorsUpdateIter != collisionActorsList.end())
{// SOME STUFF }
}
std::list uses a bidirectional iterator, which doesn't support operator- or operator+. Use std::prev(collisionActorsList.end()) and std::next(_actorUpdateIter).
As pointed out below in the comments, you should be aware of whether your list is empty. If it is, these will fail to do what you want. There's a simple function for that: collisionActorsList.empty().
I'm currently learning C++ and practicing my Knowledge by implementing an simple AddressBook Application. I started with an Entry class and an AddressBook class which implements a STL Map to access the entries by the last names of the persons. Now I arrived at the following code:
Entry AddressBook::get_by_last_name(string last_name){
if(this->addr_map.count(last_name) != 0){
//What can I do here?
} else {
return addr_map[last_name];
}
In Scripting Languages I would just return something like -1, Error Message(A List in Python) to indicate that the Function failed. I don't want throw an exception, because it's part of the application logic. The Calling Class should be able to react to the request by printing something on the console or opening a Message Box. Now I thought about implementing the Scripting Languae Approach in C++ by introducing some kind of an Invalid State to the Class Entry. But isn't that bad practice in C++? Could it be that my whole class design is just not appropriate? I appreciate any help. Please keep in mind that I'm still learning C++.
Some quick notes about your code:
if(this->addr_map.count(last_name) != 0){
//What can I do here?
You probably wanted it the other way:
if(this->addr_map.count(last_name) == 0){
//handle error
But your real problem lies here:
return addr_map[last_name];
Two things to note here:
The operator[] for map can do 2 things: If the element exists, it returns it; If the element doesn't exist, it creaets a new (key,value) pair with the specified key and value's default constructor. Probably not what you wanted. However, if your if statement from before would have been the right way, then the latter would never happen because we would knowthe key exists before hand.
In calling count() before, you effectively tell map to try and find the element. By calling operator[], you are telling map to find it again. So, you're doing twice the work to retrieve a single value.
A better (faster) way to do this involves iterators, and the find method:
YourMap::iterator it = addr_map.find(last_name); //find the element (once)
if (it == addr_map.end()) //element not found
{
//handle error
}
return *it.second; //return element
Now, back to the problem at hand. What to do if last_name is not found?
As other answers noted:
Simplest solution would be to return a pointer (NULL if not found)
Use boost::optional.
Simply return the YourMap::iterator but it seems that you are trying to "hide" the map from the user of AddressBook so that's probably a bad idea.
throw an exception. But wait, now you'll have to first check that calling this method is 'safe' (or handle the exception when appropriate). This check requires a boolean method like lastNameExists which would have to be called before calling get_by_last_name. Of course then we'er back to square 1. We're performing 2 find operations to retrieve a single value. It's safe, but if you're doing A LOT of calls to get_by_last_name then this is potentially a good place to optimize with a different solution (besides, arguably the exception is not very constructive: What's wrong with searching for something that isn't there, huh?).
Create a dummy member for Entryindicating that is not a real Entry but that is very poor design (unmanageable, counter intuitive, wasteful - you name it).
As you can see, the first 2 solutions are by far preferable.
One dead-simple option is to change the return type to Entry* (or const Entry*) and then return either the address of the Entry if found, or NULL if not.
If you use Boost, you could return a boost::optional<Entry>, in which case your success code would be the same, but on not-found you'd say return boost::none. This is fancier, but does about the same thing as using a pointer return type.
Throwing an exception is definitely the 'correct' C++ thing to do, based on your function return type.
You might want a function like this to help you, though:
bool AddressBook::lastNameExists(const string &last_name)
{
return addr_map.count(last_name) > 0;
}
Note that your current code returns the entry 'by value' so modifying the returned entry won't update the map. Not sure if this is by accident or design...
Other answers have given various approaches, most of them valid. I didn't see this one yet:
You could add a second parameter with a default value:
Entry AddressBook::get_by_last_name(string last_name, const Entry& default_value){
if(this->addr_map.count(last_name) == 0){
return default_value;
} else {
return addr_map[last_name];
}
In this particular instance, there might not be a sensible default value for a non-existing last name, but in many situations there is.
In C++ you have several ways of signalling that an issue happened in your function.
You can return a special value which the calling code will recognize as an invalid value. This can be a NULL pointer if the function should return a pointer, or a negative value if your function returns an index in an array, or, in the case of a custom class (e.g. your Entry class) you can define a special Entry::invalid value or something similar that can be detected by the calling function.
Your calling code could look like
if ( entryInstance->get_by_last_name("foobar") != Entry::invalid)
{
// here goes the code for the case where the name is valid
} else {
// here goes the code for the case where the name is invalid
}
On the other hand you can use the C++ exceptions mechanism and make your function throw an exception. For this youcan create your own exception class (or use one defined in the standard library, deriving from std::exception). Your function will throw the exception and your calling code will have to catch it with a try...catch statement.
try
{
entryInstance->get_by_last_name("foobar")
}
catch (Exception e)
{
// here goes the code for the case where the name is invalid
}
// here goes the code for the case where the name is valid
Apart from the fact that you could have more than one entry per surname.
Eliminate the getter, and you've solved the problem, or at least shifted it elsewhere.
Tell the AddressBook to display people with given surnames. If there aren't any it can do nothing.
AddressBookRenderer renderer;
AddressBook contacts;
contacts.renderSurnames("smith", renderer);
contacts.renderCompletions("sm", renderer);
//etc
You can do what std::map (and the other containers do).
You return an iterator from your search function.
If the search does not find a value that is useful return an iterator to end().
class AddressBook
{
typedef <Your Container Type> Container;
public:
typedef Container::iterator iterator;
iterator get_by_last_name(std::string const& lastName) {return addr_map.find[lastName];}
iterator end() {return addr_map.end();}
};
Your address book is a container like object.
Not finding an item in a search is likely to happen but it does not have enough context to incorporate error handling code (As the address book could be used from lots of places and each place would have different error handling ideas).
So you must move the test for not found state out of your address book.
just like "Python" we return a marker. In C++ this is usually an iterator to end() which the calling code can check and take the appropriate action.
AddressBook& ab = getAddressBookRef();
AddressBook::iterator find = ab.get_by_last_name("cpp_hobbyist");
if (find != ab.end())
{
Entity& person *find; // Here you have a reference to your entity.
// you can now manipulate as you want.
}
else
{
// Display appropriate error message
}