C中的可移植嵌套函数

Isa*_*ner 5 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);
}
Run Code Online (Sandbox Code Playgroud)

可以使用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
}
Run Code Online (Sandbox Code Playgroud)

注意:我没有在很多平台上测试过这个,所以如果你看到一个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);
Run Code Online (Sandbox Code Playgroud)

Leu*_*nko 8

只是为了好玩(并回答原始问题),是的,完全可以使用宏系统在符合标准的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);
    });
})
Run Code Online (Sandbox Code Playgroud)

我们是非常有关的东西,虽然清楚:这是绝对最差写这种代码在C,如果你发现自己在你真正需要使用宏库来编写代码,这样的位置的方式,辞掉工作作为一个程序员,成为一个农民.在生产中使用它,你的同事可能会在你的睡眠中谋杀你.

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

  • 很好的答案,虽然我认为如果你觉得有必要在预处理方面走得这么远,你可能最好不要再编写一个完整的源到源的翻译器,而不再使用你的语言C. (2认同)

Isa*_*ner 1

按照@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);
}
Run Code Online (Sandbox Code Playgroud)

结果示例:

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
Run Code Online (Sandbox Code Playgroud)