Je suis en train d'écrire un ensemble de fonction C pour gérer des
listes chaînées génériques. L'une de ces fonctions permet d'executer un
traitement sur chaque element d'une liste.
Oui mais non car cela me prive du contrôle de type sur STRING_ToUpper.
(Accessoirement "typedef void (*MaFonction_t)(void*);" serait plus lisible pour des cast.)
C'est ce que j'ai déjà fait et c'est ce qui me semble le plus propre.
Merci.
-ed-
On 9 déc, 16:06, batyann811 wrote:
Je suis en train d'écrire un ensemble de fonction C pour gérer des listes chaînées génériques. L'une de ces fonctions permet d'execu ter un traitement sur chaque element d'une liste.
je te laisse deviner quelle est la bonne syntaxe...
de même
return 0; return (0); return ((((((((0))))))));
On 9 déc, 16:06, batyann811 <inva...@invalid.fr> wrote:
Je suis en train d'écrire un ensemble de fonction C pour gérer des
listes chaînées génériques. L'une de ces fonctions permet d'execu ter un
traitement sur chaque element d'une liste.
Je suis en train d'écrire un ensemble de fonction C pour gérer des listes chaînées génériques. L'une de ces fonctions permet d'execu ter un traitement sur chaque element d'une liste.
Oui mais non car cela me prive du contrôle de type sur STRING_ToUpper.
C'est de la programmation générique, tu n'as pas le choix. Tu passes par un void*, donc tu perds le contrôle de type. C'est inévitable. La programmation générique c'est pour les gens réveillés. Tu peux éventuellement ajouter une surcouche applicative pour sécuriser l'usage à destination de programmeurs lambda.
On 10 déc, 08:37, batyann811 <inva...@invalid.fr> wrote:
Xavier Roche wrote:
> LIST_Traverse(ma_list, (void (*)(void*)) STRING_ToUpper);
> Mais c'est terriblement crade.
Effectivement c'est crade (et je me doutais bien que ça le serait) mais
c'était surtout pour satisfaire ma curiosité.
> La solution un peu plus propre est d'utiliser le bon prototype:
Oui mais non car cela me prive du contrôle de type sur STRING_ToUpper.
C'est de la programmation générique, tu n'as pas le choix. Tu passes
par un void*, donc tu perds le contrôle de type. C'est inévitable. La
programmation générique c'est pour les gens réveillés. Tu peux
éventuellement ajouter une surcouche applicative pour sécuriser
l'usage à destination de programmeurs lambda.
Oui mais non car cela me prive du contrôle de type sur STRING_ToUpper.
C'est de la programmation générique, tu n'as pas le choix. Tu passes par un void*, donc tu perds le contrôle de type. C'est inévitable. La programmation générique c'est pour les gens réveillés. Tu peux éventuellement ajouter une surcouche applicative pour sécuriser l'usage à destination de programmeurs lambda.
batyann811
-ed- wrote:
C'est de la programmation générique, tu n'as pas le choix. Tu passes par un void*, donc tu perds le contrôle de type. C'est inévitable. La programmation générique c'est pour les gens réveillés. Tu peux éventuellement ajouter une surcouche applicative pour sécuriser l'usage à destination de programmeurs lambda.
Euh... Ben si j'ai le choix...
Je voulais juste dire que je ne veux pas changer le prototype de ma fonction STRING_ToUpper juste pour pouvoir l'utiliser avec LIST_Traverse.
La bonne solution est donc (pour moi) de passer par un cast de la fonction que je passe en paramètre pas comme me le proposait Xavier de redefinir de prototype la fonction que je passe en paramètre. Sinon je perd le contrôle de type pour toutes utilisations de STRING_ToUpper.
Et puis je ne vais pas redefinir le prototype de toutes mes fonctions succeptibles de servir un jour de paramètre à LIST_Traverse...
-ed- wrote:
C'est de la programmation générique, tu n'as pas le choix. Tu passes
par un void*, donc tu perds le contrôle de type. C'est inévitable. La
programmation générique c'est pour les gens réveillés. Tu peux
éventuellement ajouter une surcouche applicative pour sécuriser
l'usage à destination de programmeurs lambda.
Euh... Ben si j'ai le choix...
Je voulais juste dire que je ne veux pas changer le prototype de ma
fonction STRING_ToUpper juste pour pouvoir l'utiliser avec LIST_Traverse.
La bonne solution est donc (pour moi) de passer par un cast de la
fonction que je passe en paramètre pas comme me le proposait Xavier de
redefinir de prototype la fonction que je passe en paramètre. Sinon je
perd le contrôle de type pour toutes utilisations de STRING_ToUpper.
Et puis je ne vais pas redefinir le prototype de toutes mes fonctions
succeptibles de servir un jour de paramètre à LIST_Traverse...
C'est de la programmation générique, tu n'as pas le choix. Tu passes par un void*, donc tu perds le contrôle de type. C'est inévitable. La programmation générique c'est pour les gens réveillés. Tu peux éventuellement ajouter une surcouche applicative pour sécuriser l'usage à destination de programmeurs lambda.
Euh... Ben si j'ai le choix...
Je voulais juste dire que je ne veux pas changer le prototype de ma fonction STRING_ToUpper juste pour pouvoir l'utiliser avec LIST_Traverse.
La bonne solution est donc (pour moi) de passer par un cast de la fonction que je passe en paramètre pas comme me le proposait Xavier de redefinir de prototype la fonction que je passe en paramètre. Sinon je perd le contrôle de type pour toutes utilisations de STRING_ToUpper.
Et puis je ne vais pas redefinir le prototype de toutes mes fonctions succeptibles de servir un jour de paramètre à LIST_Traverse...
batyann811
-ed- wrote:
Oui, je recommande cette pratique, ça facilite la lecture et la maintenance.
Effectivement c'est ce qui me parait le plus propre.
Par contre je déconseille d'inclure le *, car ça empêche d'utiliser le type pour définir un prototype de fonction.
Effectivement ça me parait mieux je vais donc de ce pas aller virer les *
Un cast masque un problème mais ne le corrige pas.
Oui mais ça peut éviter d'avoir à créer 623 fonctions callback...
Merci à toi et à Xavier pour ces conseils.
Antoine Leca
>>> void LIST_Traverse(LIST * list, void ( * func) (void *) ); Je veux éxecuter la fonction suivante sur chaque élément de ma liste void STRING_ToUpper(STRING * str); /* STRING est un type perso pas char * */
Dans mon code j'essaye donc de faire un truc du genre : LIST_Traverse(ma_list, STRING_ToUpper); Normal les 2 fonctions n'ont pas exactement le même prototypes.
void ( * func) (void *) et void STRING_ToUpper(STRING * str); n'ont pas le même prototype.
Plus précisement, le problème ici est le format de l'argument str; en haut, c'est un pointeur vers void, donc supposé capable de pointer vers n'importe quel adresse; tandis qu'en bas, str n'est plus qu'un pointeur vers STRING, ce peut être un format de pointeur différent, par exemple avec moins de bits pour le représenter (l'idée étant que si le compilateur « sait » que STRING est toujours aligné sur une frontière de X octets, il n'est pas nécessaire de garder les bits de poids faible).
Évidemment, LIST_Traverse N'est PAS au courant des particularités de la représentation des STRING*...
Il faut donc passer par un callback du même type :
Ma question est donc quel syntaxe utiliser pour faire le cast ?
Un cast masque un problème mais ne le corrige pas.
Oui mais ça peut éviter d'avoir à créer 623 fonctions callback...
À vouloir économiser 623 (?) fonctions callback, tu vas te priver d'un peu de portabilité, et introduire par le cast une possibilité réelle de supprimer des messages intéressants d'avertissement de la part du compilateur.
Par ailleurs, si l'argument c'est d'économiser des touches, je ne suis pas sûr que le cast soit gagnant : les call-backs se résument à modifier les appels à LIST_Traverse (et encore, ce peut être fait grâce à une macro de présentation), tandis que la déclaration des call-backs se résume à l'écriture d'une macro cpp
Bienvenue dans le monde étrange de cpp, celui qu'on n'en voit pas en C++.
Antoine
>>> void LIST_Traverse(LIST * list, void ( * func) (void *) );
Je veux éxecuter la fonction suivante sur chaque élément de ma liste
void STRING_ToUpper(STRING * str);
/* STRING est un type perso pas char * */
Dans mon code j'essaye donc de faire un truc du genre :
LIST_Traverse(ma_list, STRING_ToUpper);
Normal les 2 fonctions n'ont pas exactement le même prototypes.
void ( * func) (void *)
et
void STRING_ToUpper(STRING * str);
n'ont pas le même prototype.
Plus précisement, le problème ici est le format de l'argument str; en haut,
c'est un pointeur vers void, donc supposé capable de pointer vers n'importe
quel adresse; tandis qu'en bas, str n'est plus qu'un pointeur vers STRING,
ce peut être un format de pointeur différent, par exemple avec moins de bits
pour le représenter (l'idée étant que si le compilateur « sait » que STRING
est toujours aligné sur une frontière de X octets, il n'est pas nécessaire
de garder les bits de poids faible).
Évidemment, LIST_Traverse N'est PAS au courant des particularités de la
représentation des STRING*...
Il faut donc passer par un callback du même type :
En news:4940e330$0$929$ba4acef3@news.orange.fr, batyann811 va escriure:
-ed- wrote:
Ma question est donc quel syntaxe utiliser pour faire le cast ?
Un cast masque un problème mais ne le corrige pas.
Oui mais ça peut éviter d'avoir à créer 623 fonctions callback...
À vouloir économiser 623 (?) fonctions callback, tu vas te priver d'un peu
de portabilité, et introduire par le cast une possibilité réelle de
supprimer des messages intéressants d'avertissement de la part du
compilateur.
Par ailleurs, si l'argument c'est d'économiser des touches, je ne suis pas
sûr que le cast soit gagnant : les call-backs se résument à modifier les
appels à LIST_Traverse (et encore, ce peut être fait grâce à une macro de
présentation), tandis que la déclaration des call-backs se résume à
l'écriture d'une macro cpp
>>> void LIST_Traverse(LIST * list, void ( * func) (void *) ); Je veux éxecuter la fonction suivante sur chaque élément de ma liste void STRING_ToUpper(STRING * str); /* STRING est un type perso pas char * */
Dans mon code j'essaye donc de faire un truc du genre : LIST_Traverse(ma_list, STRING_ToUpper); Normal les 2 fonctions n'ont pas exactement le même prototypes.
void ( * func) (void *) et void STRING_ToUpper(STRING * str); n'ont pas le même prototype.
Plus précisement, le problème ici est le format de l'argument str; en haut, c'est un pointeur vers void, donc supposé capable de pointer vers n'importe quel adresse; tandis qu'en bas, str n'est plus qu'un pointeur vers STRING, ce peut être un format de pointeur différent, par exemple avec moins de bits pour le représenter (l'idée étant que si le compilateur « sait » que STRING est toujours aligné sur une frontière de X octets, il n'est pas nécessaire de garder les bits de poids faible).
Évidemment, LIST_Traverse N'est PAS au courant des particularités de la représentation des STRING*...
Il faut donc passer par un callback du même type :
Ma question est donc quel syntaxe utiliser pour faire le cast ?
Un cast masque un problème mais ne le corrige pas.
Oui mais ça peut éviter d'avoir à créer 623 fonctions callback...
À vouloir économiser 623 (?) fonctions callback, tu vas te priver d'un peu de portabilité, et introduire par le cast une possibilité réelle de supprimer des messages intéressants d'avertissement de la part du compilateur.
Par ailleurs, si l'argument c'est d'économiser des touches, je ne suis pas sûr que le cast soit gagnant : les call-backs se résument à modifier les appels à LIST_Traverse (et encore, ce peut être fait grâce à une macro de présentation), tandis que la déclaration des call-backs se résume à l'écriture d'une macro cpp