Question

I have a function that sorts a list of pointers to structures (implementation omitted):

void sort_by(struct thing **start, int size,
    int (*cmp_by)(struct thing *, struct thing *));

This function allows me to pass in a comparison function cmp_by that takes two structures and returns 1, 0, -1 as appropriate. Then there exists this (GCC-only!) function:

void reverse_by(struct thing **start, int size,
        int (*cmp_by)(struct thing *, struct thing *)) {

    int cmp_reverse(struct thing *a, struct thing *b) {
        return -cmp_by(a, b);
    }

    sort_by(start, size, cmp_reverse);
}

This function, takes the same arguments as sort_by but reverses the sort order by flipping the sign of the result of comparison. GCC has this extension to allow nested functions. How should reverse_by be written portably? Even my attempts at getting this to work come nowhere near to compiling:

int (*reverse(int (*fn)(struct thing *, struct thing *)))(struct thing *, struct thing *) {
    return -fn;  /* OBVIOUSLY AND CONCEPTUALLY WRONG */
}
int (*reverse(int (*fn)(struct thing *a, struct thing *b)))(struct thing *x, struct thing *y) {
    return -fn(a, b);  /* WRONG RETURN TYPE WRONG WRONG */
}
int (*reverse(int (*fn)(struct thing *a, struct thing *b)))(struct thing *x, struct thing *y) {
    return -fn(x, y);  /* YOU HAVE NO IDEA WHAT YOU'RE DOING STOP GUESSING */
}

Short of adding a lot of extra structure parts or rewriting swaths of code calling reverse_by (or copy-pasting sort_by code into it), how can this be rewritten to eliminate the nested function? It would be good to avoid copy/paste as a solution because this code base involves this nested-function-pattern elsewhere.

Was it helpful?

Solution

Re-entrant but not thread-safe solution:

typedef int (*cmp_func)(struct thing *, struct thing *);

static cmp_func reverse_cmp_by_impl;

static int cmp_reverse(struct thing *a, struct thing *b) {
    int ret;
    cmp_func impl = reverse_cmp_by_impl;
    ret = -impl(a, b);
    reverse_cmp_by_impl = impl; // For re-entrancy
    return ret;
}

void reverse_by(struct thing **start, int size, cmp_func cmp_by) {
    reverse_cmp_by_impl = cmp_by;
    sort_by(start, size, cmp_reverse);
}

Note that this is assuming a sane implementation of sort_by. i.e. one that doesn't call reverse_by in any way except potentially through the comparison function.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top