用C便携式嵌套函数(Portable nested functions in C)

2019-09-22 00:59发布

是否有可能编写出使用嵌套函数/块移植的C代码?

据我所知,只有GCC支持嵌套函数作为非标准扩展,并铛只支持块 - 但有没有编写代码,将编译与宏都用标准C的方法吗?

如果它是不可能的 - 什么是各地最好的工作? 举个例子,一个将如何实现以下那种需要一个参数的一个便携版本? 在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);
}

类似的例子可以在使用锵块放在一起。 理想情况下,解决方案应该是线程安全的(这样可以避免全局变量)。

编辑:为清楚起见,让我们假设“标准”是指C99。 上面是一个简单的例子。 我后是C99的方法来排序,需要一些参数。 这只是使用一个字符作为一个布尔值,但会采取多个整数等它看起来像这样没有全局变量可能无法解决后我。

编辑2:我意识到,传递一个空指针用一个函数指针一起让你这样做可以嵌套函数来所做的一切。 由于@Quuxplusone的建议qsort_rqsort_s 。 我试图把一个便携式包装上qsort_rqsort_s 。 它需要一个比较器功能和一个空指针存储在状态,从而对嵌套函数去除依赖于复杂的排序算法 - 因此,您可以GCC和锵编译。

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

Answer 1:

只是为了好玩(和回答原来的问题),是的,它是完全可以编写使用宏系统“排解”你的代码的嵌套版本符合标准的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是锵和反正。



Answer 2:

还有就是C编写嵌套函数仅仅是因为C标准不允许嵌套函数没有可移植的方法。
因为它们是由预处理器宏的评价不会帮助你在这里多,编译器将仍然可以看到它的巢的功能和标志错误的代码。



Answer 3:

继@Kirilenko的建议在这里 ,我已经想出了使用全局变量和互斥来传递参数排序比较器功能的解决方案。 这种方法是线程安全的,可以做嵌套函数实现的一切,应该是编译器之间移植。

这个例子排序整数列表,但是反转的排序为给定的区域。

// 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


Answer 4:

嵌套函数不在C标准,但它是一个gcc延伸(当标记-fnested-functions已在工作)。 此外,您还可以使用静态函数( cmp_func )和一个额外的参数( reverse )做同样的事情。



Answer 5:

为什么要在所有嵌套函数和全局的麻烦? 100%便携式(甚至到K&R)溶液是简单地使用两个不同的功能,一个有规则的次序进行排序,其他反向,然后调用它作为

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

注:没有必要采取一个函数的地址与&



文章来源: Portable nested functions in C