*Bonjour*
Tout est dans le titre. Si vous savez comment solutionner �a, je suis preneur.
*Merci d'avance*
Version imprimable
*Bonjour*
Tout est dans le titre. Si vous savez comment solutionner �a, je suis preneur.
*Merci d'avance*
La norme du C n'exige pas qu'un pointeur sur un objet sur void puisse �tre converti en pointeur sur une fonction. Assigner un void * � un pointeur de fonction est donc une op�ration non portable, d'o� le warning. Une mani�re de contourner le warning (autre que le d�sactiver) sans toutefois r�soudre le probl�me de portabilit� : utiliser memcpy au lieu de l'affectation.Citation:
hook.c:7: warning: ISO C forbids assignment between function pointer and `void *'
hook.c:8: warning: ISO C forbids assignment between function pointer and `void *'
Ici la syntaxe est tout simplement non standard, il faut initialiser l'objet pendant sa d�finition ou alors appeler �crire une fonction d'initialisation pour �tre 100% conforme et donc portable.Citation:
wrapper.c:19: warning: ISO C89 forbids specifying subobject to initialize
wrapper.c:20: warning: ISO C89 forbids specifying subobject to initialize
Je ne pense pas que l'ajout d'un cast puisse supprimer ce warning car le r�sultat sera le m�me : tu convertis toujours, par cast, un void * en pointeur de fonction, ce qui n'est pas portable. Mais c'est vrai qu'il faut tenter car je viens de me rendre compte que m�me le memcpy ne r�sout pas l'affaire. Si le cast non plus ne marche pas, et ben comme le code n'est pas portable, le seul moyen de supprimer le warning c'est de faire taire le compilateur (enlever aussi -pedantic et/ou -Wall et/ou -W).
Effectivement, si j'essaie sous GCC de transtyper � void * � vers le pointeur de fonction ad�quat, j'obtiens une erreur parce que le compilateur consid�re que le pointeur, tout void soit-il, reste un pointeur d'objet et donc ne peut �tre transform� en pointeur de fonction :
Code:
1
2
3
4
5
6
7
8
9 int main (void) { hook_t h; h._open = (int(*)(const char *,int,mode_t))dlsym( RTLD_NEXT, "open"); h._close = (int(*)(int))dlsym( RTLD_NEXT, "close" ); return 0; }
Code:
1
2
3
4 $ gcc -pedantic -ansi -W -Wall funcptrcast.c -o funcptrcast funcptrcast.c: In function �main�: funcptrcast.c:5: attention : ISO C interdit la conversion d'un pointeur d'objet vers un type de pointeur � une fonction funcptrcast.c:6: attention : ISO C interdit la conversion d'un pointeur d'objet vers un type de pointeur � une fonction
C'est d'ailleurs tr�s discutable parce que �a aurait du sens sur les architectures purement Harvard, mais �a devrait �tre une exception g�r�e par le compilo concern� et pas une g�n�ralit� de la norme C puisqu'il est �tabli qu'un pointeur, en g�n�ral, peut tr�s bien acc�der une zone de m�moire contenant du code sur la plupart des architectures conventionnelles.
Par contre, le contraire fonctionne bien ! :P
Code:
1
2 *(void **)&(h._open) = dlsym( RTLD_NEXT, "open"); *(void **)&(h._close) = dlsym( RTLD_NEXT, "close" );
C'est m�me sensiblement plus lisible parce que plus court, moins complexe et parce que le type est le m�me � chaque appel.
�DIT : La norme C99 (enfin, n1256) sp�cifie explicitement cette possibilit� :
Mais GCC cesse de se plaindre �galement d�s lors que l'on enl�ve � -pedantic �, m�me avec � -std=c89 �. La man page de GCC sp�cifie :Citation:
J.5.7 Function pointer casts
1 A pointer to an object or to void may be cast to a pointer to a function, allowing data to
be invoked as a function (6.5.4).
2 A pointer to a function may be cast to a pointer to an object or to void, allowing a
function to be inspected or modified (for example, by a debugger) (6.5.4).
Je ne sais pas dans quelle section de la norme ces contr�les stricts sont d�finis.Citation:
Issue all the warnings demanded by strict ISO C and ISO C++; reject all programs that use forbidden extensions, and some other programs that do not follow ISO C and ISO C++. For ISO C, follows the version of the ISO C standard specified by any -std option used.
[�]
Some users try to use -pedantic to check programs for strict ISO C conformance. They soon find that it does not do quite what they want: it finds some non-ISO practices, but not all---only those for which ISO C requires a diagnostic, and some others for which diagnostics have been added.
Ca marche !
Enfin disparus ces warnings et le code fonctionne toujours !
Merci � tous pour votre aide.
Obsidian est un authentique guru, merci � lui de prendre le temps de faire part de son savoir � des d�veloppeurs de mon niveau.
A l'occasion, il faudra que tu m'orientes sur un �change en MP ou un tuto balaise pour que je comprenne ces cast de fonctions qui me semblent encore bien myst�rieux.
Mais c'est toujours pas portable ;). Soyons bien d'accord, je parle de portabilit� absolue, c'est-�-dire vis-�-vis de la norme. Je ne remets pas du tout en cause le fait qu'il existe pas mal de plateformes pour lesquelles cette conversion a un sens.Citation:
Envoy� par Obsidian
Je pr�cise juste pour �viter certaines mauvaises interpr�tations, que ce n'est pas la norme qui le sp�cifie, mais un document en annexe : J.5 Common extensions. Ces "extensions populaires" ne font pas partie de la norme.Citation:
Envoy� par Obsidian
La stricte conformit� signifie juste que le programme n'utilise aucune fonctionnalit� non d�crite par la norme. Comme la norme ne dit rien sur l'interchangeabilit� entre pointeurs d'objet et pointeurs de fonction, un programme strictement conforme ne doit pas supposer qu'elle soit possible.Citation:
Envoy� par Obsidian
Obsidian : :hola:Citation:
Envoy� par TomTom68
Oui, oui, on est bien d'accord. J'ai justement mis le smiley pour montrer que c'�tait une mani�re de � tricher � (et qu'� mon avis, c'est la plus propre). C'est d'ailleurs �tonnant que GCC ne se plaigne pas dans ce sens-l� puisque la norme ne d�finit pas plus la conversion pointeur de fonction vers pointeur d'objet que l'inverse.
Mais comme tu l'as fait remarquer toi-m�me, le prototype de memcpy() est lui-aussi compos� de types void * et le probl�me demeure.
Oulala ! :-) C'est tr�s flatteur mais ce n'est malheureusement pas vrai. Melem et Diog�ne, par exemple, sont bien meilleurs que je ne le suis�Citation:
Obsidian : :hola:
Il y avait ici un commentaire qui expliquait bien la chose mais que, malheureusement, je ne retrouve plus. Quoiqu'il en soit, �a devient plus clair si tu admets qu'il y a au moins deux cas o� l'on d�finit le type d'un symbole en mettant des choses � sa droite : les tableaux et les fonctions.Citation:
A l'occasion, il faudra que tu m'orientes sur un �change en MP ou un tuto balaise pour que je comprenne ces cast de fonctions qui me semblent encore bien myst�rieux.
D�composons le cas qui t'int�resse : � (int(*)(const char *,int,mode_t)) �
- On commence par le symbole lui-m�me � quand il existe ! Dans le cas du prototype d'une fonction, on passe le type seul et le nom du param�tre est optionnel. Donc :
;Code:(int(*ptr)(const char *,int,mode_t))
- On le fait pr�c�der d'une �toile pour indiquer qu'il s'agit d'un pointeur de fonction, et pas d'une d�finition classique :
;Code:(int(*ptr)(const char *,int,mode_t))
- On est oblig� de les encadrer par une paire de parenth�ses pour �viter une ambiguit�. Sans elles, l'�toile s'appliquerait � gauche sur le type de la fonction et on d�finirait une fonction ordinaire renvoyant un pointeur sur quelque chose :
;Code:int(*ptr)(const char *,int,mode_t))
- On indique que cette expression repr�sente une fonction de la m�me fa�on qu'en d�clarant une fonction ordinaire : on la fait suivre d'une paire de parenth�ses contenant les arguments. Note que j'ai fait dispara�tre le symbole � ptr � :
;Code:(int(*)(const char *,int,mode_t))
- Une fonction, comme une variable, ou n'importe quelle expression math�matique, peut �tre �valu�e. Et puisqu'elle a une valeur, elle a un type. Du point de vue du programme, ce type sera celui de la valeur renvoy�e � l'ex�cution par la fonction. Tu qualifies donc l'expression enti�re en la faisant pr�c�der de son type, comme pour une variable :
;Code:(int(*)(const char *,int,mode_t))
- � ce stade, tu as une d�finition de type compl�te. Si tu voulais t'en servir pour d�clarer une variable (donc un pointeur de fonction) � ou un nom de type avec typedef, chose importante � tu placerais le symbole au milieu de la d�finition, l� o� j'ai �crit ptr dans mes premi�res lignes ;
- Mais tu peux surtout utiliser ce type anonyme tel quel soit en tant qu'argument de fonction (si tu veux passer ce pointeur � une fonction), soit dans le cadre d'un transtypage. Dans ce cas, comme avec les casts plus ordinaires, tu encadres le type complet avec des parenth�ses :
;Code:(int(*)(const char *,int,mode_t))
�videmment, il est beaucoup plus ais� de faire un typedef sur cette expression et d'utiliser ensuite le symbole ainsi d�fini de mani�re plus classique.
Dernier petit d�tail : si tu veux t'exercer, essaie de deviner comment on �crirait une fonction qui retournerait un tel pointeur (sans typedef). :-)
Moi aussi j'�tais persuad� que le m�me warning existait aussi dans l'autre sens. Avec ce que tu viens de nous montrer, je pense finalement que le soucis de gcc n'�tait pas vraiment de l'ordre de la stricte conformit� tel que le message veut nous le faire croire, mais plut�t de s�ret� et de s�curit� du programme. En effet, comme c'est l'OS qui dit quelles zones de la m�moire sont ex�cutables et lesquelles ne le sont pas, quelles zones peuvent �tre �crites et lesquelles ne le peuvent pas etc., gcc pense que la conversion d'un pointeur de fonction vers pointeur d'objet ou void est plus safe que l'inverse puisqu'elle consiste en quelque sorte � retirer un privil�ge (le privil�ge "ex�cutable") � la m�moire alors que dans le sens inverse en ajoute sans garanti que la m�moire la poss�de r�ellement. En gros c'est un peu comme le type * vers const type * qui est autoris� alors que l'inverse non.Citation:
Envoy� par Obsidian
Encore :hola::hola: pour ton humilit� :lol:.Citation:
Envoy� par Obsidian
Je me suis pos� la question aussi mais en fait, �a ne tient pas r�ellement non plus : dans l'absolu, un code peut �tre ex�cut� sans que le processus n'ait droit de lecture sur le code. C'est coh�rent d'un point de vue commercial. :-)
En fait, je pense que la s�paration nette entre pointeurs de fonctions et pointeurs de donn�es se justifie surtout pour les architectures Harvard. Si les bus d'adressage du code et des donn�es sont distincts et dans des espaces diff�rents, non seulement la conversion n'aura jamais de sens, mais elle ne sera pas forc�ment possible (formats diff�rents). Si on consid�re les architectures de Von Neumann comme �tant la g�n�ralit� et les Harvard comme des exceptions, alors il faudrait que le compilo l�ve une erreur dans cette derni�re architecture, ce qui rendrait le code normalis� non portable. Il est plus simple de dire que c'est interdit partout.
�a ne nous explique toujours pas pourquoi GCC est tol�rant sur ce point, et il doit y avoir une raison (probablement �crite en commentaires dans le code de GCC) mais, � vue de nez, je vois deux hypoth�ses possibles :
- Soit GCC a pr�vu un cas de failsafe qui marche toujours, justement pour �viter de se retrouver dans une impasse ;
- Soit (hypth�se privil�gi�e), �a fonctionne parce que dans le premier cas, il fallait convertir la valeur retourn�e par dlsym(), ce qui n'est pas forc�ment possible alors que dans le second, je d�pose directement la valeur telle quelle � l'endroit point� sans me soucier de ce qui s'y trouve et, donc, sans avoir � effectuer de conversion.
Mais par contre, la section 6.3.2.3 concernant les conversions de type pr�cise quand m�me quelque chose d'int�ressant :
Ceci est fait pour pouvoir repr�senter une adresse de fa�on num�rique et donc la sp�cifier en dur dans les environnement o� cela se justifie. Ex : � void * ptr = 0x12345678 �.Citation:
Envoy� par n1256�6.3.2.3
Du coup, j'ai l'impression que la technique la plus s�re et la plus � C-compliant � serait une horreur du style :
Code:
1
2 h._open = (int(*)(const char *,int,mode_t)) (unsigned long int) dlsym( RTLD_NEXT, "open" ); h._close = (int(*)(int)) (unsigned long int) dlsym( RTLD_NEXT, "close" );
� soit un double-cast consistant � transformer le pointeur void en entier (donc en adresse num�rique) et � retranstyper imm�diatement cet entier vers un nouveau pointeur. C'est l�gal sur toute la ligne et mes tests fonctionnent bien. �a a en plus l'avantage de nous laisser sp�cifier les vrais types terminaux au lieu de mentir en disant � void ** �.
Ca c'est pour la norme, la g�n�ralit�. Mais pour notre gcc qui compile ici sous un linux sur une architecture von neumann et qui n'a pas appr�ci� la conversion, je pense qu'on peut maintenir mon hypoth�se.Citation:
En fait, je pense que la s�paration nette entre pointeurs de fonctions et pointeurs de donn�es se justifie surtout pour les architectures Harvard.
intptr_t � la place de unsigned long serait encore mieux. Sinon c'est en effet, � mon avis, la meilleure fa�on de contourner les messages de warning caus�s par les casts pointeur d'objet <-> pointeur de fonction quand on veut les ignorer.Citation:
Du coup, j'ai l'impression que la technique la plus s�re et la plus � C-compliant � serait une horreur du style :
Code:
1
2 h._open = (int(*)(const char *,int,mode_t)) (unsigned long int) dlsym( RTLD_NEXT, "open" ); h._close = (int(*)(int)) (unsigned long int) dlsym( RTLD_NEXT, "close" );
C'est ce que je dis aussi : la norme choisit volontairement de ne pas d�finir les transtypages entre pointeurs de donn�es et pointeurs de fonction, probablement pour les raisons que je propose. Le fait que, du coup, cela reste tout-�-fait possible (et justifi�) sur une Von Neumann reste quand m�me un cas particulier, non universel. Et un code normalis� deviendrait non-portable.Citation:
Ca c'est pour la norme, la g�n�ralit�. Mais pour notre gcc qui compile ici sous un linux sur une architecture von neumann et qui n'a pas appr�ci� la conversion, je pense qu'on peut maintenir mon hypoth�se.
Voila ! C'est le type que je recherchais. Je te remercie.Citation:
intptr_t � la place de unsigned long serait encore mieux. Sinon c'est en effet, � mon avis, la meilleure fa�on de contourner les messages de warning caus�s par les casts pointeur d'objet <-> pointeur de fonction quand on veut les ignorer.
Bonjour,
Donc si j'ai bien suivi, il vaut mieux �crire ainsi la m�morisation du pointeur de fonction :Citation:
#include <stdint.h>
These allow you to declare variables of the same size as a pointer.
typedef int16_t intptr_t
Merci de confirmer.Code:
1
2 h._open = (int(*)(const char *,int,mode_t)) (intptr_t) dlsym( RTLD_NEXT, "open" ); h._close = (int(*)(int)) (intptr_t) dlsym( RTLD_NEXT, "close" );
C'est bien �a, mais c'est mieux si tu laisses un commentaire sans ambigu�t� avant ces lignes expliquant le pourquoi de la chose. Sinon, les gens qui passeront apr�s toi risquent de te prendre pour un programmeur tr�s sale. :-)
Bonjour, pour apporter ma modeste contribution � l'�difice, je voulais simplement rappeler que ce comportement est bien d�fini par la norme POSIX 2008:
Cordialement.Citation:
2.12.3 Pointer Types
All function pointer types shall have the same representation as the type pointer to void. Conversion of a function pointer to void * shall not alter the representation. A void * value resulting from such a conversion can be converted back to the original function pointer type, using an explicit cast, without loss of information.
Note:
The ISO C standard does not require this, but it is required for POSIX conformance.