Fonctions nestedes portables en C

Est-il possible d’écrire du code C portable en utilisant des fonctions / blocs nesteds?

Je comprends que gcc ne prend en charge que les fonctions nestedes en tant qu’extension non standard et que clang ne prend en charge que les blocs – mais existe-t-il un moyen d’écrire du code à comstackr à la fois en utilisant le standard C avec MACROS?

Si ce n’est pas possible – quel est le meilleur travail autour? Par exemple, comment implémenter une version portable du type suivant qui prend un paramètre? Exemple sortingvial dans 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); } 

Un exemple similaire pourrait être mis en place en utilisant Blocks in Clang. Idéalement, la solution devrait être thread-safe (évitez donc les variables globales).

Edit: Pour plus de clarté, supposons que “standard” signifie C99. Ce qui précède est un exemple sortingvial. Ce que je recherche, c’est l’approche C99 d’un type nécessitant certains parameters. Ici, il utilise simplement un caractère comme un booléen, mais je recherche une solution qui prendrait plusieurs entiers, etc. Il semble que cela pourrait ne pas être possible sans variables globales.

Edit 2: J’ai compris que le fait de passer un pointeur vide avec un pointeur de fonction vous permet de faire tout ce que vous pouvez faire avec des fonctions nestedes. Merci à @Quuxplusone pour avoir suggéré qsort_r et qsort_s . J’ai essayé de mettre en place un wrapper portable sur qsort_r et qsort_s . Il faut une fonction de comparaison et un pointeur vide pour stocker l’état, supprimant ainsi la dépendance à l’égard des fonctions nestedes pour les algorithmes de sorting complexes – afin que vous puissiez comstackr à la fois avec GCC et 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 } 

Remarque: je n’ai pas testé cela sur de nombreuses plates-formes, alors merci de me prévenir si vous voyez un bug / cela ne fonctionne pas sur votre machine.

À titre d’exemple d’utilisation, j’ai implémenté le même type que dans la réponse choisie:

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

    Juste pour le plaisir (et pour répondre à la question initiale), oui, il est tout à fait possible d’écrire des fonctions nestedes dans C99 conforme aux normes en utilisant le système de macros pour “démêler” la version nestede de votre code. Voici une implémentation possible: https://github.com/Leushenko/C99-Lambda

    Avec cela, vous pouvez écrire des abominations comme ceci:

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

    Soyons cependant très clairs sur un point: c’est le pire moyen d’écrire ce type de code en C. Si vous vous trouvez dans une position où vous devez réellement utiliser une bibliothèque de macros pour écrire un code comme celui-ci, quittez votre travail en tant que un programmeur et devenir un agriculteur. Utilisez ceci en production et vos collègues vous assassineront probablement dans votre sumil.

    De plus, assez hilarement, même s’il est techniquement conforme aux normes, les seuls compilateurs dotés d’un pré-processeur capable de gérer le poids de tant de macros sont de toute façon GCC et Clang.

    Il n’y a pas de moyen portable d’écrire des fonctions nestedes en C, simplement parce que le standard C n’autorise pas les fonctions nestedes.
    Les macros ne vous aideront pas beaucoup ici car elles sont évaluées par le préprocesseur et le compilateur verra toujours le code qui imbrique les fonctions et marque une erreur.

    En suivant la suggestion de @Kirilenko, j’ai trouvé une solution utilisant des variables globales et un mutex pour passer des parameters à une fonction de comparateur de sorting. Cette approche est thread-safe, peut tout faire avec des fonctions nestedes et doit être portable entre les compilateurs.

    Cet exemple sortinge une liste d’entiers, mais l’inverse pour une région donnée.

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

    Exemple de résultats:

     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 

    Les fonctions nestedes ne sont pas dans le standard C, mais il s’agit d’une extension gcc (lorsque l’indicateur -fnested-functions est activé). En outre, vous pouvez utiliser une fonction statique ( cmp_func ) et un paramètre supplémentaire ( reverse ) pour faire la même chose.

    Pourquoi se préoccuper des fonctions nestedes et des globales? La solution 100% portable (même pour K & R) consiste simplement à utiliser deux fonctions différentes, l’une pour sortinger dans l’ordre normal, l’autre en sens inverse, puis l’appeler comme suit:

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

    Remarque: inutile de prendre l’adresse d’une fonction avec & .