C中的可移植嵌套函数

时间:2012-08-31 11:55:00

标签: c gcc clang nested-function

是否可以使用嵌套函数/块编写可移植C代码?

我知道gcc只支持嵌套函数作为非标准扩展,而clang只支持块 - 但有没有办法编写可以使用标准C和MACROS编译的代码?

如果不可能 - 最好的解决方法是什么?举个例子,如何实现一个带参数的以下类型的可移植版本?海湾合作委员会的简单例子:

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);
}

类似的例子可以使用Clang中的Blocks组合在一起。理想情况下,解决方案应该是线程安全的(因此请避免使用全局变量)。

编辑:为清楚起见,我们假设“标准”表示C99。以上是一个简单的例子。我所追求的是一种需要一些参数的C99方法。这里它只使用一个char作为布尔值,但我正在使用一个可以采用多个整数等的解决方案。看起来如果没有全局变量,这可能是不可能的。

编辑2:我意识到将void指针与函数指针一起传递使您可以执行可以使用嵌套函数完成的所有操作。感谢@Quuxplusone建议qsort_rqsort_s。我试图在qsort_rqsort_s上放置一个便携式包装器。它需要一个比较器函数和一个用于存储状态的void指针,从而消除了对复杂排序算法的嵌套函数的依赖性 - 因此您可以使用GCC和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
}

注意:我没有在很多平台上测试过这个问题,所以如果你看到一个bug /这在你的机器上不起作用,请告诉我。

作为一个使用示例,我实现了与所选答案相同的排序:

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);

5 个答案:

答案 0 :(得分:7)

只是为了好玩(并回答原始问题),是的,完全可以使用宏系统在符合标准的C99中编写嵌套函数来“解开”代码的嵌套版本。以下是一种可能的实现方式:https://github.com/Leushenko/C99-Lambda

有了它,你可以写下这样的可憎之物:

typedef int(* fptr)(int);
func(fptr, someFunc, (void) {
    return fn(int, (int a), {
        fptr f = fn(int, (int b), { return b * 6; });
        return a * f(a + 1);
    });
})

让我们非常明确一些事情:这是用C语言编写这种代码的绝对最差方式。如果你发现自己处于某个位置实际上需要使用宏库来编写这样的代码,退出作为程序员的工作并成为农民。在生产中使用它,你的同事可能会在你的睡眠中谋杀你。

另外,非常搞笑,尽管它在技术上符合标准,但是只有能够处理许多宏的绝对重量的预处理器的唯一编译器仍然是GCC和Clang。

答案 1 :(得分:4)

没有可移植的方法在C中编写嵌套函数只是因为C标准不允许嵌套函数 宏在这里对你没什么帮助,因为它们是由预处理器评估的,编译器仍会看到嵌套函数的代码并标记错误。

答案 2 :(得分:1)

根据@Kirilenko here的建议,我提出了一个使用全局变量和互斥锁将参数传递给排序比较器函数的解决方案。这种方法是线程安全的,可以使用嵌套函数实现所有功能,并且可以在编译器之间移植。

此示例对整数列表进行排序,但反转给定区域的排序。

// define lock for sort parameters
pthread_mutex_t lock;

// Parameters used in sort funciton - invert region (inclusive)
int invert_start, invert_end;

// Comparitor that uses global variables (invert_start, invert_end) as paramaters
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);
}

示例结果:

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

答案 3 :(得分:0)

嵌套函数不在C标准中,但它是gcc扩展名(当标志-fnested-functions处于活动状态时)。此外,您可以使用静态函数(cmp_func)和额外参数(reverse)来执行相同的操作。

答案 4 :(得分:0)

为什么要去嵌套函数和全局变量的麻烦? 100%便携式(甚至是K&amp; R)解决方案只是使用两种不同的功能,一种是按正常顺序排序,另一种是反向排序,然后将其称为

qsort(list, 8, sizeof(int), reverse ? cmp_func_reverse : cmp_func);

注意:无需使用&的函数地址。