Question

Y at-il une façon « appropriée » pour mettre en œuvre des fonctions d'ordre supérieur en C.

Je suis surtout curieux de choses comme la correction de la portabilité et la syntaxe ici et s'il y a plus d'une façons ce que les mérites et les défauts sont.

Edit: La raison pour laquelle je veux savoir comment créer des fonctions d'ordre supérieur sont que j'ai écrit un système pour convertir les listes PyObject (que vous obtenez lors de l'appel des scripts python) dans une liste de structures C contenant les mêmes données, mais organisées de façon à ne dépend pas les bibliothèques Python.h. Donc, mon plan est d'avoir une fonction qui le compare à une liste pythonique et appelle une fonction sur chaque élément dans la liste et place le résultat dans une liste qui retourne alors.

C'est donc essentiellement mon plan:

typedef gpointer (converter_func_type)(PyObject *)

gpointer converter_function(PyObject *obj)
{
    // do som stuff and return a struct cast into a gpointer (which is a void *)
}

GList *pylist_to_clist(PyObject *obj, converter_func_type f)
{
   GList *some_glist;
   for each item in obj
   {
       some_glist = g_list_append(some_glist, f(item));
   }
   return some_glist;
}

void some_function_that_executes_a_python_script(void)
{
   PyObject *result = python stuff that returns a list;
   GList *clist = pylist_to_clist(result, converter_function);
}

Et pour clearify la question: Je veux savoir comment faire cela en plus sûr et plus juste C. Je voudrais vraiment garder le style de la fonction d'ordre supérieur mais si cela est mal je l'apprécie grandement les moyens de le faire une autre manière.

Était-ce utile?

La solution

Si vous êtes désireux de faire cela en C ordinaire, vous devez vous rappeler d'inclure la possibilité de passer un pointeur de contexte de l'appelant du foncteur (la fonction d'ordre supérieur) à la fonction passée. Cela permet vous simulez assez d'une fermeture que vous pouvez faire fonctionner les choses assez facilement. Qu'est-ce que les points de pointeur pour ... eh bien, qui est à vous, mais il devrait être un void* dans l'API du foncteur (ou l'un des nombreux alias pour elle, comme gpointer dans le monde GLib ou ClientData dans l'API Tcl C) .

[EDIT]: Pour utiliser / adapter votre exemple:

typedef gpointer (converter_func_type)(gpointer,PyObject *)

gpointer converter_function(gpointer context_ptr,PyObject *obj)
{
    int *number_of_calls_ptr = context_ptr;
    *number_of_calls_ptr++;
    // do som stuff and return a struct cast into a gpointer (which is a void *)
}

GList *pylist_to_clist(PyObject *obj, converter_func_type f, gpointer context_ptr)
{
   GList *some_glist;
   for each item in obj
   {
       some_glist = g_list_append(some_glist, f(context_ptr,item));
   }
   return some_glist;
}

void some_function_that_executes_a_python_script(void)
{
   int number_of_calls = 0;
   PyObject *result = python stuff that returns a list;
   GList *clist = pylist_to_clist(result, converter_function, &number_of_calls);
   // Now number_of_calls has how often converter_function was called...
}

Ceci est un exemple trivial de la façon de le faire, mais il devrait vous montrer le chemin.

Autres conseils

Techniquement, les fonctions d'ordre supérieur ne sont que des fonctions qui prennent ou retournent des fonctions. Donc, les choses comme qsort sont déjà d'ordre supérieur.

Si vous voulez dire quelque chose comme les fonctions lambda dans les langages fonctionnels (qui est où les fonctions d'ordre supérieur deviennent vraiment utiles), ce sont un peu plus difficile et ne peut se faire naturellement dans la norme actuelle C. Ils sont juste ne fait pas partie de la langue. blocs d'Apple extension est le meilleur candidat. Il ne fonctionne que dans (le compilateur LLVM et C) GCC, mais ils sont vraiment utiles. Espérons que quelque chose comme ça se propagera. Voici quelques ressources pertinentes:

Le gros problème avec la mise en œuvre des fonctions d'ordre supérieur en C est que, pour faire tout ce que vous avez besoin de fermeture non trivial, qui sont des pointeurs de fonction augmentée avec des structures de données contenant des variables locales auxquelles ils ont accès. Depuis l'idée derrière la fermeture est de capturer les variables locales et passer ceux avec le pointeur de fonction, il est difficile de le faire sans l'aide du compilateur. Et même avec l'aide du compilateur, il est difficile de le faire sans la collecte des ordures, car les variables peuvent exister en dehors de leur champ d'application, ce qui rend difficile de savoir quand les libérer.

En c droite, ce qui est vraiment fait que par des pointeurs de fonction, qui sont à la fois une douleur et non destiné à ce genre de chose (ce qui est en partie pourquoi ils sont une douleur). (Blocs ou fermetures, selon non-pomme) sont fantastiques pour cela, cependant. Ils compilent en 4.x gcc ou quelque chose, et quelque chose icc, mais quel que est ce que vous cherchez. Malheureusement, je ne peux pas sembler trouver de bons tutoriels en ligne, mais il suffit de dire que cela fonctionne quelque chose comme ceci:

void iterate(char *str, int count, (^block)(str *)){
  for(int i = 0; i < count; i++){
    block(list[i]);
  }
}

main() {
  char str[20];
  iterate(str, 20, ^(char c){
    printf("%c ", c);
  });

  int accum = 0;
  iterate(someList, 20, ^(char c){
    accum += c;
    iterate(str, 20, ^(char c){
      printf("%c ", c);
    });
  });
}

de toute évidence ce code est inutile, mais elle imprime chaque caractère d'une chaîne (str) avec un espace entre elle, ajoute alors tous les personnages ensemble dans accum, et chaque fois qu'il fait elle imprime la liste des caractères à nouveau.

Espérons que cela aide. Soit dit en passant, les blocs sont très visibles sous Mac OS X Snow Leopard api-s, et je crois sont dans la future norme C ++ 0x, donc ils ne sont pas vraiment hors du commun.

Pratiquement toutes les applications de la fonction d'ordre supérieur intéressante exige la fermeture, ce qui entraîne la routine C laborous et source d'erreurs de définir manuellement et en remplissant les arguments de fonction struct.

Ceci est une réponse à la question:. Comment composer des fonctions en C, qui est redirigé ici

Vous pouvez créer une structure de données pour mettre en œuvre un type de données de la liste. cette structure peut contenir des pointeurs de fonction.

#include<stdlib.h>
#include<malloc.h>

typedef (*fun)();

typedef struct funList { fun car; struct funList *cdr;} *funList;

const funList nil = NULL;

int null(funList fs){ return nil==fs; }

fun car(funList fs)
{
   if(!null(fs)) return fs->car; 
   else 
   {
     fprintf(stderr,"error:can't car(nil) line:%d\n",__LINE__);
     exit(1);
   }
}

funList cdr(funList ls)
{ if(!null(ls)) return ls->cdr; 
  else 
  {
    fprintf(stderr,"error:can't cdr(nil) line:%d\n",__LINE__);
    exit(1);
  }
}

funList cons(fun f, funList fs)
{  funList ls;

   ls=(funList) malloc(sizeof(struct funList));
   if(NULL==ls)
   {
     fprintf(stderr,"error:can't alloc mem for cons(...) line:%d\n",__LINE__);
     exit(1);
   }

   ls->car=f;
   ls->cdr=fs;

   return ls;
}

on peut écrire un échantillon de fonction qui applique une liste de fonctions:

type_2 comp(funList fs, type_1 x)
{  
   return (null(fs)) ? x : car(fs)(comp(cdr(fs),x)); 
}

Un exemple de la façon dont cela fonctionne. Nous utilisons (f g h) sous la forme d'une notation abrégée pour les inconvénients (f, g, les inconvénients (les inconvénients (h, nil))), qui est appliqué à un argument donné x:

comp((f g h),x)

=

f(comp((g h),x))

=

f(g(comp((h),x)))

=

f(g(h(comp(nil,x))))

=

f(g(h(x)))

si vous aviez utilisé le type de liste polymorphes dans un langage typé comme SML ou Haskell le type de maquette doit être:

comp :: ([a -> a],a) -> a

parce que, dans ce contexte, tous les membres d'une liste ont le même type. C peut être plus souple dans ce sens. Peut-être quelque chose comme

typedef void (*fun)();

ou

typedef (*fun)();

vous devriez voir ce que dit le manuel de C à ce sujet. Et assurez-vous que toutes les fonctions contiguës ont des types compatibles.

Les fonctions pour composer doivent être purs, à savoir sans effets secondaires ni variables libres.

Il est très difficile de le faire en C. Il est plus droit possible en C ++ (voir foncteurs tutoriel ou de Boost bind et fonction bibliothèques). Enfin, C ++ 0x ajoute le support natif pour les fonctions lambda , qui prend en charge pour vous de capturer la fermeture de toutes les variables que votre funcion dépend.

Si vous voulez créer des fonctions d'ordre supérieur, ne pas utiliser C. Il existe des solutions C à votre problème. Ils peuvent ne pas être élégant, ou ils peuvent être plus élégant que vous vous rendez compte.

[Modifier] Je suggère que la seule façon d'y parvenir était d'utiliser un langage de script. D'autres me ont appelé sur elle. Donc, je remplace cette suggestion avec ceci: [/ Modifier]

Qu'est-ce que vous essayez d'atteindre? Si vous voulez imiter les fermetures, utilisez une langue qui les prend en charge (vous pouvez attacher en Ruby, Lua, javascript, etc dans les bibliothèques). Si vous souhaitez utiliser les callbacks, les pointeurs de fonction sont ok. pointeurs de fonction combinent les zones les plus dangereuses de C (pointeurs et le système de type faible), donc soyez prudent. déclarations de pointeur de fonction ne sont pas amusant à lire, que ce soit.

Vous trouverez des bibliothèques C en utilisant des pointeurs de fonction parce qu'ils doivent. Si vous écrivez une bibliothèque, vous avez besoin peut-être de les utiliser aussi. Si vous êtes juste de les utiliser dans votre propre code, vous n'êtes probablement pas penser à C. Vous pensez à Lisp ou système ou rubis ou ... et d'essayer de l'écrire en C. Apprendre la voie C.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top