Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

va_start et non-POD

10 réponses
Avatar
Luc Hermitte
Bonjour,

Je savais que l'on ne pouvait pas passer d'objets non-POD dans
l'ellipse, mais l=E0 je viens de d=E9couvrir un autre truc qui m'a quelque
peu surpris.
Si le dernier param=E8tre formel avant l'ellipse est un non-POD, alors
le compilateur se plaint que l'on a pass=E9 un non-POD =E0 va_start.

C'est un comportement normal, ou bien il s'agit d'une excentricit=E9
d'un compilateur incapable de calculer le d=E9but de la pile bien qu'il
dispose de tout le n=E9cessaire pour connaitre le type du param=E8tre =E0
l'int=E9rieur du code appel=E9 ?

--
Luc Hermitte

10 réponses

Avatar
Gabriel Dos Reis
Luc Hermitte writes:

| Bonjour,
|
| Je savais que l'on ne pouvait pas passer d'objets non-POD dans
| l'ellipse, mais là je viens de découvrir un autre truc qui m'a quelque
| peu surpris.
| Si le dernier paramètre formel avant l'ellipse est un non-POD, alors
| le compilateur se plaint que l'on a passé un non-POD à va_start.
|
| C'est un comportement normal, ou bien il s'agit d'une excentricité
| d'un compilateur incapable de calculer le début de la pile bien qu'il
| dispose de tout le nécessaire pour connaitre le type du paramèt re à
| l'intérieur du code appelé ?

Excentricité du programmeur.

Passer un non-POD comment argumnent dans une position '...' invoque un
comportement indéfini. Un compilateur non-excentrique pourrait
reformater le disque dur.
Avatar
Luc Hermitte
On 29 mai, 16:29, Gabriel Dos Reis wrote:
Luc Hermitte writes:
| Je savais que l'on ne pouvait pas passer d'objets non-POD dans
| l'ellipse, mais là je viens de découvrir un autre truc qui m'a quel que
| peu surpris.
| Si le dernier *paramètre formel* *avant* l'ellipse est un non-POD, al ors
| le compilateur se plaint que l'on a passé un non-POD à va_start.
|
| C'est un comportement normal, ou bien il s'agit d'une excentricité
| d'un compilateur incapable de calculer le début de la pile bien qu'il
| dispose de tout le nécessaire pour connaitre le type du paramètre à
| l'intérieur du code appelé ?

Excentricité du programmeur.

Passer un non-POD comment argumnent dans une position '...' invoque un
comportement indéfini. Un compilateur non-excentrique pourrait
reformater le disque dur.



Justement, je ne passe pas le non POD dans l'ellipse (cf "paramètre
formel" et "avant l'ellipse" dans mon message original). Je le passe
avant. Comme dans:
void f(std::string s, ...) { // volontairement par copie: j'avais
vu le
va_list ap; // problème avec un autre petit type
pris par copie
va_start(ap, s); // <- le compilateur n'est pas content ici
...
f("toto %i titi %s tutu %pn", 42, "foobar", (void*)ptr);

--
Luc Hermitte
Avatar
pjb
Luc Hermitte writes:

On 29 mai, 16:29, Gabriel Dos Reis wrote:
Luc Hermitte writes:
| Je savais que l'on ne pouvait pas passer d'objets non-POD dans
| l'ellipse, mais là je viens de découvrir un autre truc qui m'a quelque
| peu surpris.
| Si le dernier *paramètre formel* *avant* l'ellipse est un non-POD, alors
| le compilateur se plaint que l'on a passé un non-POD à va_start.
|
| C'est un comportement normal, ou bien il s'agit d'une excentricité
| d'un compilateur incapable de calculer le début de la pile bien qu'il
| dispose de tout le nécessaire pour connaitre le type du paramètre à
| l'intérieur du code appelé ?

Excentricité du programmeur.

Passer un non-POD comment argumnent dans une position '...' invoque un
comportement indéfini. Un compilateur non-excentrique pourrait
reformater le disque dur.



Justement, je ne passe pas le non POD dans l'ellipse (cf "paramètre
formel" et "avant l'ellipse" dans mon message original). Je le passe
avant. Comme dans:
void f(std::string s, ...) { // volontairement par copie: j'avais
vu le
va_list ap; // problème avec un autre petit type
pris par copie
va_start(ap, s); // <- le compilateur n'est pas content ici
...
f("toto %i titi %s tutu %pn", 42, "foobar", (void*)ptr);



Ceci dit, l'idiome C++ utilisant un operateur autre que la virgule
pour passer un nombre variable de "paramètre" est avantageux par
rapport à va_list:

// pseudo-code:
( format("toto %i titi %s tutu %pn") % 42 % "foobar" % (void*)ptr ).print;

Voir par exemple boost::format.


--
__Pascal Bourguignon__
Avatar
Luc Hermitte
On 29 mai, 17:38, (Pascal J. Bourguignon)
wrote:
Ceci dit, l'idiome C++ utilisant un operateur autre que la virgule
pour passer un nombre variable de "paramètre" est avantageux par
rapport à va_list:

// pseudo-code:
 ( format("toto %i titi %s tutu %pn") % 42 % "foobar" % (void*)ptr ).p rint;

Voir par exemple boost::format.



Voire même format("toto %1% titi %2% tutu %3%n") .
Vi, vi, je connais :)

Mais passer le résultat ainsi construit (i.e. la liste des paramètres)
à une fonction variadique (venant de l'API de JNI) n'est pas une chose
que je trouve particulièrement aisée. Ou alors il y a une macro qui
m'a échappé.

--
Luc Hermitte
Avatar
James Kanze
On May 29, 4:18 pm, Luc Hermitte wrote:

Je savais que l'on ne pouvait pas passer d'objets non-POD dans
l'ellipse, mais là je viens de découvrir un autre truc qui m'a
quelque peu surpris. Si le dernier paramètre formel avant
l'ellipse est un non-POD, alors le compilateur se plaint que
l'on a passé un non-POD à va_start.



C'est un comportement normal, ou bien il s'agit d'une
excentricité d'un compilateur incapable de calculer le début
de la pile bien qu'il dispose de tout le nécessaire pour
connaitre le type du paramètre à l'intérieur du code appelé ?



J'avoue que je n'y avais jamais pensé, et que sans avoir régardé
la norme, j'aurais supposé que les restrictions de C valait (pas
de type sujet à la promotion). Mais selon la norme : « If the
parameter parmN is declared with a function, array, or reference
type, or with a type that is not compatible with the type that
results when passing an argument for which there is no
parameter, the behavior is undefined. » Alors, ce n'est pas
vraiment clair, mais je crois que « not compatible with the type
that results when passing an argument for which there is no
parameter » couvre l'affaire : si passer le paramètre où il n'y
a pas de paramètre formel a un comportement indéfini, c'est
difficile de dire que le type est compatible avec le type qui en
résulte quand on le fait.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
James Kanze
On May 29, 5:38 pm, (Pascal J.
Bourguignon) wrote:
Luc Hermitte writes:
> On 29 mai, 16:29, Gabriel Dos Reis wrote:
>> Luc Hermitte writes:



[...]
Ceci dit, l'idiome C++ utilisant un operateur autre que la
virgule pour passer un nombre variable de "paramètre" est
avantageux par rapport à va_list:



// pseudo-code:
( format("toto %i titi %s tutu %pn") % 42 % "foobar" % (void*)ptr ).pri nt;



Voir par exemple boost::format.



Il y a plusieurs idiomes en fait dont on peut se servir.
Malheureusement, tu en as choici un un peu mal pensé : le choix
de '%' et pour le caractère de formattage et pour l'opérateur
rend le code complètement illisible. Avec un opérateur un peu
plus voyant (<<, par exemple), en revanche, ça peut aller, ou
sinon, carrément une fonction :

Format( "toto %i titi %s tutu %pn" )
.with( 42 )
.with( "foobar" )
.with( (void*)ptr ) //...

(Évidemment, pour les entrées/sorties formattées, c'est un
grand pas en arrière. Mais il y a d'autres situations où il se
justifie.)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Sylvain SF
Luc Hermitte a écrit :

Mais passer le résultat ainsi construit (i.e. la liste des paramètres)
à une fonction variadique (venant de l'API de JNI) n'est pas une chose
que je trouve particulièrement aisée. Ou alors il y a une macro qui
m'a échappé.



chaque fonction variadique de jni.h est surchargée en triplet:
1- définition avec ...
2- définition avec paramètre formel va_list
3- définition avec paramètre formel const jvalue*
(où jvalue est une union de tous les types JNI).

il est aisé d'utiliser un jvalue[] (alloué localement) pour utiliser
la 3ième surcharge.

à noter également qu'une std::string a peu (aucun) point commun
avec le type jstring utilisé par JNI, l'exemple fourni avec une
std::string risque de ne pas donner le résultat souhaité même
s'il passait le "..."

si "env" est le JNIEnv* reçu par JNI, on créera une jstring par
jstring aStr = env->NewString(unicode, size)
ou env->NewStringUTF(utf).

Sylvain.
Avatar
Gabriel Dos Reis
Luc Hermitte writes:

| On 29 mai, 16:29, Gabriel Dos Reis wrote:
Luc Hermitte writes:
| Je savais que l'on ne pouvait pas passer d'objets non-POD dans
| l'ellipse, mais là je viens de découvrir un autre truc qui m 'a quelque
| peu surpris.
| Si le dernier *paramètre formel* *avant* l'ellipse est un non-POD , alors
| le compilateur se plaint que l'on a passé un non-POD à va_st art.
|
| C'est un comportement normal, ou bien il s'agit d'une excentricité
| d'un compilateur incapable de calculer le début de la pile bien q u'il
| dispose de tout le nécessaire pour connaitre le type du paramà ¨tre à
| l'intérieur du code appelé ?

Excentricité du programmeur.

Passer un non-POD comment argumnent dans une position '...' invoque un
comportement indéfini. Un compilateur non-excentrique pourrait
reformater le disque dur.





| Justement, je ne passe pas le non POD dans l'ellipse (cf "paramètre

Mais en pratique, si. Voir plus bas.

| formel" et "avant l'ellipse" dans mon message original). Je le passe
| avant. Comme dans:
| void f(std::string s, ...) { // volontairement par copie: j'avais

oui mais non. Ici s désigne le début de l'ellipse, donc les mà ªme
restrictions s'appliquent. Voir plus bas.

| vu le
| va_list ap; // problème avec un autre petit ty pe
| pris par copie
| va_start(ap, s); // <- le compilateur n'est pas content ici

Le compilateur a raison. 18.7/3:

The restrictions that ISO C places on the second parameter to the
va_start() macro in header <stdarg.h> are different in this
International Standard. The parameter parmN is the identifier of the
rightmost parameter in the variable parameter list of the function
definition (the one just before the ...). If the parameter parmN is
declared with a function, array, or reference type, or with a type
that is not compatible with the type that results when passing an
argument for which there is no parameter, the behavior is
undefined.

-- Gaby
Avatar
Luc Hermitte
On 30 mai, 01:40, Sylvain SF wrote:
Luc Hermitte a écrit :
> Mais passer le résultat ainsi construit (i.e. la liste des paramètr es)
> à une fonction variadique (venant de l'API de JNI) n'est pas une chos e
> que je trouve particulièrement aisée. Ou alors il y a une macro qui
> m'a échappé.

chaque fonction variadique de jni.h est surchargée en triplet:
1- définition avec ...
2- définition avec paramètre formel va_list
3- définition avec paramètre formel const jvalue*
    (où jvalue est une union de tous les types JNI).



Je l'avais zappée cette troisième surcharge. Je ne sais pas si j'aurai
pu en tirer quelque chose se "simple" à utiliser.

il est aisé d'utiliser un jvalue[] (alloué localement) pour utiliser
la 3ième surcharge.

à noter également qu'une std::string a peu (aucun) point commun
avec le type jstring utilisé par JNI, l'exemple fourni avec une
std::string risque de ne pas donner le résultat souhaité même
s'il passait le "..."



Oui, oui, je sais. ^^
Mes problèmes véritables étaient tout autres.
Je m'étais défini des ScopedLocalRef<> et ScopedGlobalRef<> (*) que je
voulais pouvoir passer directement à mon encapsulation de Methode JNI
sans avoir à me compliquer plus la vie que nécessaire déjà que ... Mes
encapsulations RAII n'étant évidemment pas compatibles avec l'ellipse,
j'avais du renoncer à la passer directement soit. Cet aspect là, je le
connaissais.
int r=theMethod.intCall(instance, param1 /*ScopedLocalRef<Date>*/,
42);

L'autre problème, connexe, venait que je voulais définir mes
encapsulations de méthode ainsi:
int Method::intCall(JObject instance, ...); // <=> instance.{/
method/}(va_args)
Le type (classe) JObject servant à convertir implicitement les
Scoped*Ref<> dans ce seul contexte.

Et c'est là que je fus surpris. Je me serai attendu à ce que le
compilateur C++ soit capable de retrouver ces petits vu que le type du
paramètre formel précédant l'ellipse est bien connu (contrairement à
l'ellipse)


(*) Oui, oui, j'avais bien un intérêt pour de telles classes vu que la
JVM était lancée depuis le C++, et non le contraire : la chose qui
revient à bien moins de 1% quand on cherche JNI sur le web.
--
Luc Hermitte
Avatar
Luc Hermitte
On 10 juin, 12:38, Gabriel Dos Reis wrote:
Le compilateur a raison.  18.7/3:

   The restrictions that ISO C places on the second parameter to the
   va_start() macro in header  <stdarg.h> are different in this
   International Standard. The parameter parmN is the identifier of t he
   rightmost parameter in the variable parameter list of the function
   definition (the one just before the ...).  If the parameter parm N is
   declared with a function, array, or reference type, or with a type
   that is not compatible with the type that results when passing an
   argument for which there is no parameter, the behavior is
   undefined.



D'accord. Merci pour l'info.

--
Luc Hermitte