1

Salut,
J'ai besoin de faire un wrapper autour d'une fonction avec un nombre d'arguments variable.
En gros, mon wrapper demande un nombre d'arguments variable, et appelle à son tour la fonction d'origine avec les même arguments...
C'est possible en C/C++ ou pas ? (Je connais mal cet aspect du C sad)
Merci d'avance !


PS aux admin : -s à variable dans le titre svp ? Merci happy

2

doit être chiant ça

je suppose que tes args sont sur la pile, en gros j'appellerais la fonction wrappée avec un pointeur vers les données initiales

même pas en fait? J'ai peur qu'il faille faire des trucs sales avec sp sad

en général ce qu'on fait c'est appeler la version va_list de la fonction wrappée mais je suppose que tu l'as pas, si tu le demandes...

3

squalyl (./2) :
en général ce qu'on fait c'est appeler la version va_list de la fonction wrappée mais je suppose que tu l'as pas, si tu le demandes...

La version va_list ? C'est à dire ?

4

// a printf who write in the log
void printl(const char * format, ...)
{	va_list va;
	va_start(va, format);
	wvsprintf(dbg,format,va);
	log(dbg,lastLogFile,0);
}
et la le mec il le pécho par le bras et il lui dit '

5

Oui mais non, puisque j'imagine que le problème se trouve au moment où il doit appeler sa 2eme fonction. Si j'ai bien saisi, ça bloque ici :
void func1 (int a, ...)
{
    // init va_args

    func2 (/*comment écrire les arguments ici, puisqu'il y en a un nombre variable ?*/);
}

J'ai eu le même problème et j'ai du poster un topic sur yN il y a 5 ans (bonne chance pour le trouver grin), mais de mémoire personne n'a proposé de solution et j'ai fini par contourner avec une méthode crade sad
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

6

Pen^2: en général une fonction a args variables est un wrapper sur la même fonction, mais qui utilise un va_list, du coup si tu veux un autre wrapper c'est plus facile de wrapper la fonction qui utilise déja un va_list

exemple:

void sprintf(char *buf, const char * format, ...)
{ va_list va;
va_start(va, format);
vsprintf(buf,format,va);
va_end(va);
}

du coup si tu veux wrapper sprintf, vaut mieux directement wrapper vsprintf
http://www.manpagez.com/man/3/vsprintf/

7

(cross, et je pense aussi qu'il faut passer par du crade, genre manipuler SP, ce qui ultra bof si le wrapper a des variables locales, et qui n'est pas portabe)

8

En C/C++ standard, ce n'est pas possible de recréer des varargs à partir d'une va_list, c'est pour ça que les fonctions v* existent dans la stdio.

GCC permet de faire ça sous certaines conditions (assez contraignantes): http://gcc.gnu.org/onlinedocs/gcc/Constructing-Calls.html.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

9

Zephyr (./5) :
Oui mais non, puisque j'imagine que le problème se trouve au moment où il doit appeler sa 2eme fonction. Si j'ai bien saisi, ça bloque ici :
void func1 (int a, ...)
{
    // init va_args

    func2 (/*comment écrire les arguments ici, puisqu'il y en a un nombre variable ?*/);
}

J'ai eu le même problème et j'ai du poster un topic sur yN il y a 5 ans (bonne chance pour le trouver grin), mais de mémoire personne n'a proposé de solution et j'ai fini par contourner avec une méthode crade sad

C'est exactement ça smile
Je vais essayer de trouver ton topic. Tu te souviens de la solution crade en question ?


squalyl (./6) :
Pen^2: en général une fonction a args variables est un wrapper sur la même fonction, mais qui utilise un va_list, du coup si tu veux un autre wrapper c'est plus facile de wrapper la fonction qui utilise déja un va_list

exemple:

void sprintf(char *buf, const char * format, ...)
{ va_list va;
va_start(va, format);
vsprintf(buf,format,va);
va_end(va);
}

du coup si tu veux wrapper sprintf, vaut mieux directement wrapper vsprintf
http://www.manpagez.com/man/3/vsprintf/

OK, je vois. Je vais regarder si un prototype de ce genre existe... smile
squalyl (./7) :
(cross, et je pense aussi qu'il faut passer par du crade, genre manipuler SP, ce qui ultra bof si le wrapper a des variables locales, et qui n'est pas portabe)

arg grin
Kevin Kofler (./8) :
En C/C++ standard, ce n'est pas possible de recréer des varargs à partir d'une va_list, c'est pour ça que les fonctions v* existent dans la stdio.

GCC permet de faire ça sous certaines conditions (assez contraignantes): http://gcc.gnu.org/onlinedocs/gcc/Constructing-Calls.html.

Merci, je vais jeter un œil, mais il se trouve qu'en plus mon code doit être portable cheeky

10

Bon ben c'est bon en fait, il y a bien un prototype qui utilise une va_list \o/ (et même un tableau : il font ça bien les gens de chez Sun hehe)
    void (JNICALL *CallStaticVoidMethod)
      (JNIEnv *env, jclass cls, jmethodID methodID, ...);
    void (JNICALL *CallStaticVoidMethodV)
      (JNIEnv *env, jclass cls, jmethodID methodID, va_list args);
    void (JNICALL *CallStaticVoidMethodA)
      (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args);


Merci beaucoup à tous happy
Vous me retirez une fière chandelle du pied oui

11

ah bah oui en JNI c'était forcément prévu ce genre de truc, t'aurais du le dire ^^

12

Désolé hehe

13

pour la peine tu t'autofouetteras 42 fois ce soir, ça t'apprendra.

14

embarrassed

En attendant ça ne fonctionne pas pour l'instant :/ Ça compile mais la JVM plante :/ Il doit se passer un truc louche...

15

c'est une méthode java a arguments variable type Formatter.format(String,...) que t'appelles?

parce que dans ce cas niveau java les args sont passés dans un Object[]

avec cette api tu as un va_arg mais ils doivent correspondre a la signature de la méthode que tu appelles...

16

Non non, c'est un
public static void main( String[] args )

l'appel fonctionne très bien si je fais un appel direct avec un
jfcpp.jniEnv->CallStaticVoidMethod(jcMain, mid, argsArray) ;
avec argsArray un
jobjectArray

mais si je fais un
jfcpp.callStaticVoidMethod(jcMain, mid, argsArray) ;
avec callStaticVoidMethod défini comme suit
      void
      JNI::JavaFromCpp::
callStaticVoidMethod( const jclass& javaClass,
                      const jmethodID& methodID, 
                      ... )
      const
{
   va_list vl ;
   va_start(vl, methodID) ;

   jniEnv->CallStaticVoidMethodV(javaClass, methodID, vl) ;

   //va_end(vl) ;
}

ça plante dans la JVM dès que je veux utiliser les arguments sad
Dans l'exemple de r043v, le va_end n'est pas utilisé : est-ce normal ?

17

//      void
//      JNI::JavaFromCpp::
//callStaticVoidMethod( const jclass& javaClass,
//                      const jmethodID& methodID, 
//                      ... )
//      const
//{
//   va_list vl ;
//   va_start(vl, methodID) ;
//
//   jniEnv->CallStaticVoidMethodV(javaClass, methodID, vl) ;
//
//   va_end(vl) ;
//}


      void
      JNI::JavaFromCpp::
callStaticVoidMethod( const jclass& javaClass,
                      const jmethodID& methodID, 
                      const jobjectArray& jo )
      const
{
   jniEnv->CallStaticVoidMethod(javaClass, methodID, jo) ;
}


Y a-t-il une raison évidente qui m'échappe pour que la première version plante la JVM et que la seconde fonctionne bien ? confus

18

oui c'est absolument normal, en java un tableau est un objet instance de la classe tableau de truc, il faut le construire à la main , y ajouter des instances de string construites à partir de tes char*, puis le passer à main().

un tableau de string est totalement différent qu'une liste de paramètres, dans le premier cas tu as un unique argument de type tableau, dans le 2e cas tu fais un truc non autorisé parce que la méthode que tu appelles DOIT avoir le prototype requis, tu peux pas lui passer n'importe quoi!


(et non c'est pas normal, il faut utiliser va_end, en général il fait rien mais on sait jamais)

edit: tu vas devoir utiliser

NewObjectArray(env,len,class,init=null)
SetObjectArrayElement(env,arr,index,val)

en donnant à la dernière des trucs obtenus avec NewStringUTF ou NewString si tu veux utiliser l'encodage local

après le retour de ta fonction tu devras utiliser ReleaseString[UTF]Chars
(y'a pas de releaseArray pour des objets car c'est géré par le garbage collector)

19

Ah non mais dans les deux cas je passe exactement la même chose, à savoir un jobjectarray de jstring !
Je ne change pas le code appelant, c'est juste que je commente alternativement l'une ou l'autre des implémentations smile

cross edit : oui oui, j'utilise bien NewObjectArray et SetObjectArrayElement, pas de problème de ce côté là smile

20

D'accord, alors je comprends pas trop, c'est strange.
dans ma VM à moi ça marche et ça devrait pas poser de problème.
Bug de la jni? (flemme d'aller chercher les sources d'openjdk)

21

Peut-être que le problème se situe ailleurs, je vais essayer avec une méthode qui utilise uniquement des types primitifs en paramètre.

Sinon j'avais lu qu'on n'avait rien à libérer arpès un jniEnv->NewStringUTF(""), tu confirmes qu'il faut bien libérer avec un ReleaseStringUTFChars() ?

22

oui on a besoin, les strings obtenues par NewString ne sont pas gérées par le GC.

23

OK merci.

24

Bon, j'ai finalement trouvé le problème. Ça fonctionne si je n'utilise pas le passage par référence dans les paramètres qui précèdent le va_args trifus (i.e. javaClass et methodID)
Quelqu'un sait-il pourquoi ??? confus

Sur ce, je rentre à ma maison© embarrassed

25

ah je découvre là grin

peut être parce que le C++ est juste un wrapper sur le C, et que le code C attend des pointeurs grin

26

C'est très possible, en effet... #trisick#

27

Vu que tu as l'air de bien connaître JNI, j'ai une autre petite question. Comment fais tu pour initialiser proprement la JVM depuis un code C++ ?
JNI_CreateJavaVM(&jvm, reinterpret_cast<void**>(&jniEnv), &vm_args) ;
fonctionne, mais le cast vers void** génère un warning sous GCC sad ("warning: dereferencing type-punned pointer will break strict-aliasing rules")
Avant, il me semble que le proto de JNI_CreateJavaVM attendait un JNIEnv**, mais désormais c'est un void** qu'il attend confus cry

28

je connais pas cette partie, j'ai juste fait des DLL en C pour wrapper des fonctions natives.

apparemment dans la doc de java 6 c'est pas un void.

http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/invocation.html#wp9502

jint JNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env, void *vm_args);
The second argument to JNI_CreateJavaVM is always a pointer to JNIEnv *
confus Elle vient d'ou, ta doc?

29

ben je suppose qu'ils ont oublié de mettre à jour cette partie de la documentation, parce qu'un peu plus bas (sous le tableau), ils utilisent :
JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);

et le proto de jni.h est bien :
_JNI_IMPORT_OR_EXPORT_ jint JNICALL
JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args);

sad

30

Bon, j'ai utilisé une union finalement, mais j'aimerais bien savoir pourquoi ils utilisent un void** quand même ! embarrassed