gcc C中的可移植嵌套函数

xv8emn3q  于 6个月前  发布在  其他
关注(0)|答案(5)|浏览(54)

是否可以使用嵌套函数/块编写可移植的C代码?
我知道gcc只支持嵌套函数作为非标准扩展,clang只支持块-但有没有一种方法可以使用标准C和MACROS编写代码,在两者上编译?
如果这是不可能的-最好的解决方法是什么?作为一个例子,如何实现一个接受参数的以下排序的可移植版本?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);
}

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

**编辑:**为了清楚起见,让我们假设“标准”是指C99。上面是一个简单的例子。我所追求的是一个C99的排序方法,需要一些参数。这里它只是使用一个char作为布尔值,但我追求的解决方案将采取多个整数等。看起来没有全局变量这可能是不可能的。
**编辑2:**我意识到传递一个空指针沿着一个函数指针可以让你做所有可以用嵌套函数做的事情。感谢@Quuxplusone推荐qsort_rqsort_s。我试着在qsort_rqsort_s上组装一个可移植的 Package 器。它需要一个比较器函数和一个空指针来存储状态,从而消除了复杂排序算法对嵌套函数的依赖--所以你可以用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);

bogh5gae

bogh5gae1#

只是为了好玩(也是为了回答最初的问题),是的,在符合标准的C99中编写嵌套函数是完全可能的,使用宏系统来“解开”代码的嵌套版本。
有了它,你可以写这样的可憎的东西:

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才有预处理器可以处理这么多宏的绝对重量。

7cwmlq89

7cwmlq892#

没有可移植的方法来编写C中的嵌套函数,因为C标准不允许嵌套函数。
宏在这里帮不上什么忙,因为它们是由预处理器计算的,编译器仍然会看到嵌套函数和标记错误的代码。

1wnzp6jl

1wnzp6jl3#

100%可移植(甚至对K&R)的解决方案就是简单地使用两个不同的函数,一个按常规顺序排序,另一个按相反顺序排序,然后将其调用为

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

字符串
注意:不需要使用&的函数的地址。

o8x7eapl

o8x7eapl4#

根据@Kirilenko的建议,我提出了一个解决方案,使用全局变量和互斥锁将参数传递给排序比较器函数。这种方法是线程安全的,可以完成嵌套函数实现的所有功能,并且应该在编译器之间可移植。
此示例对整数列表进行排序,但对给定区域反转排序。

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

字符串
示例结果:

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

pobjuy32

pobjuy325#

嵌套函数不在C标准中,但它是一个gcc扩展(当标志-fnested-functions被激活时)。此外,您可以使用静态函数(cmp_func)和额外参数(reverse)来做同样的事情。

相关问题