J'ai une bibliothèque C (avec les en-têtes C) qui existe dans deux versions différentes.
L'une d'entre elles a une fonction qui ressemble à ceci:
#if HAS_ARGUMENT_COUNT(test, 5) test("a", "b", "c", true, 20); #elif HAS_ARGUMENT_COUNT(test, 4) test("a", "b", "c", true); #else #error "wrong argument count" #endif
et L'autre version ressemble à ceci:
int test(char * a, char * b, char * c, bool d)
(pour lequel E n'est pas donné comme paramètre de fonction mais il est codé dur dans la fonction elle-même).
La bibliothèque ou ses en-têtes ne définissent / n'incluent aucun moyen de vérifier la version de la bibliothèque afin que je ne puisse pas simplement utiliser un #if
ou #ifdef
pour vérifier une version Numéro.
Y a-t-il un moyen d'écrire un programme C qui peut être compilé avec les deux versions de cette bibliothèque, selon celui qui est installé lorsque le programme est compilé? De cette façon, les contributeurs qui souhaitent compiler mon programme sont gratuits d'utiliser l'une ou l'autre version de la bibliothèque et l'outil pourrait être compilé avec l'un ou l'autre.
Donc, pour clarifier, je cherche quelque chose comme ça (ou similaire):
int test(char * a, char * b, char * c, bool d, int e);
Y a-t-il un moyen de le faire en C? Je n'ai pas pu trouver un moyen.
La bibliothèque serait libogc ( https: // github .com / Devkitpro / libogc ) qui a changé sa définition de if_config
il y a quelque temps, et j'aimerais que mon programme fonctionne à la fois avec l'ancien et la nouvelle version. Je n'ai pas pu trouver d'identifiant de version dans la bibliothèque. Pour le moment, j'utilise une version modifiée de GCC 8.3.
8 Réponses :
Je ne vois aucun moyen de le faire avec C, si vous compiliez avec gcc
, une manière très très laide peut utiliser gcc aux-info
dans une commande et passant le nombre de paramètres avec -d
:
foo has 3 parameters
Cet extrait
#include <stdio.h> int foo(int a, int b, int c); #ifndef COUNT #define COUNT 0 #endif int main(void) { printf("foo has %d parameters\n", COUNT); return 0; }
sort p>
#!/bin/sh gcc -aux-info output.info demo.c COUNT=`grep "extern int foo" output.info | tr -dc "," | wc -m` rm output.info gcc -o demo demo.c -DCOUNT="$COUNT + 1" ./demo
Je pense qu'il n'y a aucun moyen de le faire au stade de la préproce (du moins pas sans certains scripts externes). D'un autre côté, il y a un moyen de détecter la signature d'une fonction au moment de la compilation si vous utilisez C11: _generic
. Mais rappelez-vous: vous ne pouvez pas l'utiliser dans une macro comme #if
parce que les expressions primaires ne sont pas évaluées à l'étape de prétraitement, vous ne pouvez donc pas choisir dynamiquement d'appeler la fonction avec Signature 1 ou 2 À ce stade.
typedef int (*TYPE_A)(char *, char *, char *, bool, int); typedef int (*TYPE_B)(char *, char *, char *, bool); int newtest(char *a, char *b, char *c, bool d, int e) { void (*func)(void) = (void (*)(void))&test; if (_Generic(&test, TYPE_A: 1, TYPE_B: 2, default: 0) == 1) { return ((TYPE_A)func)(a, b, c, d, e); } return ((TYPE_B)func)(a, b, c, d); }
Je suis désolé si cela ne répond pas à votre question. Si vous ne pouvez vraiment pas détecter la version du fichier d'en-tête de bibliothèque "stock", il existe des solutions de contournement où vous pouvez
Ceci est juste une horrible conception de bibliothèque.
Mise à jour: Après avoir lu les commentaires, je dois clarifier pour les futurs lecteurs qu'il n'est pas possible à l'étape de prétraitement, mais il est toujours possible au moment de la compilation. Il vous suffit de passer conditionnellement l'appel de fonction en fonction de mon extrait ci-dessus.
#define WEIRD_LIB_FUNC_TYPE(T) _Generic(&(T), \ int (*)(char *, char *, char *, bool, int): 1, \ int (*)(char *, char *, char *, bool): 2, \ default: 0) printf("test's signature: %d\n", WEIRD_LIB_FUNC_TYPE(test)); // will print 1 if 'test' expects the extra argument, or 2 otherwise
Cela fonctionne en effet bien qu'il puisse être controversé de lancer une fonction de cette façon. L'avantage est, comme l'a dit @ pizzapants184, la condition sera optimisée car l'appel _generic
sera évalué au temps de compilation.
Vous pouvez décider de la version à appeler, tout simplement pas avec #if
. Utilisez simplement _generic
pour sélectionner l'expression à utiliser.
Vous pouvez enregistrer la valeur renvoyée par le _generic
et l'utiliser dans un normal si
. Notez que vous devrez peut-être jeter la fonction au type approprié pour éviter "la fonction d'appel avec un mauvais nombre de paramètres" Erreurs dans la branche non prises. Tout compilateur d'optimisation verra que la valeur de la _generic
est une constante de temps de compilation et optimisera la branche non pris. Par exemple: godbolt.org/z/t16jhk
Officiellement, les pointeurs de fonction ne peuvent pas être jetés à void *
, mais ils peuvent être jetés les uns aux autres, alors essayez ((type_a) & test)
et (((( Type_b) & test)
et sautez le void *
étape.
Cela doit être fait à l'étape Configurer
, en utilisant une étape de test automatique (ou cmake, ou autre) - en gros, en essayant de compiler un petit programme qui utilise la signature à cinq paramètres, et Voir s'il compile avec succès - pour déterminer quelle version de la bibliothèque est utilisée. Qui peut être utilisé pour définir une macro de préprocesseur que vous pouvez utiliser dans un #if
dans votre code.
Bien que la courbe d'apprentissage soit assez raide. Pouvez-vous faire référence à un guide pour les perplexes?
Voir le manuel autoconf , et en particulier ac_compile_ifelse .
... ce qui n'appelle guère une simple introduction à quelqu'un qui ne l'a jamais rencontré auparavant.
Cela devrait être facile à faire avec un simple script de shell même sans devenir complet AutoConf. Exécutez simplement le chèque et générez un fichier d'en-tête en une ligne. Tant que vous n'avez pas trop de choses, vous devez configurer au moment de la compilation de toute façon ...
@Markmorganlloyd Si vous connaissez un bon tutoriel, je serais heureux d'une suggestion; Sinon, votre Google est aussi bon que le mien. J'ai appris d'un livre O'Reilly hors imprimé.
Cette réponse serait améliorée par un extrait d'exemple de code.
@Sneftel J'ai peur d'être aussi perplexe que vous :-) C'est l'une des situations où nous pourrions connaître la réponse "correcte" mais pas nécessairement comment l'exploiter à partir de zéro. J'ai fait quelques modifications triviales aux fichiers AutoConf etc. existants, mais c'est vraiment aussi loin que je suis parti.
Via l'article "AutoTools" de WP, pdf à lrde.epita.fr/~adl/autotools .html est un résumé OKISH (utilisez la version "Handout"). La section 10 traite de la rédaction de macros personnalisées, où OP devrait probablement aller si la bibliothèque qu'il veut utiliser n'est pas encore prise en charge.
Si votre compilateur est un gcc , par ex. Certains gcc 10 En novembre 2020, vous pourriez écrire le vôtre plugin gcc Pour vérifier la signature dans vos fichiers d'en-tête et émettre un préprocesseur C approprié et associé #define
-s et / ou #ifdef
, à la GNU autoconf ). Votre plugin pourrait (par exemple) pour remplir certains sqlite base de données et vous généreriez plus tard un certain #include code > -d Fichier d'en-tête.
vous établiriez alors votre construire l'automatisation (par exemple Votre makefile
) pour utiliser ce plugin gcc et les données qu'il a calculées. >
Pour une seule fonction, une telle approche est exagérée.
Pour un grand projet, cela pourrait avoir du sens, en particulier si vous décidez également de coder également certains validateurs de règles de codage spécifiques au projet dans votre plugin GCC.
L'écriture d'un plugin GCC pourrait prendre des semaines de votre temps , et vous devrez peut-être corriger votre code source de plugin lorsque vous passez à un futur gcc 11.
Voir aussi ce projet Rapport et le Href = "européen chariot et Decoder Projets (financement des travaux décrits dans ce rapport).
btw, vous pourriez demander aux auteurs de cette bibliothèque d'ajouter des métadonnées de versioning. L'inspiration pourrait provenir de libonion ou glib ou < a href = "https://gcc.gnu.org/onlinedocs/jit/" rel = "nofollow noreferrer"> libgccjit . p>
btw, comme a commenté à juste titre dans ce problème , vous ne devez pas utiliser un non-souligné sans cesse ancienne version d'une bibliothèque OpenSource. Utilisez celui qui est travaillé.
J'aimerais que mon programme fonctionne à la fois avec l'ancien et la nouvelle version.
Faire fonctionner votre programme avec l'ancienne version (non entretenue) de libogc et eux. Je ne comprends pas pourquoi vous dépendriez d'une vieille bibliothèque non remplie, si vous pouvez éviter de le faire.
ps. Vous pouvez bien sûr écrire un plugin pour GCC 8. Je recommande de passer à GCC 10: il s'est amélioré.
"Je ne comprends pas pourquoi ..." Je suppose que vous n'avez jamais eu à déboguer ce qui se passe lorsque quelqu'un utilise une version d'un fichier d'en-tête dans son code et des liens avec une version différente de la bibliothèque. (Et l'emplacement de l'en-tête et de la bibliothèque peut avoir été choisi automatiquement par une procédure de construction créée par quelqu'un qui n'était ni le programmeur ni le débogueur!)
@Alephzero: L'OP demande une détection automatisée de temps de compilation en fonction de l'en-tête qu'ils incluaient, pas de la bibliothèque contre laquelle ils sont liés. Cela ne résout pas le problème ABI-Mismatch dont vous parlez. En fait, faire fonctionner votre programme avec les deux versions pourrait signifier éviter une nouvelle fonction uniquement disponible dans la nouvelle version, ce qui aurait pu vous sauver en provoquant une erreur de liaison soit au moment de la construction, soit en temps de liaison dynamique, car l'ancienne bibliothèque La version ne fournirait pas ce symbole.
vous ne le faites pas.
Les outils avec lesquels vous travaillez sont liés statiquement et ne prennent pas en charge les versioning. Vous pouvez le contourner en utilisant toutes sortes d'astuces et de conseils qui ont été mentionnés, mais à la fin de la journée, ce sont des travaux de patch laids de quelque chose que vous essayez de faire qui n'a aucun sens dans ce contexte (boîte à outils / environnement de code) .
Vous concevez votre code pour la version de la boîte à outils que vous avez installée. c'est une exigence difficile. Je ne comprends pas non plus pourquoi vous voudriez concevoir votre code GameCube / Wii pour permettre de construire sur différentes versions. La boîte à outils change constamment pour corriger les bogues, les hypothèses, etc. Si vous voulez que votre code utilise une ancienne version qui a potentiellement des bogues ou faire des choses mal, c'est sur vous.
Je pense que vous devriez réaliser quel type de travail de botch vous avez affaire ici si vous avez besoin ou que vous voulez faire cela avec une boîte à outils en constante évolution ..
Je pense aussi, mais c'est parce que je connais vous et votre relation avec Devkitpro, je suppose que vous demandez ceci parce que vous avez une version plus ancienne installée et que vos buts CI ne fonctionneront pas car ils utilisent une version plus récente (de Docker) . c'est soit ceci, soit vous avez plusieurs versions installées sur votre machine pour un projet différent que vous créez (mais ne mettez pas à jour la source pour une raison étrange).
Mes versions CI fonctionnent très bien parce que je ne comptez pas sur les conteneurs Docker existants. Et oui, j'ai plusieurs versions de Devkitpro et libogc installées sur ma machine, exactement à cause de problèmes comme celui-ci. Parce qu'il est impossible d'écrire un logiciel pour fonctionner avec plusieurs versions. Je construis beaucoup de logiciels différents de différents développeurs et ce n'est pas mon travail de les garder tous à jour.
Ce n'est pas votre travail de compiler de nombreux logiciels différents de différents développeurs, mais nous y sommes. On vous a demandé à plusieurs reprises de cesser d'essayer de fournir et de soutenir des versions anciennes et obsolètes de nos outils et des bibliothèques. Veuillez ne pas faire cela. Vous créez plus de problèmes que vous ne résolvez.
Je suis d'accord avec WinterMute ici. Ce n'est pas votre travail de créer ces anciennes applications. Même si c'était le cas, ne pensez-vous pas qu'il est temps de mettre à jour et de réparer le code pour fonctionner avec du code / des bibliothèques modernes et fixes? Pensez-vous qu'il m'a fallu beaucoup de temps pour mettre à jour Priiloader avec chaque version de Libogc? non. Au contraire, je n'ai généralement rien à faire. A-t-il fallu beaucoup de temps avant de pouvoir construire l'ancien code de geckoos sur le libogc moderne? Non, 30min max (+ correctifs de code). C'est pourquoi j'ai dit «travail de botch». Réparez-le. Quel est le pire qui puisse arriver? Le mauvais code ne fonctionne plus et nécessite un refactor?
Tenter de prendre en charge le code de compilation avec plusieurs versions d'une bibliothèque statique ne sert à rien. Mettez à jour votre code pour utiliser la dernière version et arrêter de rendre la vie plus difficile qu'elle ne doit l'être.
C'est vraiment vrai pour cette bibliothèque spécifiquement - je vois que vous avez également commenté github.com/devkitpro/libogc/ Problèmes / 102
Cela pourrait être vrai pour cette bibliothèque spécifiquement, mais ce n'est certainement pas vrai pour les bibliothèques en général. En tant que développeur de bibliothèque moi-même, je déteste les vieilles versions autant que vous, et je souhaite qu'ils disparaissent de la terre. Mais pragmatiquement, il y a de bonnes raisons pour les développeurs professionnels de s'en tenir aux anciennes versions des bibliothèques, en particulier aux stades tardifs d'un cycle de développement. Le diable vous savez...
Dans le langage C d'origine de Dennis Ritchie, une fonction pourrait être transmise n'importe quel nombre d'arguments, quel que soit le nombre de paramètres qu'il attendait, à condition que la fonction n'ait accès à aucun paramètre au-delà de ceux qui lui aient été transmis. Même sur les plates-formes dont la convention d'appels normale ne serait pas en mesure de s'adapter à cette flexibilité, les compilateurs C utilisaient généralement une convention d'appel différente qui pourrait le supporter à moins que les fonctions ne soient marquées de qualifications comme Pascal
pour indiquer qu'ils devraient Utilisez la convention d'appel ordinaire.
Ainsi, quelque chose comme ce qui suit aurait eu un comportement entièrement défini dans le langage C original de Ritchie:
int addTwoOrThree(count, x, y, z) int count, x, y, z; { if (count == 3) return x+y+z; else return x+y; } int test() { return count(2, 10,20) + count(3, 1,2,3); }
Parce qu'il y a certaines plateformes où il serait Soyez peu pratique pour prendre en charge une telle flexibilité par défaut, la norme C ne nécessite pas que les compilateurs traitent de manière significative des appels vers des fonctions qui ont plus ou moins d'arguments que prévu, sauf que les fonctions qui ont été déclarées avec un ...
Le paramètre "s'attendra" à un nombre d'arguments qui est au moins aussi grand que le nombre de paramètres réels spécifiés. Il est donc rare que le code soit écrit qui exploiterait la flexibilité qui était présente dans la langue de Ritchie. Néanmoins, de nombreuses implémentations accepteront toujours le code écrit pour prendre en charge ce modèle si la fonction appelée se trouve dans une unité de compilation distincte des appelants, et il est déclaré mais non prototypé dans les unités de compilation qui l'appellent.
>
Je ne suis pas sûr que cela résout votre problème spécifique, ou vous aide du tout, mais voici un engin préprocesseur, en raison de Laurent Deniau, qui compte le nombre d'arguments transmis à une fonction au temps de compilation .
Signification, quelque chose comme args_count (a, b, c)
évalue (au moment de la compilation) à la constante de constante de la constante 3
, et quelque chose comme args_count (__ va_args __)
(dans une macro variadique) évalue (au moment de la compilation) au nombre d'arguments transmis à la macro.
Cela vous permet, par exemple, d'appeler des fonctions variadiques sans spécifier le nombre d'arguments, parce que le préprocesseur le fait pour vous.
donc, si vous avez une fonction variadique
#include <stdint.h> #include <stdarg.h> #include <stdio.h> #define m_args_idim__get_arg100( \ arg00,arg01,arg02,arg03,arg04,arg05,arg06,arg07,arg08,arg09,arg0a,arg0b,arg0c,arg0d,arg0e,arg0f, \ arg10,arg11,arg12,arg13,arg14,arg15,arg16,arg17,arg18,arg19,arg1a,arg1b,arg1c,arg1d,arg1e,arg1f, \ arg20,arg21,arg22,arg23,arg24,arg25,arg26,arg27,arg28,arg29,arg2a,arg2b,arg2c,arg2d,arg2e,arg2f, \ arg30,arg31,arg32,arg33,arg34,arg35,arg36,arg37,arg38,arg39,arg3a,arg3b,arg3c,arg3d,arg3e,arg3f, \ arg40,arg41,arg42,arg43,arg44,arg45,arg46,arg47,arg48,arg49,arg4a,arg4b,arg4c,arg4d,arg4e,arg4f, \ arg50,arg51,arg52,arg53,arg54,arg55,arg56,arg57,arg58,arg59,arg5a,arg5b,arg5c,arg5d,arg5e,arg5f, \ arg60,arg61,arg62,arg63,arg64,arg65,arg66,arg67,arg68,arg69,arg6a,arg6b,arg6c,arg6d,arg6e,arg6f, \ arg70,arg71,arg72,arg73,arg74,arg75,arg76,arg77,arg78,arg79,arg7a,arg7b,arg7c,arg7d,arg7e,arg7f, \ arg80,arg81,arg82,arg83,arg84,arg85,arg86,arg87,arg88,arg89,arg8a,arg8b,arg8c,arg8d,arg8e,arg8f, \ arg90,arg91,arg92,arg93,arg94,arg95,arg96,arg97,arg98,arg99,arg9a,arg9b,arg9c,arg9d,arg9e,arg9f, \ arga0,arga1,arga2,arga3,arga4,arga5,arga6,arga7,arga8,arga9,argaa,argab,argac,argad,argae,argaf, \ argb0,argb1,argb2,argb3,argb4,argb5,argb6,argb7,argb8,argb9,argba,argbb,argbc,argbd,argbe,argbf, \ argc0,argc1,argc2,argc3,argc4,argc5,argc6,argc7,argc8,argc9,argca,argcb,argcc,argcd,argce,argcf, \ argd0,argd1,argd2,argd3,argd4,argd5,argd6,argd7,argd8,argd9,argda,argdb,argdc,argdd,argde,argdf, \ arge0,arge1,arge2,arge3,arge4,arge5,arge6,arge7,arge8,arge9,argea,argeb,argec,arged,argee,argef, \ argf0,argf1,argf2,argf3,argf4,argf5,argf6,argf7,argf8,argf9,argfa,argfb,argfc,argfd,argfe,argff, \ arg100, ...) arg100 #define m_args_idim(...) m_args_idim__get_arg100(, ##__VA_ARGS__, \ 0xff,0xfe,0xfd,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf2,0xf1,0xf0, \ 0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0, \ 0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0, \ 0xcf,0xce,0xcd,0xcc,0xcb,0xca,0xc9,0xc8,0xc7,0xc6,0xc5,0xc4,0xc3,0xc2,0xc1,0xc0, \ 0xbf,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0, \ 0xaf,0xae,0xad,0xac,0xab,0xaa,0xa9,0xa8,0xa7,0xa6,0xa5,0xa4,0xa3,0xa2,0xa1,0xa0, \ 0x9f,0x9e,0x9d,0x9c,0x9b,0x9a,0x99,0x98,0x97,0x96,0x95,0x94,0x93,0x92,0x91,0x90, \ 0x8f,0x8e,0x8d,0x8c,0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80, \ 0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x74,0x73,0x72,0x71,0x70, \ 0x6f,0x6e,0x6d,0x6c,0x6b,0x6a,0x69,0x68,0x67,0x66,0x65,0x64,0x63,0x62,0x61,0x60, \ 0x5f,0x5e,0x5d,0x5c,0x5b,0x5a,0x59,0x58,0x57,0x56,0x55,0x54,0x53,0x52,0x51,0x50, \ 0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40, \ 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, \ 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, \ 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, \ 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, \ ) typedef struct{ int32_t x0,x1; }ivec2; int32_t max0__ivec2(int32_t nelems, ...){ // The largest component 0 in a list of 2D integer vectors int32_t max = ~(1ll<<31) + 1; // Assuming two's complement va_list args; va_start(args, nelems); for(int i=0; i<nelems; ++i){ ivec2 a = va_arg(args, ivec2); max = max > a.x0 ? max : a.x0; } va_end(args); return max; } #define max0_ivec2(...) max0__ivec2(m_args_idim(__VA_ARGS__), __VA_ARGS__) int main(){ int32_t max = max0_ivec2(((ivec2){0,1}), ((ivec2){2,3}, ((ivec2){4,5}), ((ivec2){6,7}))); printf("%d\n", max); }
où vous devez (généralement) Passez le nombre d'arguments n
, vous pouvez automatiser ce processus en écrivant une macro variadique "frontend"
#define function_frontend(...) function_backend(args_count(__VA_ARGS__), __VA_ARGS__)
Et maintenant vous appelez function_fronttend ()
avec autant d'arguments que vous le souhaitez:
Je vous ai fait tutoriel youtube >
Je ne comprends pas comment cela m'aiderait? J'aurais encore besoin de comprendre d'une manière ou d'une autre si j'ai besoin d'appeler le test avec 4 ou 5 arguments de l'intérieur de la macro, non?
Quelle est la taille de cette bibliothèque C? Douzaine de fonctions, ou des milliers d'entre elles? Un millier de lignes de C ou un million d'entre eux? Quel est votre compilateur C? s'il vous plaît modifier Votre question pour l'améliorer.
OP: Serais en mesure de partager la bibliothèque C exactement?
Je m'attendrais à ce que toute personne fournissant différentes versions API d'une bibliothèque définisse également une macro
version
qui peut être utilisée pour détecter l'API appropriée. C'est une bibliothèque vraiment bizarre ...Ajout de github.com/devkitpro/libogc/issues/102
Merci d'avoir ouvert le rapport de bogues, mais ce n'est aussi que quelque chose qui pourrait aider s'ils changent autour de plus de fonctions dans les versions futures, pas particulièrement pour cette instance.
Et des commentaires comme celui qui venait d'être publié sur le rapport de bogue était l'une des raisons pour lesquelles j'ai demandé une solution de contournement, alors au lieu de demander une solution appropriée dans leur bugtracker ... j'adorerais commenter ce bug, mais le propriétaire du référentiel m'a bloqué pour avoir posé une question similaire il y a quelque temps.
ne pas n'omettez pas le prototype approprié, n'appelez pas avec 5 arguments (même lorsque vous utilisez la bibliothèque où la fonction prend 4 arguments). Je répète: ne fais pas ça