Today I was writing some C code to sort an array of structs using quicksort with a custom comparator function to determine their ordering.
At first I wrote it with the call to the comparator function hard-coded into the quicksort function. Then I thought perhaps it would be nicer to pass that function as an argument to a generic quicksort function.
In my original code, I had declared the comparator function inline. In my new code, I kept the inline declaration, even though that didn’t really make much sense to me, given that the function was being passed as a parameter. However, the compiler didn’t complain!
My question is: is the inline declaration having any effect here, or is it merely a recommendation to the compiler which is being ignored?
Original code:
typedef struct _CGRect {
CGPoint origin;
CGSize size;
} CGRect;
typedef enum _NSComparisonResult {
NSOrderedAscending = -1,
NSOrderedSame,
NSOrderedDescending
} NSComparisonResult;
static inline NSComparisonResult CGRectCompareRowsFirst(CGRect r1, CGRect r2)
{
if (r1.origin.y < r2.origin.y)
return NSOrderedAscending;
else if (r1.origin.y > r2.origin.y)
return NSOrderedDescending;
else
{
if (r1.origin.x < r2.origin.x)
return NSOrderedAscending;
else if (r1.origin.x > r2.origin.x)
return NSOrderedDescending;
else
return NSOrderedSame;
}
}
static void CGRectQuicksortRowsFirst(CGRect *left, CGRect *right)
{
if (right > left) {
CGRect pivot = left[(right-left)/2];
CGRect *r = right, *l = left;
do {
while (CGRectCompareRowsFirst(*l, pivot) == NSOrderedAscending) l++;
while (CGRectCompareRowsFirst(*r, pivot) == NSOrderedDescending) r--;
if (l <= r) {
CGRect t = *l;
*l++ = *r;
*r-- = t;
}
} while (l <= r);
CGRectQuicksortRowsFirst(left, r);
CGRectQuicksortRowsFirst(l, right);
}
}
static void CGRectSortRowsFirst(CGRect *array, int length)
{
CGRectQuicksortRowsFirst(array, array+length-1);
}
New code:
static inline NSComparisonResult CGRectCompareRowsFirst(const void *s1, const void *s2)
{
CGRect r1 = *(CGRect *)s1, r2 = *(CGRect *)s2;
if (r1.origin.y < r2.origin.y)
return NSOrderedAscending;
else if (r1.origin.y > r2.origin.y)
return NSOrderedDescending;
else
{
if (r1.origin.x < r2.origin.x)
return NSOrderedAscending;
else if (r1.origin.x > r2.origin.x)
return NSOrderedDescending;
else
return NSOrderedSame;
}
}
static void quick(CGRect *left, CGRect *right, NSComparisonResult(*f)(const void *, const void *))
{
if (right > left) {
CGRect pivot = left[(right-left)/2];
CGRect *r = right, *l = left;
do {
while (f(&*l, &pivot) == NSOrderedAscending) l++;
while (f(&*r, &pivot) == NSOrderedDescending) r--;
if (l <= r) {
CGRect t = *l;
*l++ = *r;
*r-- = t;
}
} while (l <= r);
quick(left, r, f);
quick(l, right, f);
}
}
static void CGRectSortRowsFirst(CGRect *array, int length)
{
quick(array, array+length-1, CGRectCompareRowsFirst);
}
Inline is just a recommendation to the compiler, and can be ignored. This can happen for several reasons, such as if the function is too complex to be safely inlined. If you pass it as a parameter to a function like above, the compiler will create the non-inlined version whose address will be passed to the function.
It may be possible that the compiler could still inline the function – for instance, during code generation the compiler could utilize the inline function hint replacing the call via a function pointer to just the expanded function; I am not sure if any current compiler would do that.
Inlined and non-inlined versions can and often do co-exist within one compiled program.