1

I have a struct like that :

struct Car {
    int weight;
    int price;
    int speed;
    etc..}

I want to write a function to sort array of this kind of structs.

void sortCars(struct Car table[], int tableSize, ?structMemberParameter?)
{
    struct Car temp;
    int swapped;
    for (int i = 0; i < tableSize; i++)
    {
        swapped = 0;
        for (int j = 0; j < tableSize - 1; j++) {
            if (table[j].?structMemberParameter? > table[j + 1].?structMemberParameter?) {
                temp = table[j + 1];
                table[j + 1] = table[j];
                table[j] = temp;
                swapped = 1;
            }           
        }
        if (swapped == 0) {
            break;
        }
    }
}

What should I put as "?structMemberParameter?"?

2
  • size_t by offsetof Commented Nov 22, 2016 at 21:54
  • As you've described the problem, there is no solution. If you relax constraints you've imposed, there are solutions. One is to use separate comparators, as described by iharob. Another is to write a macro instead of a function - with the downside that such a macro is not particularly safe, and incorrect usage will give really confusing error messages from the compiler. I'd stick with the separate comparators. Commented Nov 22, 2016 at 22:28

1 Answer 1

2

Writing a comparison function that compares such member from two structs and using qsort(). You can see the example in the qsort(3) manual.

This is the answer to the title of the question, the answer to your question is you don't need to pass the member but the comparison function just like qsort() does.

So the if statement becomes

if (compare(&table[j], &table[j + 1]) > 0) ...

and the function signature

typedef int (*cmpfn_type)(const struct Car *const, const struct Car *const);
void sortCars(struct Car table[], int tableSize, cmpfn_type compare);

So you can have multiple comparison functions, for example let's say you want to compare by price, the appropriate one would be

int compare_by_price(const struct Car *const A, const struct Car *const B)
{
    return (int) (A->price - B->price);
}

you should be careful with the type casting though.

The good thing of this approach, is that you can easily rewrite the comparison function to use the qsort() library function.

NOTE: Answering to your comment/question in the comments you could also have an enumerator, like

enum CarProperties {
    Speed, Price // ... AND SO ON
};

and then a sort function like

void sort_car_array(struct Car *const array,
        size_t size, enum CarProperties sort_property);

where you have a switch statement to choose the appropriate callback to pass to a generic sort like say, generic_car_array_sort() like the suggested above or even, directly to qsort() which would be a better solution.

If your intention is to have an API for the struct Car structure you can do it like this and I think it's a very elegant solution. I also recommend making the struct opaque and allowing the caller only to set/get the values via functions.

Sign up to request clarification or add additional context in comments.

3 Comments

I can write comparator for every member, but can I write universal sorting function that will always work, sortCars(struct , Size, speed), sortCars(struct , Size, price) etc..? Without the need for new comparators, all members can be ints, so all comparators will be the same, differs only in member name.
@PawełDymowski: you can consider a 'universal comparator' function, but you'll probably need to use the offset (via the offsetof macro from <stddef.h>) and the length and the type of the data because you've probably got some character strings as well as a set of integers (and maybe some floating point numbers, such as the for the fuel consumption or the size of the fuel tank), and handling all those cleanly in a single function is moderately fiddly.
@PawełDymowski if you want to have all members of the same type accessible with a selector computed at run-time, use an array. enum CarProperties{Speed, Price, ..., NumProperties}; struct Car { int properties[NumProperties]; };

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.