Tout d'abord, meilleurs v=9Cux pour cette ann=E9e 2013.
Dans le cas d'un petit projet personnel, je souhaite cr=E9e une liste dont =
chaque n=9Cud comporte un pointeur vers une ressource allou=E9e et un point=
eur de fonction sp=E9cifiant la fonction permettant de lib=E9rer cette ress=
ource. Ainsi, j'ai repr=E9sent=E9 un n=9Cud =E0 l'aide de cette structure :
Le probl=E8me, c'est que les ressources allou=E9es sont stock=E9es au sein =
d'un pointeur g=E9n=E9rique (pointeur sur void), mais que les fonctions de =
destructions r=E9f=E9renc=E9es, elles, n'attendent pas forc=E9ment un tel p=
ointeur (parfois oui, comme free, mais parfois non, comme fclose ou autre).=
Je sais que, =E0 ce sujet, la norme fourni quelques garanties pour certain=
s types de pointeurs :
C11 (n1570), =A7 6.2.5 Types, al. 28, p. 43
> A pointer to void shall have the same representation and alignment
> requirements as a pointer to a character type. Similarly, pointers
> to qualified or unqualified versions of compatible types shall have
> the same representation and alignment requirements. All pointers to
> structure types shall have the same representation and alignment
> requirements as each other. All pointers to union types shall have the
> same representation and alignment requirements as each other. Pointers
> to other types need not have the same representation or alignment
> requirements.
mais pour le reste, rien n'est pr=E9vu. Aussi, j'aurais voulu savoir si j'a=
i des chances de tomber sur des architectures o=F9 une telle solution pose =
probl=E8me et, =E9galement, s'il existe une autre solution, cette fois-ci p=
ortable ?=20
Euh, si. 6.3 /Conversions/, § 6.3.2.3 /Pointers/, al. 1 : A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
En gros, tant que ton code manipule des pointeurs génériques et que derrière il y a des pointeurs vers des types _objets_, tout va bien. Il faut bien comprendre que si dans ton code tu écris FILE* f; ptr_ress‐>data = f; il y a conversion de FILE* à void* ; et quand tu écris plus tard fclose( (FILE*)(ptr_ress‐>data) ); la conversion de retour (/back/) est même explicite !
En fait, mon problème est que la libération de la ressource est faite via le pointeur de fonction « delete » de la structure. Autrement dit, pour chaque ressource, on a un appel du type :
Le problème, c'est que tu utilises un prototype générique pour "delete" Autrement dit, il n'y a plus aucun contrôle, tu peux tout aussi bien appeler ptr_ress‐>delete(42);
et de coder les adjudants «delete», par exemple pour la mémoire void delete_memoire(void*p) { free(p); } ou pour les fichiers void delete_fichier(void*f) { fclose(p); }
Cette dernière fonction va effectuer la conversion de type si besoin est, et va aussi va permettre d'ignorer la différence de type des valeurs de retour: la signature de &fclose est int(*)(), pas void(*)()
[couic]
Mais, si je comprend bien, dans la réalité, tous les pointeurs ont généralement la même taille sauf les pointeurs de fonctions ?
Sauf machines vraiment bizarres, oui. Les exemples les plus classiques des cas contraires sont d'une part les machines genre PDP10 ou certains Unisys à adressage mot (donc un pointeur vers char est plus gros car il faut y ajouter un sous-pointeur vers le byte au sein du mot), et d'autre part les adressages «segmentés» genre iAPX286 en mode H où chaque structure utilise son propre segment.
De plus, un problème avec les pointeurs de fonctions, au delà d'une taille différente, ils peuvent être incommensurables, opérant dans un espace d'adresses différent, avec un jeu d'instructions différentes.
Antoine
Jérôme Frgacic écrivit :
Antoine Leca a écrit :
Euh, si. 6.3 /Conversions/, § 6.3.2.3 /Pointers/, al. 1 :
A pointer to void may be converted to or from a pointer to any
object type. A pointer to any object type may be converted to
a pointer to void and back again; the result shall compare
equal to the original pointer.
En gros, tant que ton code manipule des pointeurs génériques et que
derrière il y a des pointeurs vers des types _objets_, tout va bien.
Il faut bien comprendre que si dans ton code tu écris
FILE* f;
ptr_ress‐>data = f;
il y a conversion de FILE* à void* ; et quand tu écris plus tard
fclose( (FILE*)(ptr_ress‐>data) );
la conversion de retour (/back/) est même explicite !
En fait, mon problème est que la libération de la ressource est faite
via le pointeur de fonction « delete » de la structure. Autrement dit,
pour chaque ressource, on a un appel du type :
Le problème, c'est que tu utilises un prototype générique pour "delete"
Autrement dit, il n'y a plus aucun contrôle, tu peux tout aussi bien appeler
ptr_ress‐>delete(42);
et de coder les adjudants «delete», par exemple pour la mémoire
void delete_memoire(void*p) { free(p); }
ou pour les fichiers
void delete_fichier(void*f) { fclose(p); }
Cette dernière fonction va effectuer la conversion de type si besoin
est, et va aussi va permettre d'ignorer la différence de type des
valeurs de retour: la signature de &fclose est int(*)(), pas void(*)()
[couic]
Mais, si je comprend bien, dans la réalité, tous les pointeurs ont
généralement la même taille sauf les pointeurs de fonctions ?
Sauf machines vraiment bizarres, oui. Les exemples les plus classiques
des cas contraires sont d'une part les machines genre PDP10 ou certains
Unisys à adressage mot (donc un pointeur vers char est plus gros car il
faut y ajouter un sous-pointeur vers le byte au sein du mot), et d'autre
part les adressages «segmentés» genre iAPX286 en mode H où chaque
structure utilise son propre segment.
De plus, un problème avec les pointeurs de fonctions, au delà d'une
taille différente, ils peuvent être incommensurables, opérant dans un
espace d'adresses différent, avec un jeu d'instructions différentes.
Euh, si. 6.3 /Conversions/, § 6.3.2.3 /Pointers/, al. 1 : A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
En gros, tant que ton code manipule des pointeurs génériques et que derrière il y a des pointeurs vers des types _objets_, tout va bien. Il faut bien comprendre que si dans ton code tu écris FILE* f; ptr_ress‐>data = f; il y a conversion de FILE* à void* ; et quand tu écris plus tard fclose( (FILE*)(ptr_ress‐>data) ); la conversion de retour (/back/) est même explicite !
En fait, mon problème est que la libération de la ressource est faite via le pointeur de fonction « delete » de la structure. Autrement dit, pour chaque ressource, on a un appel du type :
Le problème, c'est que tu utilises un prototype générique pour "delete" Autrement dit, il n'y a plus aucun contrôle, tu peux tout aussi bien appeler ptr_ress‐>delete(42);
et de coder les adjudants «delete», par exemple pour la mémoire void delete_memoire(void*p) { free(p); } ou pour les fichiers void delete_fichier(void*f) { fclose(p); }
Cette dernière fonction va effectuer la conversion de type si besoin est, et va aussi va permettre d'ignorer la différence de type des valeurs de retour: la signature de &fclose est int(*)(), pas void(*)()
[couic]
Mais, si je comprend bien, dans la réalité, tous les pointeurs ont généralement la même taille sauf les pointeurs de fonctions ?
Sauf machines vraiment bizarres, oui. Les exemples les plus classiques des cas contraires sont d'une part les machines genre PDP10 ou certains Unisys à adressage mot (donc un pointeur vers char est plus gros car il faut y ajouter un sous-pointeur vers le byte au sein du mot), et d'autre part les adressages «segmentés» genre iAPX286 en mode H où chaque structure utilise son propre segment.
De plus, un problème avec les pointeurs de fonctions, au delà d'une taille différente, ils peuvent être incommensurables, opérant dans un espace d'adresses différent, avec un jeu d'instructions différentes.
Il y a deux points de vue sur les comportements indefinis: ils sont la pour traiter des cas particuliers et en dehors de ceux-ci il faut agir de maniere sensee; ils sont reellement la porte ouverte a tout. Ceux qui ecrivent les optimiseurs ont tendance a avoir le second.
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Il y a deux points de vue sur les comportements indefinis: ils sont la
pour traiter des cas particuliers et en dehors de ceux-ci il faut agir
de maniere sensee; ils sont reellement la porte ouverte a tout. Ceux
qui ecrivent les optimiseurs ont tendance a avoir le second.
A+
--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Il y a deux points de vue sur les comportements indefinis: ils sont la pour traiter des cas particuliers et en dehors de ceux-ci il faut agir de maniere sensee; ils sont reellement la porte ouverte a tout. Ceux qui ecrivent les optimiseurs ont tendance a avoir le second.
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Jerome se demande si ce qu'il fait fonctionne avec du C standard de facon portable.
Et les trucs dont tu parles sortent du C standard.
Plus exactement, il y a la possibilite a des implementations d'avoir des trucs "implementation-defined" pour certains adressages (voire d'avoir des archis suffisamment exotiques pour qu'il y ait des implementations de C qui ne se conforment pas completement a la norme).
Mais, de ce que je sais du C, dans une implementation conforme, il n'y a pas de souci avec les pointeurs.
Les seuls pointeurs "bizarres" sont explicites, c'est les trucs comme __far et __near sur certaines implementations. La norme couvre ces cas, mais tu ne vas pas te retrouver avec ceux-ci "par surprise". Tu n'es plus dans la partie strictement conforme, tu es dans la partie "implementation-defined", et la norme, car c'est son boulot, te dit que tout marche bien tant que tu restes dans les clous.
In article <kcjjnh$ikl$1@shakotay.alphanet.ch>,
Antoine Leca <root@localhost.invalid> wrote:
Jerome se demande si ce qu'il fait fonctionne avec du C standard de
facon portable.
Et les trucs dont tu parles sortent du C standard.
Plus exactement, il y a la possibilite a des implementations d'avoir
des trucs "implementation-defined" pour certains adressages (voire d'avoir
des archis suffisamment exotiques pour qu'il y ait des implementations de
C qui ne se conforment pas completement a la norme).
Mais, de ce que je sais du C, dans une implementation conforme, il n'y a pas
de souci avec les pointeurs.
Les seuls pointeurs "bizarres" sont explicites, c'est les trucs comme
__far et __near sur certaines implementations. La norme couvre ces cas,
mais tu ne vas pas te retrouver avec ceux-ci "par surprise". Tu n'es plus
dans la partie strictement conforme, tu es dans la partie
"implementation-defined", et la norme, car c'est son boulot, te dit que
tout marche bien tant que tu restes dans les clous.
Jerome se demande si ce qu'il fait fonctionne avec du C standard de facon portable.
Et les trucs dont tu parles sortent du C standard.
Plus exactement, il y a la possibilite a des implementations d'avoir des trucs "implementation-defined" pour certains adressages (voire d'avoir des archis suffisamment exotiques pour qu'il y ait des implementations de C qui ne se conforment pas completement a la norme).
Mais, de ce que je sais du C, dans une implementation conforme, il n'y a pas de souci avec les pointeurs.
Les seuls pointeurs "bizarres" sont explicites, c'est les trucs comme __far et __near sur certaines implementations. La norme couvre ces cas, mais tu ne vas pas te retrouver avec ceux-ci "par surprise". Tu n'es plus dans la partie strictement conforme, tu es dans la partie "implementation-defined", et la norme, car c'est son boulot, te dit que tout marche bien tant que tu restes dans les clous.
Jerome se demande si ce qu'il fait fonctionne avec du C standard de facon portable.
Et les trucs dont tu parles sortent du C standard.
Pas d'accord: 2.6.5/28 (C 11 mais je suis sur que ce n'est pas quelque chose de nouveau)
A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.
Jerome se demande si ce qu'il fait fonctionne avec du C standard de
facon portable.
Et les trucs dont tu parles sortent du C standard.
Pas d'accord: 2.6.5/28 (C 11 mais je suis sur que ce n'est pas quelque
chose de nouveau)
A pointer to void shall have the same representation and alignment
requirements as a pointer to a character type. Similarly, pointers to
qualified or unqualified versions of compatible types shall have the
same representation and alignment requirements. All pointers to
structure types shall have the same representation and alignment
requirements as each other. All pointers to union types shall have the
same representation and alignment requirements as each other. Pointers
to other types need not have the same representation or alignment
requirements.
Jerome se demande si ce qu'il fait fonctionne avec du C standard de facon portable.
Et les trucs dont tu parles sortent du C standard.
Pas d'accord: 2.6.5/28 (C 11 mais je suis sur que ce n'est pas quelque chose de nouveau)
A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.
Je me demande si le comportement est defini ou pas sur une implementation qui choisit d'avoir la meme representation pour tous les pointeurs; il faudrait rechercher l'ensemble des references pertinentes et j'ai pas le souvenir de l'avoir deja fait -- pire, ca me semble toucher a des zones ou les differences de formulation entre C90, C99 (et peut-etre C11) ne sont pas minimes.
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Je me demande si le comportement est defini ou pas sur une
implementation qui choisit d'avoir la meme representation pour tous les
pointeurs; il faudrait rechercher l'ensemble des references pertinentes
et j'ai pas le souvenir de l'avoir deja fait -- pire, ca me semble
toucher a des zones ou les differences de formulation entre C90, C99 (et
peut-etre C11) ne sont pas minimes.
A+
--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Je me demande si le comportement est defini ou pas sur une implementation qui choisit d'avoir la meme representation pour tous les pointeurs; il faudrait rechercher l'ensemble des references pertinentes et j'ai pas le souvenir de l'avoir deja fait -- pire, ca me semble toucher a des zones ou les differences de formulation entre C90, C99 (et peut-etre C11) ne sont pas minimes.
A+
-- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Antoine Leca
Marc Espie écrivit :
Attention, je crois qu'on melange deux trucs.
C'est bien possible, merci de recentrer.
Jerome se demande si ce qu'il fait fonctionne avec du C standard de facon portable.
Et justement, je ne trouve que ce soit une très bonne idée.
Pour mémoire, il voudrait manipuler des fonctions prenant des types pointeurs variés en paramètre, _sans_ utiliser de prototypes.
Ne pas utiliser de prototype est effectivement du C standard, mais je ne suis pas sûr que ce soit une très bonne solution pour assurer la portabilité.
D'un autre côté, en termes de perfs, je ne sais pas si les compilateurs actuels sont assez finaux pour remplacer void delete_memoire(void*p) { free(p); } void delete_fichier(void*f) { fclose(p); } par .alias delete_memoire, free .alias delete_fichier, fclose et éviter l'indirection « inutile ».
Après, et c'est là que l'on mélange, il est vrai qu'avec la quasi-totalité des machines actuelles, le seul effet de tout cela sera des avertissements sans frais (même avec des prototypes). Et il est bien clair que la tendance va dans le sens d'avoir de moins en moins de ces cas particuliers, et au contraire d'avoir tous les pointeurs (vers objets) avec des représentations identiques.
De plus, un clang récent (3.1) proteste de la manière ("incompatible pointer type") quand j'écris du code qui va marcher « partout », comme
int (*delete_fichier)(void*) = fclose;
ou si j'écris du code beaucoup plus tendancieux, genre
alors même que dans le second cas, l'appel ultérieur avec utilisation de la valeur retournée appelle au meurtre...
Antoine
Marc Espie écrivit :
Attention, je crois qu'on melange deux trucs.
C'est bien possible, merci de recentrer.
Jerome se demande si ce qu'il fait fonctionne avec du C standard de
facon portable.
Et justement, je ne trouve que ce soit une très bonne idée.
Pour mémoire, il voudrait manipuler des fonctions prenant des types
pointeurs variés en paramètre, _sans_ utiliser de prototypes.
Ne pas utiliser de prototype est effectivement du C standard, mais je ne
suis pas sûr que ce soit une très bonne solution pour assurer la
portabilité.
D'un autre côté, en termes de perfs, je ne sais pas si les compilateurs
actuels sont assez finaux pour remplacer
void delete_memoire(void*p) { free(p); }
void delete_fichier(void*f) { fclose(p); }
par
.alias delete_memoire, free
.alias delete_fichier, fclose
et éviter l'indirection « inutile ».
Après, et c'est là que l'on mélange, il est vrai qu'avec la
quasi-totalité des machines actuelles, le seul effet de tout cela sera
des avertissements sans frais (même avec des prototypes).
Et il est bien clair que la tendance va dans le sens d'avoir de moins en
moins de ces cas particuliers, et au contraire d'avoir tous les
pointeurs (vers objets) avec des représentations identiques.
De plus, un clang récent (3.1) proteste de la manière ("incompatible
pointer type") quand j'écris du code qui va marcher « partout », comme
int (*delete_fichier)(void*) = fclose;
ou si j'écris du code beaucoup plus tendancieux, genre
Jerome se demande si ce qu'il fait fonctionne avec du C standard de facon portable.
Et justement, je ne trouve que ce soit une très bonne idée.
Pour mémoire, il voudrait manipuler des fonctions prenant des types pointeurs variés en paramètre, _sans_ utiliser de prototypes.
Ne pas utiliser de prototype est effectivement du C standard, mais je ne suis pas sûr que ce soit une très bonne solution pour assurer la portabilité.
D'un autre côté, en termes de perfs, je ne sais pas si les compilateurs actuels sont assez finaux pour remplacer void delete_memoire(void*p) { free(p); } void delete_fichier(void*f) { fclose(p); } par .alias delete_memoire, free .alias delete_fichier, fclose et éviter l'indirection « inutile ».
Après, et c'est là que l'on mélange, il est vrai qu'avec la quasi-totalité des machines actuelles, le seul effet de tout cela sera des avertissements sans frais (même avec des prototypes). Et il est bien clair que la tendance va dans le sens d'avoir de moins en moins de ces cas particuliers, et au contraire d'avoir tous les pointeurs (vers objets) avec des représentations identiques.
De plus, un clang récent (3.1) proteste de la manière ("incompatible pointer type") quand j'écris du code qui va marcher « partout », comme
int (*delete_fichier)(void*) = fclose;
ou si j'écris du code beaucoup plus tendancieux, genre