4

I have my comparison function to use in qsort() like this:

int compar(const void *p, const void *q){
    interval a,b;

    *p = (interval)a
    *q = (interval)b;

    if(a.extrem[1] < b.extrem[0])
        return -1;
    if(b.extrem[1] < a.extrem[0] )
        return 1;

    return 0;
}

My structure is as follows:

typedef struct interval{
    double extrem[2];
    } interval;

I've tried many variations of "casting" in function compar, which all failed. My question, as it is apparent, how can I cast a const void* to my struct element? I know it seems to be a very basic question but I could not find a clear answer anywhere, also I'm new to this. Any help is gladly appreciated.

Random Davis
  • 6,662
  • 4
  • 14
  • 24
l.arlev
  • 43
  • 1
  • 6
  • 4
    `a = *(interval*)p;` is probably what you meant. – Tavian Barnes May 26 '16 at 16:35
  • 1
    You can't cast to or from a `struct` type. You can cast `void*` to or from a pointer type. (In fact, the conversion can be done implicitly, without a cast.) – Keith Thompson May 26 '16 at 16:36
  • 2
    Why on earth would you do this? Discarding all type information in your arguments, then treating the data as a specific type is a horrible practice. If someone calls this with a pointer to a different type (nothing prevents anyone from doing so!) this will cause all kinds of problems. Why not just take const ref arguments of your actual struct type? – notadam May 26 '16 at 16:37
  • 2
    @adam10603 http://linux.die.net/man/3/qsort – Tavian Barnes May 26 '16 at 16:38
  • @TavianBarnes Yes, I know about functions like that, but that is not the same. You see, `qsort` takes an argument that specifies the size of your type, so it knows exactly how big an element is. And since it only cares about the bytes each element has, and not its type, this is totally fine. Whereas the function in question simply expects an essentially "typeless" pointer to point to be pointing to and object of an exact type, which is terrible. – notadam May 26 '16 at 16:40
  • @adam10603 `qsort` takes a function pointer `int (*compar)(const void *, const void *)`. It does not take a function pointer `int (*compar)(const interval *, const interval *)`. – Tavian Barnes May 26 '16 at 16:41
  • @adam10603 That's the `qsort()` generic callback interface, it's not his fault. :) – unwind May 26 '16 at 16:41
  • @TavianBarnes it worked, thanks. – l.arlev May 26 '16 at 16:42
  • @unwind Oh, it's a callback... I must admit, I completely missed the first line of the question, my bad :) I take that back then. I thought this was just a standalone function that the OP wrote, in which case `void*` really would have been bad practice. – notadam May 26 '16 at 16:42
  • Confident that OP's compare will not work `if(a.extrem[1] – chux - Reinstate Monica May 26 '16 at 16:51
  • 2
    @chux This appears to be an ordering on intervals that considers overlapping intervals to be equal. It is an invalid comparator for sorting because you can have intervals such that `compar(a, b) == 0 && compare(b, c) == 0` but `compar(a, c) == -1`. – Tavian Barnes May 26 '16 at 17:21

2 Answers2

4

You were close...

typedef struct interval {
  double extrem[2];
} interval;

int compar(const void *p, const void *q) {
  const interval *a = p, *b = q;

  if(a->extrem[1] < b->extrem[0])
    return -1;
  if(b->extrem[1] < a->extrem[0])
    return 1;
  return 0;
}

BTW, without a single cast, this will be perfectly clean under gcc -Wall.

Update... Tavian makes a good point, this is not a transitive ordering, so your set technically has no partial order. But depending on your data and your objective, qsort may return a useful result even so.

Community
  • 1
  • 1
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
1

The qsort() function is going to call your callback with pointers to the elements.

Assuming the array is an actual array of interval, not one of interval pointers, you'd do:

static int interval_compare(const void *a, const void *b)
{
  const interval *ia = a, *ib = b;

  if(ia->extrem[1] < ib->extrem[0]) return -1;
  return ib->extrem[1] > ia->extrem[0];
}

I don't understand the extrem indexes, but that's what you used. No casts are necessary when converting void * to a more specific type. See this answer.

Community
  • 1
  • 1
unwind
  • 391,730
  • 64
  • 469
  • 606