c++bsearch

Getting multiple Instances of key using bsearch()


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:

**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.


Solution

  • 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.

    1. 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

    1. 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;
        }
    }