Search code examples
cgccclangnested-function

Portable nested functions in C


Is it possible to write portable C code using nested functions/blocks?

I understand that gcc only supports nested functions as an non-standard extension, and clang only supports blocks - but is there a way to write code that will compile on both using standard C with MACROS?

If it is not possible - what is the best work around? As an example, how would one implement a portable version of the following sort that takes a parameter? Trivial example in GCC:

int main(int argc, char*[] argv)
{
  char reverse = 0;

  int cmp_func(const void *a, const void *b)
  {
    const int* aa = (const int)a;
    const int* bb = (const int)b;
    return (reverse) ? aa - bb : bb - aa;
  }

  int list[8] = {1,2,3,4,5,20,100,200};
  qsort(list, 8, sizeof(int), &cmp_func);
}

A similar example could be put together using Blocks in Clang. Ideally the solution should be thread-safe (so avoid global variables).

Edit: For clarity, lets assume "standard" means C99. The above is a trivial example. What I'm after is a C99 approach to a sort that requires some parameters. Here it just uses a char as a boolean, but I'm after a solution that would take multiple integers etc. It looks like this might not be possible without global variables.

Edit 2: I realised that passing a void pointer along with a function pointer enables you to do everything that can be done with nested functions. Thanks to @Quuxplusone for suggesting qsort_r and qsort_s. I've tried to put together a portable wrapper on qsort_r and qsort_s. It takes a comparator function and a void pointer to store state in, thus removing the dependency on nested functions for intricate sorting algorithms -- so you can compile with both GCC and Clang.

typedef struct
{
  void *arg;
  int (*compar)(const void *a1, const void *a2, void *aarg);
} SortStruct;

int cmp_switch(void *s, const void *aa, const void *bb)
{
  SortStruct *ss = (SortStruct*)s;
  return (ss->compar)(aa, bb, ss->arg);
}

void sort_r(void *base, size_t nel, size_t width,
            int (*compar)(const void *a1, const void *a2, void *aarg), void *arg)
{
  #if (defined _GNU_SOURCE || defined __GNU__ || defined __linux__)

    qsort_r(base, nel, width, compar, arg);

  #elif (defined __APPLE__ || defined __MACH__ || defined __DARWIN__ || \
         defined __FREEBSD__ || defined __BSD__ || \
         defined OpenBSD3_1 || defined OpenBSD3_9)

    SortStruct tmp = {arg, compar};
    qsort_r(base, nel, width, &tmp, &cmp_switch);

  #elif (defined _WIN32 || defined _WIN64 || defined __WINDOWS__)

    SortStruct tmp = {arg, compar};
    qsort_s(*base, nel, width, &cmp_switch, &tmp);

  #else
    #error Cannot detect operating system
  #endif
}

Note: I haven't tested this on many platforms, so please let me know if you see a bug / this doesn't work on your machine.

As an example of usage, I've implemented the same sort as in the chosen answer:

int sort_r_cmp(const void *aa, const void *bb, void *arg)
{
  const int *a = aa, *b = bb, *p = arg;
  int cmp = *a - *b;
  int inv_start = p[0], inv_end = p[1];
  char norm = (*a < inv_start || *a > inv_end || *b < inv_start || *b > inv_end);

  return norm ? cmp : -cmp;
}

int arr[18] = {1, 5, 28, 4, 3, 2, 10, 20, 18, 25, 21, 29, 34, 35, 14, 100, 27, 19};
int p[] = {20, 30};
sort_r(arr, 18, sizeof(int), sort_r_cmp, p);

Solution

  • Following the suggestion by @Kirilenko here, I've come up with a solution using global variables and a mutex to pass parameters to a sort comparator function. This approach is thread-safe, can do everything achieved with nested functions and should be portable between compilers.

    This example sorts a list of integers, but inverts the sort for a given region.

    // define lock for sort parameters
    pthread_mutex_t lock;
    
    // Parameters used in sort function - invert region (inclusive)
    int invert_start, invert_end;
    
    // Comparison that uses global variables (invert_start, invert_end) as parameters
    int cmp_func(const void *a, const void *b)
    {
      const int aa = *(const int*)a;
      const int bb = *(const int*)b;
    
      if(aa < invert_start || aa > invert_end ||
         bb < invert_start || bb > invert_end)
      {
        return aa - bb;
      }
      else
      {
        return bb - aa;
      }
    }
    
    void sort_things(int* arr, int arr_len, int inv_start, int inv_end)
    {
      // Mutex lock
      pthread_mutex_lock(&lock);
    
      // Set params
      invert_start = inv_start;
      invert_end = inv_end;
    
      // do sort
      qsort(arr, arr_len, sizeof(*arr), &cmp_func);
    
      // Mutex free
      pthread_mutex_unlock(&lock);
    }
    

    Example results:

    input: 1 5 28 4 3 2 10 20 18 25 21 29 34 35 14 100 27 19
    invert_start = 20, invert_end = 30
    output: 1 2 3 4 5 10 14 18 19 29 28 27 25 21 20 34 35 100