Search code examples
arrayscsortingvoid-pointers

Sorting any data type array with void pointer?


I am sorting an array of any data type (in ascending order) with the function which takes arguments as *void pointer, int size, and int data_type. I am able to sort the array with the help of the following function, but the problem is, there is so much repetition of code. In the if blocks repeating the following code four times in the same function, which is not a good approach in programming, and also it is against in DRY(don't repeat yourself) saying.

         int temp = 0;
       
        //Sort the char_array in ascending order
        for (int i = 0; i < size; i++)
        {
            for (int j = i + 1; j < size; j++)
            {
                if (char_arr[i] > char_arr[j])
                {
                    temp = char_arr[i];
                    char_arr[i] = char_arr[j];
                    char_arr[j] = temp;
                }
            }
        }

Complete code is below

// ! sorting funtion for all data types
void sort_any_data_type(void *arr, int size, int data_type)
{
    // ! data_type 1 = int,data_type 2 = float,data_type 3 = double,data_type 4 = char

    if (data_type == 1)
    {   
        int *int_arr = (int *)arr;
        int temp = 0;
        // sort()
        //Sort the int_array in ascending order
        for (int i = 0; i < size; i++)
        {
            for (int j = i + 1; j < size; j++)
            {
                if (int_arr[i] > int_arr[j])
                {
                    temp = int_arr[i];
                    int_arr[i] = int_arr[j];
                    int_arr[j] = temp;
                }
            }
        }
    }
    if (data_type == 2)
    {
        float *float_arr = (float *)arr;
        int temp = 0;
        // sort()
        //Sort the float_array in ascending order
        for (int i = 0; i < size; i++)
        {
            for (int j = i + 1; j < size; j++)
            {
                if (float_arr[i] > float_arr[j])
                {
                    temp = float_arr[i];
                    float_arr[i] = float_arr[j];
                    float_arr[j] = temp;
                }
            }
        }
    }
    if (data_type == 3)
    {
        double *double_arr = (double *)arr;
        int temp = 0;
        // sort()
        //Sort the double_array in ascending order
        for (int i = 0; i < size; i++)
        {
            for (int j = i + 1; j < size; j++)
            {
                if (double_arr[i] > double_arr[j])
                {
                    temp = double_arr[i];
                    double_arr[i] = double_arr[j];
                    double_arr[j] = temp;
                }
            }
        }
    }
    if (data_type == 4)
    {
        char *char_arr = (char *)arr;
        int temp = 0;
        // sort()
        //Sort the char_array in ascending order
        for (int i = 0; i < size; i++)
        {
            for (int j = i + 1; j < size; j++)
            {
                if (char_arr[i] > char_arr[j])
                {
                    temp = char_arr[i];
                    char_arr[i] = char_arr[j];
                    char_arr[j] = temp;
                }
            }
        }
    }
}

What I want is looks like below. Or something equivalent that solves my problem

For one week trying to solve it, I did so much research but unfortunately couldn't get a proper solution. Thank you in advance.

void sort_any_data_type(void *arr, int size, int data_type)
{
    int *array = (int*)arr;
    switch (data_type)
    {
    case 1:
        array = (int *)arr;
        break;
    case 2:
        array = (float *)arr;
        break;
    case 3:
        array = (double *)arr;
        break;
    case 4:
        array = (char *)arr;
    default:
        break;
    }
    int temp = 0;
  
    for (int i = 0; i < size; i++)
    {
        for (int j = i + 1; j < size; j++)
        {
            if (array[i] > array[j])
            {
                temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
    }
}

Solution

  • The approach will not work in C because types must be defined. C lacks support for C++-like templates. There are two practiced approaches for construction of generic algorithms.

    1. Using void*
    2. Using macros

    Approach 1. is used in standard function qsort(). The manual is easily available. Usually it results in less efficient code especially for types of small size (like char or int).

    Approach 2. Use a macro to generate a function that will sort data of specific type:

    #define DEFINE_SORT(FNAME, TYPE) \
    void FNAME(TYPE* array, int size) { \
        TYPE temp = 0;    \
        for (int i = 0; i < size; i++)  { \
            for (int j = i + 1; j < size; j++) { \
                if (array[i] > array[j]) { \
                    temp = array[i]; \
                    array[i] = array[j]; \
                    array[j] = temp; \
                } \
            } \
        } \
    }
    

    Instantiate it for relevant types.

    DEFINE_SORT(sort_char, char)
    DEFINE_SORT(sort_int, int)
    DEFINE_SORT(sort_float, float)
    DEFINE_SORT(sort_double, double)
    

    And finally dispatch it in sort_any_data_type()

    void sort_any_data_type(void *arr, int size, int data_type) {
        if (data_type == 1) sort_int(arr, size);
        if (data_type == 2) sort_float(arr, size);
        if (data_type == 3) sort_double(arr, size);
        if (data_type == 4) sort_char(arr, size);
    }
    

    This dispatcher takes advantage from automatic conversion between void* and other pointer types.

    However working with void* and magic numbers to identify types is cumbersome and error prone.

    Let me propose a superior (IMO) solution.

    In C11 the Generic Selection was introduced what allows to dispatch expression by their type

    #define SORT(array, size) \
      _Generic((array),       \
        int*: sort_int,       \
        float*: sort_float,   \
        double*: sort_double, \
        char*: sort_char      \
      )(array, size)
    
    

    The _Generic is used to select a proper function pointer to the sorting function.

    Example usage:

    int main() {
        int iarr[] = {3, 2, 5};
        SORT(iarr, 3);
        for (int i = 0; i < 3; ++i) printf("%d ", iarr[i]);
        puts("");
    
        float farr[] = {3.1, 2.2, 5.6, -12};
        SORT(farr, 4);
        for (int i = 0; i < 4; ++i) printf("%f ", farr[i]);
        puts("");
    
        char carr[] = "hello world";
        SORT(carr, sizeof carr - 1);
        puts(carr);
    }
    

    prints:

    2 3 5 
    -12.000000 2.200000 3.100000 5.600000
     dehllloorw