c++qsort

Is there a way to avoid having global variable to implement qsort


I have a project with a single .h file and multiple .cpp files. The header file contains a namespace UF (abbreviation for useful functions) that currently implements sorting.

This is done by having a comparator defined in UF.cpp thus:

    int compar_int_asc(const void *a, const void *b)
    {
        int aa = *((int *)a), bb = *((int *)b);
        if (base_arr_int[aa] < base_arr_int[bb])
            return -1;
        if (base_arr_int[aa] == base_arr_int[bb])
            return 0;
        if (base_arr_int[aa] > base_arr_int[bb])
            return 1;
    }

At present, the base array base_arr_int that needs to be accessed by qsort and the comparator function above is declared in main.cpp and externed in UF.cpp.

I access qsort within a different class, SEP as follows. Firstly, in SEP.cpp, I extern base_arr_int. Then, if ratios[100] is an integer array that is native and local to SEP, I do the following within SEP.cpp.

base_arr_int = ratios;
qsort(indices, 100, sizeof(int), UF::compar_int_asc);

Is this the best way of implementing qsort with multiple classes?

In particular, I would like to avoid using global variables defined in main.cpp as much as possible. Is there any alternative design?


Solution

  • The purpose of the global variable is to figuratively place the array inside the custom comparator. To eliminate the global variable, let's literally put ratio into the custom comparator. To do so, the custom comparator cannot be an ordinary function or function pointer. It needs to be a function object. And std::sort supports that.

    Let's do it step by step.


    So, you have an array that stores things.

    int ratio[5] = {300, 400, 200, 500, 100};
    

    But you don't want to sort it directly. You create an array of indices which actually gets sorted.

    int index[5] = {0, 1, 2, 3, 4};
    

    The goal is to sort index. So let's write:

    std::sort(index, index + 5);
    

    But it is not what you want yet. You also need to pass a custom comparator index_comp because the default less-than comparator is not what you need.

    std::sort(index, index + 5, index_comp);
    

    The rest of the work is how to write index_comp. It is actually quite simple: lambda expression

    auto index_comp = [&ratio](int index_left, int index_right) { return ratio[index_left] < ratio[index_right]; };
    

    This lambda expression captures the array ratio by reference ([&ratio]). It has a parameter list taking two indices. The body compares the two actual objects in ratio.

    If you prefer the old school way, the lambda expression is just syntax sugar for the following:

    class Compiler_Generated_Name
    {
    private:
        int (&ratio)[5];
    
    public:
        Compiler_Generated_Name(int (&ratio_)[5]) : ratio(ratio_) {}
    
        bool operator()(int index_left, int index_right)
        {
            return ratio[index_left] < ratio[index_right];
        }
    };
    
    Compiler_Generated_Name index_comp(ratio);
    

    The whole code:

    #include <iostream>
    #include <algorithm>
    
    int main()
    {
        int ratio[5] = {300, 400, 200, 500, 100};
        int index[5] = {0, 1, 2, 3, 4};
        
        auto index_comp = [&ratio](int index_left, int index_right) { return ratio[index_left] < ratio[index_right]; };
        
        std::sort(index, index + 5, index_comp);
        
        for (int i = 0; i < 5; ++i)
            std::cout << ratio[index[i]] << ' ';
    }