Переносные вложенные функции в C

Можно ли написать переносимый 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); } 

Аналогичный пример можно собрать, используя Blocks в Clang. В идеале решение должно быть streamобезопасным (так что избегайте глобальных переменных).

Изменить: для наглядности допустим, что «стандартный» означает C99. Вышеприведенный пример является тривиальным. То, что мне нужно, – это подход C99 к сортировке, требующий некоторых параметров. Здесь он просто использует char как логическое, но я после решения, которое займет несколько целых чисел и т. Д. Похоже, что это может быть невозможно без глобальных переменных.

Edit 2: я понял, что передача указателя void вместе с указателем функции позволяет делать все, что можно сделать с помощью вложенных функций. Благодаря @Quuxplusone для предложения qsort_r и qsort_s . Я попытался собрать переносимую оболочку на qsort_r и qsort_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 } 

Примечание. Я не тестировал это на многих платформах, поэтому, пожалуйста, дайте мне знать, если вы видите ошибку / это не работает на вашем компьютере.

В качестве примера использования я использовал тот же тип, что и в выбранном ответе:

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

Просто для удовольствия (и для ответа на исходный вопрос), да, вполне возможно написать вложенные функции в стандартном 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.

Не существует переносного способа записи вложенных функций в C просто потому, что стандарт C не допускает вложенных функций.
Макросы вам не помогут, потому что они оцениваются препроцессором, а компилятор по-прежнему будет видеть код, который устанавливает функции и выводит ошибку.

Следуя предложению Кириленко здесь , я придумал решение, использующее глобальные переменные, и мьютекс, чтобы передать параметры функции сортировки сортировки. Этот подход является streamобезопасным, может делать все, что достигается с помощью вложенных функций, и должен быть переносимым между компиляторами.

Этот пример сортирует список целых чисел, но инвертирует сортировку для данной области.

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

Вложенные функции не входят в стандарт C, но это расширение gcc (когда -fnested-functions ). Кроме того, вы можете использовать статическую функцию ( cmp_func ) и дополнительный параметр ( reverse ), чтобы сделать то же самое.

Зачем вообще беспокоиться о вложенных функциях и глобалах? 100% портативное решение (даже для K & R) – это просто использовать две разные функции: одну для сортировки в обычном порядке, другую в обратном порядке, а затем называть ее как

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

Примечание: нет необходимости брать адрес функции с помощью & .