I am learning C++ at the moment and have an example program implemented with an array of objects data store. To make some other operations easier, I have changed the store to a vector. With this change I am now not sure of the best way to search the store to find an object based on a member accessor value.
Initially I used a simple loop:
vector<Composer> composers; // where Composer has a member function get_last_name() that returns a string
Composer& Database::get_composer(string last_name)
{
for (Composer& c : composers)
if (c.get_last_name().compare(last_name))
return c;
throw std::out_of_range("Composer not found");
}
This works just fine of course, but to experiment I wanted to see if there were vector specific functions that could also do the job. So far I have settled on trying to use find_if() (if there is a better function, please suggest).
However, I am not sure exactly the correct way to use find_if(). Based on code seen in online research I have replaced the above with the following:
vector<Composer> composers; // where Composer has a member function get_last_name() that returns a string
Composer& Database::get_composer(string last_name)
{
auto found = find_if(composers.begin(), composers.end(),
[last_name](Composer& c) -> bool {c.get_last_name().compare(last_name);});
if (found == composers.end())
throw out_of_range("Composer not found");
else
return *found;
}
This does not work. It does find a result, but it is the incorrect one. If an argument that matches, say the third composer's last name the function always returns the first item from the vector (if I pass an argument that doesn't match any last name the function correctly throws an exception)... what am I doing wrong?
You are on the right track, your lambda needs return statement. Also in such case you do not have to specify it's return type explicitly, it can be deduced:
find_if(composers.begin(), composers.end(),
[last_name](const Composer& c) { return c.get_last_name() == last_name);});
you original code should not compile or at least emit warning(s), you should pay attention to them.
Note: it is not clear how your original code worked if you tested it, it should be:
if (c.get_last_name().compare(last_name) == 0 )
or simply:
if (c.get_last_name() == last_name )
as std::string::compare() returns int -1 0 or 1, so your code searches for string that does not match variable last_name
With range-v3, you may use projection:
auto it = ranges::find(composers, last_name, &composers::get_last_name);
Related
inside Caleb Curry's C++ course I have stumbled upon a piece of code about classes with a function returning an index if found two objects with the same data members. Here's the function:
int indexuser(std::vector<User> &users, User user)
{
for(int i = 0; i < users.size(); i++)
{
if(users[i].fname == user.fname && users[i].lname == user.lname)
{
return i;
std::cout << "\n";
}
}
users.push_back(user);
return users.size()-1;
}
It seems to me that the function should return two indexes or more if found matching users, cause no matter what it should always return the last index of a vector. However the function returns only the first one it found. Can someone explain to me why does this happen? Thank you in advance.
The function checks if user(of type User) is present in the vector users. If found it will return index of the user found in vector and if not found, it will insert the user (usingusers.push_back(user)) in the vector and return its index (which will be users.size()-1).
And whenever (any) function finds return statement it returns to its calling statement terminating everything else.
If the function meets the return statement, it immediately returns to the code that called that function.
Subsequent code will not run.
In other words, the return command may exist several times in function, but at the first time the program meets return the function execution is finished, and only one value will be returned.
In c++ normal functions can return only 1 value. This value can be of any available type: an integer, its own structure or a standard container, or any other, but this value will be the only one. So the functions are simply executed until you execute the first operand return. As soon as they meet return, they return the corresponding value and complete their execution. Functions are absolutely indifferent to what happens after the first return, because they complete their execution. So in the function described below, only the number 42 will be returned. Moreover, the variable a will not be equal to the number 5, because the function ended before assign operator
int func(int &a)
{
return 42;
a = 5;
return 51;
return 1;
}
is there a way to implement bsearch() to find multiple instances of key.
for example: (obj*)bsearch(key=r,arr,elements,sizeof(obj),(int(*)(const void*, const void*)bcompare);
The code I currently wrote only finds the first instance and cannot proceed past the first found due to how it works.
getline(target,81);
if(strcmp(target,"exit") == 0 || strcmp(target, "") == 0) break;
p = (Info*)bsearch(target,list,num,sizeof(Info),(int(*)(const void*, const void*))bcompare);
int foundIndex = (int)(p-list);
if(!p){
err_dis1_win();
clrscr();
}
else{
display_record(p);
cout << "\n\n found at index " << foundIndex << "\n";
getch();
clrscr();
}
Variables:
p - is a pointer to object of class Info
target - arr of char
list - arr of obj
foundIndex - index of element found
Info - derived class from base class
**compare function
int bcompare(char *a,Info *b){
return(strncmpi(a, b -> get_name(), strlen(a)));
}
I cannot use other methods such as std::find or writing my own binary search function and have to use bsearch()
I have tried loops inside the else block, and the compare function using the varible foundIndex, as well as using a while loop on the return value looping through the obj list arr. Is there a way to start at a specific index. I appreciate any help. I am not looking for code but a general push in the right direction. Thank you.
Caveat - The current code compiles and runs as expected however, the functionality that I want, cannot be figured out by myself. Google and search on Stackoverflow has not produced an related issue.
Since bsearch() returns only one item, I interpret "find multiple instances of key" as "find the first instance of a key". The caller can then step forward through the array from that item to process each item matching the key, until it reaches the end or reaches an item that does not match.
If you must use the standard library's bsearch() function and persuade it to find the first item matching a given key, then all you really have to work with is the comparison function you present. bsearch() will return an item that matches the key according to that function, but if more than one item matches then there is no guarantee which one will be returned. You must ensure, then, that only the item you want matches.
You can approach that with an appropriate implementation of the comparison function, but there is a significant problem. The function will in some cases need to evaluate the item preceding the one specified to it, but it must not attempt to examine an item preceding the array's first. bsearch() does not itself convey any information about the array bounds to the comparison function.
There are at least two possible solutions, neither of them stellar.
Store the array lower bound in some well-known location that the function can access. For example, if the comparison function is a static member function, then maybe you would use a static variable of its class. But that is not thread-safe. You could do something similar with thread-local variables, but even then it's ugly. Either way, you have to be sure to set that variable appropriately before you call bsearch(), and that's ugly, too.
OR
Ensure that you never bsearch() for the first item. One way you could do that would be by checking preliminarily whether the first item matches (but not via the comparison function), and using it directly instead of calling bsearch() in the event that it does match. I'd wrap that in a method, myself, and if you must not do so then requiring that such a calling discipline be employed manually is also ugly.
Having chosen one of the above, you can implement a comparison function that looks at the previous item's key in addition to the specified item's. Something along these lines (which assumes the second alternative):
struct my_item {
int key;
void *data;
};
// bsearch() passes the target item as the first argument, and the one to compare
// to it as the second
int compare_items(const void *to_find, const void *to_check) {
const struct my_item *to_find_item = (const struct my_item *) to_find;
const struct my_item *to_check_item = (const struct my_item *) to_check;
// Check first how the key members are ordered
if (to_find_item->key < to_check_item->key) {
return -1;
} else if (to_find_item->key > to_check_item->key) {
return 1;
} else {
// The key members match, so check whether we're looking at the first
// such item.
const struct my_item *previous_item = to_check_item - 1;
// If the previous item's key does match, then we know the item we're
// looking for is an earlier one than we are presently checking.
return (previous_item->key == to_check_item->key) ? -1 : 0;
}
}
I am running a modified version of example provided at Cuckoo filter repository: https://github.com/efficient/cuckoofilter/blob/master/example/test.cc
I want to add strings to cuckoo filter. Although the string is added but when I check if it exists in the filter, it always returns false. Can anyone point out what's wrong with my approach?
size_t total_items = 1000000;
CuckooFilter<string, 12> filter(total_items);
// Insert items to this cuckoo filter
string temp1 = "sample";
if (filter.Add(temp1) != cuckoofilter::Ok) {
cout<<"not added"<<endl;
}
// Check if previously inserted items are in the filter
string temp2 = "sample";
assert(filter.Contain(temp2) == cuckoofilter::Ok);
The assertion should be true but it is false in this case. Why?
A quick glance at the source of https://github.com/efficient/cuckoofilter/blob/master/src/cuckoofilter.h#L65 reveals that it uses a function
inline void GenerateIndexTagHash(const ItemType &item, size_t* index, uint32_t* tag) const
{
std::string hashed_key = HashUtil::SHA1Hash(
(const char*) &item,
sizeof(item)
);
// ... rest is skipped for brevity
}
to generate an initial index and a fingerprint (tag) of the item. The problem is that it hashes an actual object passed. To simplify, it does this:
// Your filter.Add(temp1) inside does this
HashUtil::SHA1Hash((const char*) &temp1, sizeof(temp1));
// Your filter.Contain(temp2) inside does this
HashUtil::SHA1Hash((const char*) &temp2, sizeof(temp2));
Basically, it hashes two completely different objects which, as expected, generate different hashes and map to different buckets.
For it to work in your case it would need to call HashUtil::SHA1Hash() in a way that hashes the actual string data, that is:
// It should do something like this
HashUtil::SHA1Hash(
temp1.c_str(), // <-- notice we pass a pointer to an actual character data rather than a pointer to an instance of a std::string()
temp1.length()
);
HashUtil::SHA1Hash(
temp2.c_str(), // <-- notice we pass a pointer to an actual character data rather than a pointer to an instance of a std::string()
temp2.length()
);
This should answer your Why? question. As for the
Can anyone point out what's wrong with my approach?
There is nothing really wrong with your approach per-se it just doesn't work as you might've expected, because a library does not support such a use case.
Apologies in advanced if this is the wrong site, please let me know if it is!
I've written a function that checks to see whether a key exists in a particular std::map and wondered if this is a good practise to use, and, whether or not anyone can throw any pointers on improvements.
The std::map allows for multiple data-types to be accepted for the value.
union Variants {
int asInt;
char* asStr;
Variants(int in) { asInt = in; }
Variants() { asInt = 0;}
Variants(char* in) { asStr = in; }
operator int() { return asInt; }
operator char*() { return asStr; }
};
template<typename T, typename Y>
bool in_map(T value, std::map<T, Y> &map)
{
if(map.find(value) == map.end()) {
return false;
}else{
return true;
}
}
And I can then use in main the following:
std::map<string, Variants> attributes;
attributes["value1"] = 101;
attributes["value2"] = "Hello, world";
if(in_map<std::string, Variants>("value1", attributes))
{
std::cout << "Yes, exists!";
}
Any help or advise would be greatly appreciated. Sorry if this doesn't comply to the rules or standards. Thanks!
The biggest problem I see with your function is that you're throwing away the resulting iterator.
When you're checking if a key exists in a map, most of the time you want to retrieve/use the associated value after that. Using your function in that case forces you to do a double lookup, at the cost of performance. I would just avoid the use of the function altogether, and write the tests directly, keeping the iterator around for later use in order to avoid useless lookups:
auto it = map_object.find("key");
if (it != map_object.end())
use(it->second);
else
std::cout << "not found" << std::endl;
Of course if you're just checking whether a key exists and don't care for the associated value then your function is fine (taking into account what others told you in the comments) but I think its use cases are quite limited and not really worth the extra function. You could just do:
if (map_object.find("key") != map_object.end())
std::cout << "found, but I don't care about the value" << std::endl;
ny pointers on improvements.
sure.
template<typename T, typename Y>
bool in_map(T value, const std::map<T, Y> &map)
{
return map.find(value) != map.end();
}
And I'd place map as 1st parameter (just a preference). Also, because the whole thing fits into single line, you might not even need this function.
You're also throwing away returned iterator, but since you aren't using it, that's not a problem.
Apart from this, does this look ok in terms of coding practise? I.e. Using Union or are there other types I can use such as struct?
Well, using char* doesn't looke like a good idea, because char* implies that you can modify data. char* also implies that this pointer is dynamically allocated and you might want to delete[] that pointer later. And you can't use destructors in unions. If the text cannot be changed, you could use const char*, otherwise you might want to use different datatype. Also see Rule of Three
Next problem - you're trying to place char* and int at the same location. That implies that at some point you're trying to convert pointer to integer. Which is a bad idea, because on 64bit platform pointer might not fit into int, and you'll get only half of it.
Also, if you're trying to store multiple different values in the same variable, you are not indicating which type is being stored anywhere. To do that you would need to enclose union into struct and add field (into struct) that indicates type of stored object. In this case, however, you'll end up reinventing the wheel. So if you're trying to store "universal" type, you might want to look at Boost.Any, Boost.Variant or QVariant. All of those require BIG external libraries, though (either boost or Qt).
Typing
if(in_map<std::string, Variants>("value1", attributes))
seems a bit excessive to me, typing all of that typename syntax makes me want to just use the map.find function instead just out of convenience. However, depending on your compiler, sometimes the template parameters can be interpreted automatically, for example, visual studio will allow this:
if(in_map(std::string("value1"), attributes))
In this case, I had to construct an std::string object to replace the char*, but I've completely removed the template definition from the call, the compiler still figures out what T and Y are based on the parameters given.
However, my recommended advice would be to use #define to define your "function". While it is not really a function, since #define actually just replaces snippets of code directly into the source, it can make things much easier and visually appealing:
#define in_map(value,map) (map.find(value) != map.end())
Then your code to use it would just look like this:
if(in_map("value1", attributes))
You both get the optimization of not using a function call, and the visual appearance like it does in PHP.
if (find(visitable.begin(), visitable.end(), ourstack.returnTop())) { ... }
I want to determine whether the top character in stack ourstack can be found in the vector visitable. If yes, I want this character to be deleted from visitable.
How would I code that? I know vectors use erase, but that requires the specific location of that character (which I don't know).
This is for my maze-path-finding assignment.
Also, my returnTop is giving me an error: class "std.stack<char..." has no member returnTop. I declared #include in the top of my program. What's happening here?
Thanks in advance!
If you are using find, then you already know the location of the character. find returns an iterator to the position where the character is found, or to the value used as end if it cannot find it.
vector<?>::const_iterator iter =
find(visitable.begin(), visitable.end(), ourstack.top());
if( iter != visitable.end() )
{
visitable.erase( iter );
}
As for stack, the function you are looking for is top(). The standard C++ library does not use camelCased identifiers, that looks more like a Java or C# thing.
Just like this:
// Note assume C++0x notation for simplicity since I don't know the type of the template
auto character = ourstack.top();
auto iter = std::find(visitable.begin(), visitable.end(), character);
if (iter != visitable.end())
visitable.erase(iter);
returnTop does not exist in the stack class, but top does.
Alternatively if you want some generic (and rather flamboyant way) of doing it:
// Assume type of vector and stack are the same
template <class T>
void TryRemoveCharacter(std::vector<T>& visitable, const std::stack<T>& ourStack)
{
// Note, could have passed a ref to the character directly, which IMHO makes more sense
const T& ourChar = ourStack.top();
visitable.erase(std::remove_if(visitable.begin(), visitable.end(), [&ourChar](const T& character)
{
// Note, this will not work http://www.cplusplus.com/reference/algorithm/find/
// says that std::find uses the operator== for comparisons but I doubt that
// as compilers typically do not generate equal comparison operator.
// See http://stackoverflow.com/questions/217911/why-dont-c-compilers-define-operator-and-operator
// It's best to either overload the operator== to do a true comparison or
// add a comparison method and invoke it here.
return ourChar == character;
}));
}
Note: this alternative way may not be a good idea for an assignment as your teacher will probably find suspicious that you introduce advanced C++ features (C++0x) all of a sudden.
However for intellectual curiosity it could work ;)
Here's how you may use it:
TryRemoveCharacter(visitable, ourstack);