OVH Cloud OVH Cloud

Comment rendre une classe "iterable"?

36 réponses
Avatar
Michael
Bonsoir à tous,

j'ai les deux classes suivantes:

class VideoDevice
{
private:
std::string name;
public:
std::string & GetName() const { return name; }
};

class VideoDeviceEnumerator
{
public:
void FillList(TBox * box);
void getVideoDevice(const std::string & fooName, VideoDevice * vd);
};

Les deux fonctions de VideoDeviceEnumerator énumèrent en interne tous les
périphériques de capture vidéo disponibles sur le système... Et la fonction
FillList est tout ce qu'il y a de restrictif puisqu'elle ne fonctionne
qu'avec les objets de la VCL de Borland.

Je voudrais donc rendre cette classe identique à un itérateur, pour que
l'utilisateur puisse faire comme avec les conteneurs de la STL...

Un truc du genre:

VideoDeviceEnumerator vdEnum;

for (VideoDeviceEnumerator::constIterator ite = vdEnum.begin(); ite !=
vdEnum.end(); ++ite)
box->AddItem(ite->GetName());

Et pour obtenir un VideoDevice:

VideoDeviceEnumerator vdEnum;

VideoDeviceEnumerator::constIterator ite = std::find_if(vdEnum.begin
(),vdEnum.end(),"nom du périphérique");

Enfin, vous voyez de quoi je parle ;)

Comment je peux faire ça?

J'ai entendu dire que c'était pas conseillé de dériver des classes de la
STL, donc j'ai bien peur de devoir réimplémenter tout ça :(

Merci d'avance...

Mike

10 réponses

1 2 3 4
Avatar
Sylvain
Loïc Joly wrote on 23/07/2006 05:00:

cela peut être faux au runtime, mais ce n'est pas le
point et il faut faire avec l'existant);


Pour moi, c'est justement là tout le point.


pourquoi ? et qu'a-t-il d'original qui ne soit pas déjà traité depuis
que les exceptions existent ?

A quoi ça servirait ? Tu aurais une fonction :

int f() throw (A, B);

Et l'utilisateur saurait en lisant ce prototype que la fonction peut
lancer soit A, soit B, soit n'importe quel autre type d'exception, mais
indirectement. Pas très utile comme spécification.


"demerde toi sans aucune info" n'est pas plus "utile comme spécification"

ici il pourra au moins faire:
try {
f();
} catch (A a){
// traiter la raison de 'a'
} catch (B b){
// traiter la raison de 'b'
} catch (...){
// prevenir l'utilisateur qu'il est planté et que l'auteur du code ne
sait pas pourquoi
}

ne pas avoir la spécification des exceptions nous ramène sur le dernier
cas où on ne sait jamais qui a planté ni pourquoi; tellement utile que
l'on ne voudrait rien à changer ??

Proposes-tu que les fonctions avec une spécification d'exception n'aient
pas le droit d'appeler de fonction n'en ayant pas ?


j'ai indiqué le contraire (et tu as commenté la violation au runtime).

Ca pourraît être une proposition acceptable, surtout utile AMA pour les
spécifications nothrow. Il faudrait pour ça patcher la bibliothèque
standard à 2/3 endroits (je pense par exemple à std::swap). Et dans ce
cas, il faudrait surtout que rien n'empêche une fonction sans
spécification d'appeler une fonction avec spécification. Un peu comme
const, en fait.


c'est plutôt en effet ce cas qui pose un problème de pertinence /
consistence.

toutefois la syntaxe actuelle gère très bien le cas:

ayant void foo() throw (A,B);

void g() throw (){ foo(); } est invalide

void h(){ foo(); } est valide

void j() throw (A,B){ foo(); } est valide

void k() throw (){
try {
foo();
} catch (A){
} catch (B){
}
}
est valide

hormi la STL (qui n'était pas spécifiquement l'objet de ma question), il
doit bien y avoir environ 0% des codes qui utilisent aujourd'hui
_THROW0, cela n'impacterait donc pas trop de monde.

les nouvelles méthodes qui /choississent/ de déclarer leurs exceptions
imposerait en effet à leur utilisateur et supplanteur la même
'corvée', mais est-ce bien grave ?


Il n'a jamais marché. Et dans le code que je fais, où souvent les
exceptions ne sont gérées qu'au niveau le plus haut pour arrêter le
programme avec un code d'erreur, j'aurais aisément 3 à 4 fois plus de
lignes pour spécifier mes exceptions que pour le corps de mes fonctions.


ce code /que tu fais/ fait règle ? pour ma part que préfère le
traitement local (au niveau le plus près).

je rappelle que la syntaxe actuelle sans aucune spécification permet de
transmettre toute exception - tu opposes donc un argument de contrainte
inacceptable là où justement tu n'as et continuera à n'avoir aucune
contrainte.

L'intérêt des exceptions est de séparer traitement de l'erreur de sa
localisation. Si on doit écrire des choses pour chaque fonction, je ne
vois plus l'intérêt.


si tes fonctions sont manichéennes (process normal ou core dump) soit;
là encore je pense que la vraie vie est un peu plus subtile.

Mes deux propositions étaient exclusives.


pas vraiment! s'interdire une 'compilation séparée' (au profit d'une
compilation monolithique) reviendrait à gérer du code source existant.

pour les 2 tu opposes un argument que je n'ai pas indiqué et qui n'est
pas imposé par une déclaration d'exception.

Si tu pense que la première est acceptable ...


je ne l'accepte pas car elle est inutile (re-re-bis le code existant
fonctionne comme il est).

Sylvain.


Avatar
Loïc Joly
Loïc Joly wrote on 23/07/2006 05:00:


cela peut être faux au runtime, mais ce n'est pas le point et il faut
faire avec l'existant);



Pour moi, c'est justement là tout le point.



pourquoi ? et qu'a-t-il d'original qui ne soit pas déjà traité depuis
que les exceptions existent ?


Il n'a rien d'original. Il n'a juste à mon sens jamais trouvé de bonne
réponse.


A quoi ça servirait ? Tu aurais une fonction :

int f() throw (A, B);

Et l'utilisateur saurait en lisant ce prototype que la fonction peut
lancer soit A, soit B, soit n'importe quel autre type d'exception,
mais indirectement. Pas très utile comme spécification.



"demerde toi sans aucune info" n'est pas plus "utile comme spécification"

ici il pourra au moins faire:
try {
f();
} catch (A a){
// traiter la raison de 'a'
} catch (B b){
// traiter la raison de 'b'
} catch (...){
// prevenir l'utilisateur qu'il est planté et que l'auteur du code ne
sait pas pourquoi
}

ne pas avoir la spécification des exceptions nous ramène sur le dernier
cas où on ne sait jamais qui a planté ni pourquoi; tellement utile que
l'on ne voudrait rien à changer ??


Quand une indication n'apporte aucune sûreté supplémentaire que ce que
des commentaires apporteraient, je ne vois pas pourquoi la faire entrer
dans le langage.

L'intérêt de passer du commentaire dans du langage (comme const, par
exemple) existe uniquement si le langage peut s'assurer que cet
ex-commentaire est vrai. Avec ta proposition, je ne vois vraiment pas en
quoi le langage pourrait vérifier quoi que ce soit. En fait, le
comportement actuel de Visual C++ qui ignore ces spécifications fait
exactement ce que tu souhaites...

Proposes-tu que les fonctions avec une spécification d'exception
n'aient pas le droit d'appeler de fonction n'en ayant pas ?



j'ai indiqué le contraire (et tu as commenté la violation au runtime).


Ma question était plus réthorique pour orienter sur une autre possibilité.

toutefois la syntaxe actuelle gère très bien le cas:

ayant void foo() throw (A,B);

void g() throw (){ foo(); } est invalide

void h(){ foo(); } est valide

void j() throw (A,B){ foo(); } est valide

void k() throw (){
try {
foo();
} catch (A){
} catch (B){
}
}
est valide


Je ne sais pas ce que tu entends par valide, mais tous ces bouts de code
sont légaux en C++ actuellement.

Si j'ai bien compris ce que tu souhaites, écrire :
void f() throw ()
{
throw 1;
}

Serait invalide et détecté à la compilation, mais écrire
void f(à throw ()
{
g();
}
void g()
{
throw 1;
}

Serait valide, à la compilation comme au run-time ?

Je trouve ça assez moche.



Il n'a jamais marché. Et dans le code que je fais, où souvent les
exceptions ne sont gérées qu'au niveau le plus haut pour arrêter le
programme avec un code d'erreur, j'aurais aisément 3 à 4 fois plus de
lignes pour spécifier mes exceptions que pour le corps de mes fonctions.



ce code /que tu fais/ fait règle ? pour ma part que préfère le
traitement local (au niveau le plus près).


J'aime bien aussi le traitement local, dans la plupart des cas. Et pour
le traitement local, je n'ai jamais ressenti le besoin des exceptions.
Le but des exceptions est quand le traitement est non local. C'est pour
ça qu'elles ont été introduites dans le langage.


je rappelle que la syntaxe actuelle sans aucune spécification permet de
transmettre toute exception - tu opposes donc un argument de contrainte
inacceptable là où justement tu n'as et continuera à n'avoir aucune
contrainte.


Il est des cas où pour la validité du code, savoir qu'une fonction est
nothrow est utile. Je propose que dans ces cas, on puisse l'indiquer et
que sand ce cas toujours soit obligé de fournir au compilateur les
moyens de le vérifier (mais voir le post de James sur les autres
problèmes causés). Et je propose de ne rien spécifier pour le cas général.


L'intérêt des exceptions est de séparer traitement de l'erreur de sa
localisation. Si on doit écrire des choses pour chaque fonction, je ne
vois plus l'intérêt.



si tes fonctions sont manichéennes (process normal ou core dump) soit;
là encore je pense que la vraie vie est un peu plus subtile.


Je ne comprends pas trop ce que tu dis.


--
Loïc



Avatar
James Kanze
Sylvain wrote:
James Kanze wrote on 23/07/2006 12:57:


Il oblige en fait à catcher ou à lister toutes les exceptions
qui aurait dû être des codes de retour:-). Et qui le sont en
C++, en général.



tu aurais pu indiquer: les erreurs que /tu/ prefères retourner
via un code de retour.


Disons, les erreurs qu'on doit typiquement traitées assez proche
à où on les detecte. Et parmi les experts, il y a plus ou moins
un concensus que quand l'erreur doit être traitée tout près, un
code de rétour convient plus qu'une exception. (On peut ne pas
être d'accord sur une exception donnée, mais dans l'ensemble, il
faut dire que Java abuse des exceptions.)

je te rapelles qu'une méthode retournant une instance
nouvellement créé ne peux le faire via un param variant (ptr&)
et donc n'a d'autre choix que de lever un exception si
l'opération ne peut être réalisée.


Je sais que parfois, on s'en sert pour contourner d'autres
défauts du langage. Mais même. Fallible n'est pas vraiment une
nouveauté. (Mais c'est vrai que pendant longtemps, Java n'avait
pas de templates. Ce qui veut dire qu'il aurait fallu écrire
Fallible de nouveau chaque fois qu'on avait un nouveau type de
rétour.)

au delà des conséquences de cette absence de pointeurs, il
reste (heureusement) les choix personnels, prétendre ce qui
/devrait être/ présuppose que tout n'a qu'une seule écriture
possible, je ne crois pas cela.


Chaque cas, pris isolement, relève du choix personnel. Mais il y
a bien des principes de base qui relève de la génie logicielle.

on peux noter également qu'en C++ ces codes retours ne règlent
pas tout loin s'en faut, si j'écris par exemple un streamer
d'extraction, je peux vouloir coder:


Bytes& operator >> (Bytes&, bool&)
throw (ArrayIndexOutOfBoundsException);
Bytes& operator >> (Bytes&, byte&)
throw (ArrayIndexOutOfBoundsException);
etc ...


il est évident que de tels cas (les exemples ne manquerait
pas) ne s'accorde pas avec la stratégie du code de retour.


Les flux sont un cas particulier. Où en C++ le choix a été
laissé à l'utilisateur. N'empêche que dans la plupart des cas,
c'est un des rares cas où un état dans l'objet est la politique
qui convient le mieux.

le choix de la STL et de _CATCH_IO_END qui positionne un
indicateur interne pour continuer son process est une
possibilité (à mi chemin) que je n'affectionne pas vraiment
(reste aussi que ma question n'était pas liée à la STL).


Là, tu me perds complétement. D'abord, qu'est-ce que la STL a à
voir ici ? Et c'est quoi, _CATCH_IO_END ?

returnType methodeName {
try {
// whatever
}
catch (Throwable err){
}
}




Et entre la fin du bloc de catch et le retour, il y a toujours
une possibilité d'un « VirtualMachineError ». (Un bon exemple
d'une chose qui ne doit jamais être une exception.)



si tu écris ton byte-code à la main peut être, sinon ?...


Si tu crois à ce qu'écrit Sun, tu veux dire.

(par contre la machine peut être éteinte au milieu de la
méthode, soit)


Je suis d'accord qu'il ne doit pas y avoir une
« VirtualMachineError ». Mais ce n'est pas moi qui l'a
inventé ; c'est Sun.

Mais ce n'était pas mon propo. Je disais que tu ne pouvais
jamais écrire cette garantie. La déclaration de ta fonction
methodeName, ci-dessus, ne comporte aucune garantie en ce qui
concerne ni les Error ni les RuntimeException.



Error et RuntimeException (et toutes leurs classes filles)
étendent Throwable et seront catchées ici (mais ce n'est pas
le propos).


Où est-ce que ta déclaration dit ça ?

On est arrivé à un point en C++ où on peut ajouter de
nouveaux features, mais on ne peut pas toucher à l'existant.



soit, pourquoi dans ce cas avoir défini 'throw(...)' dans la
déclaration d'une fonction/méthode ?


Sans doute pour qu'on puisse déclarer explicitement, pour que le
lecteur sache qu'on y a pensé, et pas simplement laissé le
défaut par mégard.

Dans la pratique, évidemment, avec l'experience, on a appris que
la seule distinction utile, c'était « throw() », et qu'on
pourrait bien laisser les autres de côté.

--
James Kanze
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
Loïc Joly wrote on 23/07/2006 21:43:

Quand une indication n'apporte aucune sûreté supplémentaire que ce que
des commentaires apporteraient, je ne vois pas pourquoi la faire entrer
dans le langage.


dans mon boulot on associe volontiers commodité et sureté (sécurité en
fait), une évolution du langage ne pourrait qu'apporter de la sureté ?

L'intérêt de passer du commentaire dans du langage (comme const, par
exemple) existe uniquement si le langage peut s'assurer que cet
ex-commentaire est vrai.


si le principe était si rigoureux on ne pourrait pas déconster, non?

Avec ta proposition, je ne vois vraiment pas en
quoi le langage pourrait vérifier quoi que ce soit.


alors oublie, re-répéter ma description me parait utile si c'est un pb
d'expression / compréhension.

comportement actuel de Visual C++ qui ignore ces spécifications
fait exactement ce que tu souhaites...


exactement /le contraire de/ ce que je souhaites.
(y'a pb d'expression/compréhension).

toutefois la syntaxe actuelle gère très bien le cas:

ayant void foo() throw (A,B);

void g() throw (){ foo(); } est invalide

void h(){ foo(); } est valide

void j() throw (A,B){ foo(); } est valide

void k() throw (){
try {
foo();
} catch (A){
} catch (B){
}
}
est valide


Je ne sais pas ce que tu entends par valide, mais tous ces bouts de code
sont légaux en C++ actuellement.


j'entendais générerais un warning à la compilation (pour 'invalide').

Si j'ai bien compris ce que tu souhaites, écrire :
void f() throw ()
{
throw 1;
}

Serait invalide et détecté à la compilation


oui - générerais un warning (ou erreur mais ça ne me parait pas
indispensable ou même présomptueux des effets de bord).

mais écrire
void f(à throw ()
{
g();
}
void g()
{
throw 1;
}

Serait valide, à la compilation comme au run-time ?


dans le cas général que je commentais, le compilo ne connait pas
l'implémentation de g() et je n'insinue pas qu'il devrait parser les
implémentations à sa dispo, il se contenterait des prototypes déclarés.

donc oui, la compil. serait ici valide (sans warning), ce qu'il
adviendra au runtime n'est nullement influencé par la présence ou
l'absence du "throw()" sur la déf. de f() et est donc hors de mon propos.

Je trouve ça assez moche.


quand on se place du coté moche, c'est moche, on est d'accord.
c'est un choix que de voir seulement le coté négatif.

J'aime bien aussi le traitement local, dans la plupart des cas. Et pour
le traitement local, je n'ai jamais ressenti le besoin des exceptions.
Le but des exceptions est quand le traitement est non local. C'est pour
ça qu'elles ont été introduites dans le langage.


de l'époque où les exceptions étaient inconnues de CFront et où on les
codait par macro (setjmp / longjmp) je me rappelle également des
discussions sur les erreurs dans les working threads (qui peuvent n'être
référencé par personne à qui envoyer un statut).

au delà, avoir ressenti ou une expérience individuelle ne me parait pas
pertinent.

L'intérêt des exceptions est de séparer traitement de l'erreur de sa
localisation. Si on doit écrire des choses pour chaque fonction, je
ne vois plus l'intérêt.


si tes fonctions sont manichéennes (process normal ou core dump) soit;
là encore je pense que la vraie vie est un peu plus subtile.


Je ne comprends pas trop ce que tu dis.


je me place seulement dans le cadre induit par ta remarque!

est-ce que le fait de lister les exceptions retournées diminue ou
interfère "l'intérêt des exceptions", je pense que non.

quand a-t-on une "séparation" acceptable entre l'erreur et son
traitement ? faut-il ne catcher les erreurs dans le main pour sortir un
core dump à la moindre alerte ou acceptes-tu l'idée d'un traitement plus
local de l'erreur ("local" pouvant signifier ici par l'appelant, par la
méthode responsable du processus complet initié par l'utilisateur ou un
event quelconque).

Sylvain.



Avatar
Sylvain
James Kanze wrote on 23/07/2006 23:30:

Je sais que parfois, on s'en sert pour contourner d'autres
défauts du langage.


ce n'est pas un défaut mais un choix - tes arguments ne gagnent rien à
s'accompagner de dénigrement.

Mais même. Fallible n'est pas vraiment une nouveauté.


ce package de P2P me parait un peu anecdotique non ?

ce principe me parait lourd et est peu satisfaisant (pour le contexte
décrit; il est peut être pertinent dans leur contexte où des instances
modifiables peuvent être modifiées par différent intervenant (thread
client, scheduler, manager global, ?...); dans une telle distribution
positionner un état interne est peut être plus simple que de lever une
exception ne sachant pas si l'appelant est exception savvy).

Les flux sont un cas particulier. Où en C++ le choix a été
laissé à l'utilisateur. N'empêche que dans la plupart des cas,
c'est un des rares cas où un état dans l'objet est la politique
qui convient le mieux.


je ne partage pas cet avis en tant que règle universelle ("la plupart
des cas"); j'ai des cas ou c'est (un état) très satisfaisant et d'autres
ou l'objet est suffisamment fugace et opaque (de type inconnu) pour
préférer sortir en exception.

le choix de la STL et de _CATCH_IO_END qui positionne un
indicateur interne pour continuer son process est une
possibilité (à mi chemin) que je n'affectionne pas vraiment
(reste aussi que ma question n'était pas liée à la STL).


Là, tu me perds complétement. D'abord, qu'est-ce que la STL a à
voir ici ? Et c'est quoi, _CATCH_IO_END ?


la STL a à voir qu'elle utilise justement "un état dans l'objet" pour
enregistrer les erreurs survenant durant une lecture depuis un stream.
_CATCH_IO_END est le nom de la macro positionnant le flag d'erreur dans
l'implémentation de P.J. Plauger.



returnType methodeName {
try {
// whatever
}
catch (Throwable err){
}
}



Error et RuntimeException (et toutes leurs classes filles)
étendent Throwable et seront catchées ici (mais ce n'est pas
le propos).


Où est-ce que ta déclaration dit ça ?


un handler d'exception Java catche les erreurs de son type argument et
toutes les classes filles de ce type. le comportement de java est ici le
même que celui de C++ (enfin là où applicable évidemment, dans la
pratique un catch pouvant recevoir des types primitifs, des références
clonées, des pointeurs, ..., le seul type générique que je connaisse est
en fait '...').

Sylvain.




Avatar
Sylvain
Loïc Joly wrote on 23/07/2006 21:43:

void f(à throw ()
{
g();
}
void g()
{
throw 1;
}

Serait valide, à la compilation comme au run-time ?
Je trouve ça assez moche.


btw, j'espère que l'on est au moins d'accord pour trouver cet exemple
complètement idiot ?!

tu rappelles à raison que le cas nothrow ("throw()") est le seul assez
généralement utilisé (notamment par la STL) pour déclarer des fonctions
réellement nothrow, ceci doit impliquer un 'contrat'.

le zouave qui coderait ton exemple avec g() sous les yeux ou en ne
disposant que du prototype "void g();" violerait délibérément le contrat
en déclarant avec la garantie du contraire ou sans aucune garantie que
f() sera vraiment nothrow.

trouver moche un exemple clairement erroné est juste normal.
mais ça n'argumente pas les cas où l'on ne code pas exprès des erreurs.

Sylvain.

Avatar
kanze
Sylvain wrote:
James Kanze wrote on 23/07/2006 23:30:

Je sais que parfois, on s'en sert pour contourner d'autres
défauts du langage.


ce n'est pas un défaut mais un choix - tes arguments ne
gagnent rien à s'accompagner de dénigrement.


Un défaut peu bien être le résultat d'un choix. Le fait reste
que le Java n'a pas de paramètres en sortie, aller savoir
pourquoi, et que des paramètres en sortie sont bien utile
parfois.

Mais même. Fallible n'est pas vraiment une nouveauté.


ce package de P2P me parait un peu anecdotique non ?


Quel package de P2P ? Je parlais de l'idiome de Fallible, très
répandu en C++. Ma propre implémentation, assez étendue par
rapport à la version de base, est disponible sur la toile :
http://kanze.james.neuf.fr/code/Util/Basic/Fallible/index.html.
Mais l'idiome est tellement simple et tellement répandu que
j'imagine que la plupart des applications en réécrivent le
template elle-même ; c'est souvent plus facile d'écrire 10
lignes de code (et sans mes extensions, il n'en faut pas plus)
que d'essayer d'integrer quelque chose d'étranger dans un
projet.

ce principe me parait lourd et est peu satisfaisant (pour le
contexte décrit; il est peut être pertinent dans leur contexte
où des instances modifiables peuvent être modifiées par
différent intervenant (thread client, scheduler, manager
global, ?...); dans une telle distribution positionner un état
interne est peut être plus simple que de lever une exception
ne sachant pas si l'appelant est exception savvy).


Je ne sais pas trop de quoi tu parles ici, mais ce n'est pas
l'idiome de Fallible connu des programmeurs C++.

Les flux sont un cas particulier. Où en C++ le choix a été
laissé à l'utilisateur. N'empêche que dans la plupart des
cas, c'est un des rares cas où un état dans l'objet est la
politique qui convient le mieux.


je ne partage pas cet avis en tant que règle universelle ("la
plupart des cas"); j'ai des cas ou c'est (un état) très
satisfaisant et d'autres ou l'objet est suffisamment fugace et
opaque (de type inconnu) pour préférer sortir en exception.


Tu as des cas où un flux est un objet fugace et opaque ?

Quand je dis « la plupart des cas », j'admets explicitement
des exceptions. Si je gardes un fichier ouvert très longtemps,
avec des écritures que de temps en temps, lever une exception
sur une erreur d'écriture est probablement la solution qui
convient le plus. Au moins que l'écriture est une fonctionnalité
sécondaire, et que son échec ne doit pas interrompre le
traitement général (cas des fichiers de log, par exemple). Quand
je lis un fichier, je suis de toute façon obligé à tester
l'erreur plus ou moins après chaque lecture, et d'y réagir tout
de suite. Et un if, c'est bien moins lourd et plus lisible qu'un
try/catch dans ce cas-là. Et quand j'écris un fichier tout d'une
volée, c'est beaucoup plus simple d'écrire jusqu'à la fin, quoi
qui arrive, et ne tester l'erreur qu'après la fermature finale.
(En se rappelant que l'erreur ne se présentera pas forcément
avant non plus.)

le choix de la STL et de _CATCH_IO_END qui positionne un
indicateur interne pour continuer son process est une
possibilité (à mi chemin) que je n'affectionne pas
vraiment (reste aussi que ma question n'était pas liée à
la STL).


Là, tu me perds complétement. D'abord, qu'est-ce que la STL
a à voir ici ? Et c'est quoi, _CATCH_IO_END ?


la STL a à voir qu'elle utilise justement "un état dans
l'objet" pour enregistrer les erreurs survenant durant une
lecture depuis un stream.


J'ai l'impression que tu confonds toujours. La STL n'a rien à
voir avec les flux, sauf peut-être que certains flux peuvent
utiliser des classes de la STL internalement.

_CATCH_IO_END est le nom de la macro positionnant le flag
d'erreur dans l'implémentation de P.J. Plauger.


Et alors ? Quel rapport avec le C++, si le symbole ne fait pas
partie de l'interface standard ? Comment veux-tu que quelqu'un
le connaît ; je ne vais pas étudier les détails de
l'implémentation, même si je l'avais sur mon système. Et ce
n'est même pas une extension documentée de l'implémentation de
Plauger ; c'est carrément quelque chose propre à
l'implémentation.

returnType methodeName {
try {
// whatever
}
catch (Throwable err){
}
}



Error et RuntimeException (et toutes leurs classes
filles) étendent Throwable et seront catchées ici (mais
ce n'est pas le propos).


Où est-ce que ta déclaration dit ça ?


un handler d'exception Java catche les erreurs de son type
argument et toutes les classes filles de ce type. le
comportement de java est ici le même que celui de C++ (enfin
là où applicable évidemment, dans la pratique un catch pouvant
recevoir des types primitifs, des références clonées, des
pointeurs, ..., le seul type générique que je connaisse est en
fait '...').


Je ne crois pas que tu aies compris la distinction entre la
déclaration (le contrat) et l'implémentation. Je ne vais pas
lire la source d'une fonction pour savoir ce qu'elle fait. Je
lis sa declaration, c-à-d son contrat.

Maintenant, il est clair qu'on ne peut pas mettre tout dans une
declaration ; il faut de la documentation aussi. Et je n'ai
rien contre ceux en C++ qui dit que les spécifications des
exceptions ont une valeur limitée, et qu'à part throw(), on ne
s'en sert pas. Mais dire que le Java fait mieux, c'est faux ;
on ne peut même pas declarer au niveau du langage qu'une
fonction ne sort jamais par une exception. Pour que la
vérification statique des spécifications d'exception ait une
utilité, il faut qu'elle soit complète, qu'il n'y a pas
d'exception.

Les spécifications d'exception font partie du contrat de la
fonction. La plus intéressante, c'est de pouvoir dire que la
fonction s'engage à ne pas sortir par une exception. Comme pour
tout, une erreur lors de la compilation serait préférable à une
erreur lors de l'exécution. D'office, au niveau du langage, en
revanche, le C++ n'offre qu'une erreur à l'exécution, et le Java
n'offre rien. C'est dommange, mais c'est comme ça.

--
James Kanze GABI Software
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
kanze wrote on 24/07/2006 09:35:

Un défaut peu bien être le résultat d'un choix.


Kongzi ?

Le fait reste que le Java n'a pas de paramètres en sortie


?! c'est quoi un "paramètre en sortie" ??

si tu parles de sortie d'une méthode au sens propre, si, elle peut
retourner un résultat non void.

si tu parles de paramètres par référence, toute instance est passé par
référence et peut donc être modifiée.

si tu voulais dire paramètre de type primitif variant, en effet cela
n'existe pas ...

aller savoir pourquoi,


... parce qu'il semblerait que les pointeurs n'existent pas ...

et que des paramètres en sortie sont bien utile parfois.


... mais rien n'empèche, par exemple, de transmettre une instance de
Integer pour assumer le role d'un int& du C++.

Je ne sais pas trop de quoi tu parles ici, mais ce n'est pas
l'idiome de Fallible connu des programmeurs C++.


hmm, hmm, je te cite:

Je sais que parfois, on s'en sert pour contourner d'autres
défauts du langage. Mais même. Fallible n'est pas vraiment
une nouveauté.



tu parlais de Java, je t'ai répondu sur Java; bravo de railler ma
réponse en changeant de sujet.

Tu as des cas où un flux est un objet fugace et opaque ?


couramment pourquoi ?

J'ai l'impression que tu confonds toujours. La STL n'a rien à
voir avec les flux, [...]


tu sais moi du moment que ça s'appelle std::machin ...

Je ne crois pas que tu aies compris la distinction entre la
déclaration (le contrat) et l'implémentation. Je ne vais pas
lire la source d'une fonction pour savoir ce qu'elle fait. Je
lis sa declaration, c-à-d son contrat.


tu te moques de moi là ou quoi ???????

la garantie est évidemment dans la déclaration (et justement plus en
Java qui force à lister ou à catcher les exceptions qu'en C++ qui se
fout bien des exceptions listées).

au cas où cela t'aura echappé, une déclaration Java (on parle de cela)
comme "returnType methodeName()" est *la* garantie qu'elle ne throw
rien; contrairement au "throw()" de C++, c'est ici l'absence
d'indication qui est la garantie.

on peut reboucler sur "y-a pas d'exception quand la machine crashe", je
te renvoie vers les specs Java pour la différence entre une erreur
applicative *qui doit être catchée ou listée* et une erreur machine / VM
/ autre qui est réputée 'unrecoverable' et qui n'est donc pas du ressort
d'une méthode applicative.

Maintenant, il est clair qu'on ne peut pas mettre tout dans une
declaration ; il faut de la documentation aussi. Et je n'ai
rien contre ceux en C++ qui dit que les spécifications des
exceptions ont une valeur limitée, et qu'à part throw(), on ne
s'en sert pas. Mais dire que le Java fait mieux, c'est faux ;
on ne peut même pas declarer au niveau du langage qu'une
fonction ne sort jamais par une exception.


faux voir ci-dessus.

on trouve parfois ce vieil adage chez les dinosaures Cistes -
développeur système s'il en est - pour penser qu'ils contrôlent tout; si
la comparaison vaut ici pour prétendre qu'il est impossible de "déclarer
qu'une fonction ne sort jamais par une exception" quand il s'agit
d'exception de l'OS, du runtime ou toute plante externe à l'appli.,
alors le point est archaïque et non pertinent.

... le C++ n'offre qu'une erreur à l'exécution,
et le Java n'offre rien.


continue le C++ ! tu as clairement manqué des chapitres sur Java.

Sylvain.


Avatar
kanze
Sylvain wrote:
kanze wrote on 24/07/2006 09:35:
Le fait reste que le Java n'a pas de paramètres en sortie


?! c'est quoi un "paramètre en sortie" ??

si tu parles de sortie d'une méthode au sens propre, si, elle
peut retourner un résultat non void.

si tu parles de paramètres par référence, toute instance est
passé par référence et peut donc être modifiée.

si tu voulais dire paramètre de type primitif variant, en
effet cela n'existe pas ...


Je parlais de ce qu'on appelle les « out parameters » en
anglais. Java ne supporte ni out, ni inout. (Le C++, en fait, ne
supporte pas réelement out non plus. Mais on arrive aux mêmes
fins avec des inout la plupart du temps.)

C'est un concepte assez connu, il me semble.

aller savoir pourquoi,


... parce qu'il semblerait que les pointeurs n'existent pas
...


Quel rapport ?

et que des paramètres en sortie sont bien utile parfois.


... mais rien n'empèche, par exemple, de transmettre une
instance de Integer pour assumer le role d'un int& du C++.


De quoi parles-tu ? En quoi est-ce que l'existance d'une classe
Integer change quoique ce soit ? (Et qui parlait d'int,
d'ailleurs ?)

À titre d'exemple, essayer d'écrire l'équivalant en Java de
std::swap.

Je ne sais pas trop de quoi tu parles ici, mais ce n'est pas
l'idiome de Fallible connu des programmeurs C++.


hmm, hmm, je te cite:

Je sais que parfois, on s'en sert pour contourner d'autres
défauts du langage. Mais même. Fallible n'est pas vraiment
une nouveauté.



tu parlais de Java, je t'ai répondu sur Java; bravo de railler
ma réponse en changeant de sujet.


C'est vrai que j'ai fait des suppositions apparamment invalid ;
je croyais que tu connaissais un peu de C++, et que tu saurais
traduire un idiome (qui ne dépend pas réelement du langage) d'un
langage à l'autre.

Tu as des cas où un flux est un objet fugace et opaque ?


couramment pourquoi ?


Parce que je n'en ai jamais vu dans la pratique.

J'ai l'impression que tu confonds toujours. La STL n'a rien
à voir avec les flux, [...]


tu sais moi du moment que ça s'appelle std::machin ...


Quel rapport entre std:: et STL ? Une partie de la STL a été
incorporée dans la bibliothèque standard, mais non toute. De
même, il y a une bonne partie de la bibliothèque standard qui
n'a rien à voir avec la STL.

Je ne crois pas que tu aies compris la distinction entre la
déclaration (le contrat) et l'implémentation. Je ne vais pas
lire la source d'une fonction pour savoir ce qu'elle fait.
Je lis sa declaration, c-à-d son contrat.


tu te moques de moi là ou quoi ???????


J'essaie à comprendre ce que tu comprends et ce que tu comprends
pas. A priori, tu as une certaine expérience, tu dois donc
comprendre quelque chose. De l'autre côté, tu sors parfois des
trucs tellement gros qu'on se pose la question.

la garantie est évidemment dans la déclaration (et justement
plus en Java qui force à lister ou à catcher les exceptions
qu'en C++ qui se fout bien des exceptions listées).


Je n'arrive pas à comprendre ce que tu ne comprends pas. Tu sais
très bien que le Java ne t'oblige à lister que certaines
exceptions, non toutes. C'est là le problème, parce que la
garantie la plus intéressante, c'est bien qu'il n'y aura pas
d'exception. Du tout. Qu'elle hérite de RuntimeException ou de
Error, ou non. C'est dans la pratique peut-être la seule
garantie un peu près utile. Idéalement, on l'aurait, et elle
serait vérifiée par le compilateur. En C++, on l'a, mais avec
vérification lors de l'exécution -- c'est loin d'être idéal,
mais c'est mieux que rien. En Java, il n'y a même aucune façon
d'exprimer cette garantie au niveau du langage.

au cas où cela t'aura echappé, une déclaration Java (on parle
de cela) comme "returnType methodeName()" est *la* garantie
qu'elle ne throw rien; contrairement au "throw()" de C++,
c'est ici l'absence d'indication qui est la garantie.


C'est à croire que tu connais ni le Java, ni le C++, parce que
tu l'a exactement à l'envers. La déclaration ci-dessus en Java
garantie seulement que la fonction ne peut pas lever une parmi
un petit sous-ensemble des exceptions. Et j'ai déjà (souvent
d'ailleurs) vu des fonctions Java avec une déclaration pareille
sortir par une exception.

Quand tu spécifies throw() sur une fonction en C++, en revanche,
tu es 100% sûr que la fonction ne sortira jamais par une
exception. C'est une partie du contrat enforcée (seulement lors
de l'exécution, c'est vrai, et c'est dommage) par le
compilateur.

on peut reboucler sur "y-a pas d'exception quand la machine
crashe",


Quand il y a une violation du contrat, à peu près la seule chose
raisonable à faire, c'est d'aborter. Si le contrat de la
fonction dit qu'elle ne sort pas par une exception, et qu'elle
essaie de le faire, il faut aborter.

je te renvoie vers les specs Java pour la différence entre une
erreur applicative *qui doit être catchée ou listée* et une
erreur machine / VM / autre qui est réputée 'unrecoverable' et
qui n'est donc pas du ressort d'une méthode applicative.


La seule différence entre les deux, c'est qu'il faut en citer
les unes dans la déclaration de la fonction, mais pas les
autres. C'est vraiement la seule différence, selon le langage.
Et si tu régardes les cas réels, dans la API de Java, tu
constateras que les seules qu'il faut lister, ce sont celles
qu'il faut typiquement traiter à un niveau tout près. C-à-d
celles où on se servira d'un code de rétour d'une fonction en
C++, parce que c'est quand même moins lourd.

L'exemple typique où une exception est la solution préférée,
c'est std::bad_alloc (en C++) ou java.lang.OutOfMemoryError (en
Java). C'est un cas type où l'erreur ne pourrait pas être
traitée localement. Il y en a beaucoup de ce genre qui dérivent
de RuntimeException ; ce sont des erreurs que, si on veut les
traiter, il faut les traiter à un niveau assez élevé,
typiquement très loin d'où il se produit. Et pour pouvoir être
sûr de pouvoir remonter la pile correctement, il faut bien qu'il
y a des functions dont on peut garantir qu'elle ne lève pas
d'exception. Du tout -- pas de std::bad_alloc, pas de
java.lang.OutOfMemoryError, rien.

Maintenant, il est clair qu'on ne peut pas mettre tout dans
une declaration ; il faut de la documentation aussi. Et je
n'ai rien contre ceux en C++ qui dit que les spécifications
des exceptions ont une valeur limitée, et qu'à part throw(),
on ne s'en sert pas. Mais dire que le Java fait mieux, c'est
faux ; on ne peut même pas declarer au niveau du langage
qu'une fonction ne sort jamais par une exception.


faux voir ci-dessus.


Il vaut peut-être mieux régarder les spécifications du langage,
non ? Pour Java (§8.4.4) :

A throws clause is used to declare any CHECKED exceptions
[...] It is permitted BUT NOT REQUIRED to mention other
(unchecked) exceptions in a throws clause.

Clairement, toutes les exceptions ne sont pas égales, et on peut
sortir d'une fonction par une exception (« unchecked »), même
s'il n'apparaît pas dans le throws clause.

En C++ (§15.4/8,9) :

Whenever an exception is thrown and the search for a handler
encounters the outermost block of a function with an
exception-specification, the function unexpected() is called
if the exception-specification does not allow the exception.
[...]

The function unexpected() may throw an exception that will
satisify the exception-specification for which it was
invoked, and in this case the search for another handler
will continue at the call of the function with this
exception-search, or it may call terminate().

Pas de distinction entre deux catégories d'exception. Et s'il y
a une exception-specification, c'est véritablement une
garantie : quoi qui arrive, on ne sort pas avec une exception
qui n'est pas citée, et si elle est vide, on ne sort pas de la
fonction avec une exception.

on trouve parfois ce vieil adage chez les dinosaures Cistes -
développeur système s'il en est - pour penser qu'ils
contrôlent tout; si la comparaison vaut ici pour prétendre
qu'il est impossible de "déclarer qu'une fonction ne sort
jamais par une exception" quand il s'agit d'exception de l'OS,
du runtime ou toute plante externe à l'appli., alors le point
est archaïque et non pertinent.


Sauf que sans avoir des fonctions garanties sans exceptions, on
ne peut pas écrire du code correcte. Que tu réfuses à régarder
les faits en face, c'est un autre problème.

... le C++ n'offre qu'une erreur à l'exécution,
et le Java n'offre rien.


continue le C++ ! tu as clairement manqué des chapitres sur
Java.


Je te conseille de lire la spécification de Java avant de
parler.

--
James Kanze GABI Software
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
kanze wrote on 25/07/2006 09:47:

Je parlais de ce qu'on appelle les « out parameters » en
anglais.


ok donc des "paramètres par référence", à moins que tu n'inclues
implicitement également le mot-clé "out" jouant sur les régles de
modifications du paramètre formel (ou pire les modifiers de .net).

Java ne supporte ni out, ni inout.


il supporte 'final' qui agit comme 'const' pour un "in parameter".

(Le C++, en fait, ne supporte pas réelement
out non plus. Mais on arrive aux mêmes
fins avec des inout la plupart du temps.)


match nul avec une référence Java - sauf pour changer la valeur d'un
pointeur (un "out type [const] * &").

C'est un concepte assez connu, il me semble.


archi connu ! mais comme je n'ai vu aucun lien avec le point discuté...

À titre d'exemple, essayer d'écrire l'équivalant en Java de
std::swap.


swaper quoi appartenant à qui ??

si c'est une classe clonable je ne vois pas le problème, si c'est des
types primitifs c'est également facile, si par contre ce sont des
pointeurs ... je l'ai déjà dit.

ceci étant, les map et autres ensembles triés existent (c'est là que le
"appartenant à qui" intervient), il sera donc toujours possible de swap
dans un contexte donné.

Je n'arrive pas à comprendre ce que tu ne comprends pas. Tu sais
très bien que le Java ne t'oblige à lister que certaines
exceptions, non toutes. C'est là le problème, parce que la
garantie la plus intéressante, c'est bien qu'il n'y aura pas
d'exception. Du tout. Qu'elle hérite de RuntimeException ou de
Error, ou non. C'est dans la pratique peut-être la seule
garantie un peu près utile.


je suis désolé mais justement la classe de l'exception à son importance
(tel que Java se pratique en tout cas). les erreurs RuntimeException et
Error sont réputés non récupérables - un peu moins pour les filles de
RuntimeException, mais la plupart résulterait d'erreur ou de manque
grossier dans le codage (aucune méthode standard ne lève de telle
exception par surprise).

Idéalement, on l'aurait, et elle
serait vérifiée par le compilateur. En C++, on l'a, mais avec
vérification lors de l'exécution -- c'est loin d'être idéal,
mais c'est mieux que rien.


tu peux m'expliquer ce point ?

le code:

class Exception {
public:
Exception(int) {}
};

int foo() throw()
{
int x = 3, y = 0;
int z = x / y;
int* p = NULL;
*p = 1;
throw Exception(3);
}

a, il me semble, 3 raisons de partir en exception; si je catche l'appel
à foo par un handler générique ("...") je tombe pour chacune d'elle (en
commentant la précédante) dans mon handler. que signifie alors la
"vérification à l'exécution" ? il faut que je change de compilo ?

C'est à croire que tu connais ni le Java, ni le C++, parce que
tu l'a exactement à l'envers. La déclaration ci-dessus en Java
garantie seulement que la fonction ne peut pas lever une parmi
un petit sous-ensemble des exceptions. Et j'ai déjà (souvent
d'ailleurs) vu des fonctions Java avec une déclaration pareille
sortir par une exception.


une exception que tu n'as pas à déclarer car dans 99% des cas cela ne
sert à rien (VM morte, stack corrompu, etc ...) - dit que cet
indéterminisme te parait inacceptable (le cas échéant) mais pas que ce
n'est pas une garantie (ou accepte que "garantie" ait un sens propre au
langage).

Quand tu spécifies throw() sur une fonction en C++, en revanche,
tu es 100% sûr que la fonction ne sortira jamais par une
exception.


ben j'attends ta réponse sur l'exemple précédent, parce que l'expérience
me montre le contraire pour l'instant.

La seule différence entre les deux, c'est qu'il faut en citer
les unes dans la déclaration de la fonction, mais pas les
autres. C'est vraiement la seule différence, selon le langage.
Et si tu régardes les cas réels, dans la API de Java, tu
constateras que les seules qu'il faut lister, ce sont celles
qu'il faut typiquement traiter à un niveau tout près. C-à-d
celles où on se servira d'un code de rétour d'une fonction en
C++, parce que c'est quand même moins lourd.


je suis assez d'accord; mais la lourdeur ne sera présente que si on fait
exprès d'être lourd: on peut (et il est pertinent de) faire un test
avant l'opération et sortir directement en erreur ou on peut y aller
bourrin et jouer du try/catch, e.g.

int foo(Object definedOrNot){
if (definedOrNot == null)
return -1;
return definedOrNot.g();
}

ou

int foo(Object definedOrNot){
try {
return definedOrNot.g();
}
catch (NullPointerException npe){
return -1;
}
}

les 2 sont valides, la seconde est inutilement lourde; reste que la
'proximité' utilisée ici n'est pas toujours vérifiée; le codeur doit
faire preuve de discernement.

L'exemple typique où une exception est la solution préférée,
c'est std::bad_alloc (en C++) ou java.lang.OutOfMemoryError (en
Java). C'est un cas type où l'erreur ne pourrait pas être
traitée localement.


"le codeur doit faire preuve de discernement" ...

s'il s'agit d'une initialisation de buffer de transaction, je ferais un
traitement local pour essayer de poursuivre avec un tampon plus petit
plutôt que de jeter l'éponge.

Il y en a beaucoup de ce genre qui dérivent
de RuntimeException ; ce sont des erreurs que, si on veut les
traiter, il faut les traiter à un niveau assez élevé,


je dirais plutôt cela des Error.

Il vaut peut-être mieux régarder les spécifications du langage,
non ? Pour Java (§8.4.4) :

Clairement, toutes les exceptions ne sont pas égales, et on peut
sortir d'une fonction par une exception (« unchecked »), même
s'il n'apparaît pas dans le throws clause.


c'est ce que j'ai indiqué bien plus haut dans le fil.

En C++ (§15.4/8,9) :

Pas de distinction entre deux catégories d'exception. Et s'il y
a une exception-specification, c'est véritablement une
garantie : quoi qui arrive, on ne sort pas avec une exception
qui n'est pas citée, et si elle est vide, on ne sort pas de la
fonction avec une exception.


il faut vraiment que je change de compilo alors:

int foo() throw(ExceptionOther){
throw Exception(3);
}

sort avec une 'Exception' ...

Je te conseille de lire la spécification de Java avant de
parler.


d'écrire, mais justement c'est fait.

Sylvain.

1 2 3 4