OVH Cloud OVH Cloud

assert

36 réponses
Avatar
Jarod
Bonjour
Est ce que vous pouvez me dire ce que fait cette macro svp ? je n'ai pas
trouvé sur le net de description de celle ci
assert(condition)
Merci d'avance

10 réponses

1 2 3 4
Avatar
nico
Salut,

Bonjour
Est ce que vous pouvez me dire ce que fait cette macro svp ? je n'ai pas
trouvé sur le net de description de celle ci
assert(condition)
Merci d'avance


Ca permet de vérifier une condition et de stopper le programme si la
condition est fausse. Généralement utilisé pour du debug/de la prevention
de bug :


//exemple


#include <assert.h>
#include <iostream>

double divide(const double& a, const double& b)
{
assert(b != 0);
return a/b;
}

int main(int argc, char ** argv)
{
std::cout << divide(5, 6) << std::endl;
std::cout << divide(5, 0) << std::endl;
std::cout << "fin" << std::endl;
return 0;
}

tu dois avoir une sortie du genre :

0.833333
Assertion `b != 0' failed.

--
nico

Avatar
nico
Jarod wrote:

Bonjour
Est ce que vous pouvez me dire ce que fait cette macro svp ? je n'ai pas
trouvé sur le net de description de celle ci
assert(condition)
Merci d'avance



mais sur google en tapant assert+c++ on trouve tout de suite :
http://www.google.fr/search?hl=fr&q=assert+c%2B%2B&spell=1

--
nico

Avatar
Pierre Maurette
Bonjour
Est ce que vous pouvez me dire ce que fait cette macro svp ? je n'ai pas
trouvé sur le net de description de celle ci
assert(condition)
assert est une macro définie dans assert.h

Je préfère écrire assert(expr), expr s'évaluant en un type "scalar".
Elle se développe différemment selon qu'une macro NDEBUG est ou non
définie *au moment de l'inclusion de assert.h*.
- Si NDEBUG est définie, elle se développe en .. que dalle ((void)0).
- Si NDEBUG n'est pas définie, la macro assert évalue expr. Si le
résultat est nul, elle affiche un message de diagnostic (sur stderr
généralement) puis appelle abort(). Si le résultat n'est pas nul, il ne
se passe rien de plus.

Attention, expr est ou non évaluée selon NDEBUG, avec les effets de
bord possibles de cette évaluation *éventuelle*.
On voit par exemple, je ncrois dans du code exemple Microsoft:

UneStructure TestA;
assert(UneFonctionApi(,,&TestA,,));
.....
assert(UneAutreFonction());

Personnellement, je préfèrerais, certainement parce que je "lis" encore
mal assert, utiliser des blocs #ifdef NDEBUG et réduire les assert à de
simples évaluations.

--
Pour répondre directement: enlever une lettre sur deux
wwaannaaddoooo -> wanadoo

Pierre Maurette

Avatar
Andre Heinen
On Mon, 20 Jun 2005 02:01:56 +0200, nico wrote:

assert(condition)


Ca permet de vérifier une condition et de stopper le programme si la
condition est fausse. Généralement utilisé pour du debug/de la prevention
de bug :


J'ajoute qu'assert a ceci de particulier qu'elle n'est pas compilée
lorsque la macro NDEBUG est définie, ce qui permet d'aisément
supprimer les tests (et ainsi accélérer le programme) lorsqu'on
compile une version release.

Assert n'est donc à utiliser que pour le débogage, et jamais pour la
détection d'erreurs susceptibles de se produire lors d'une utilisation
normale du programme.

Exemple:

#include <cassert>
#include <iostream>

double divide(double a, double b) {
assert(b != 0); // pas compilé en release
return a/b;
}

int main() {
double a, b;
std::cin >> a >> b;
if (b != 0) { // toujours compilé
std::cout << divide(a, b) << 'n';
} else {
std::cout << "Division par zéro!n";
}
}

--
André Heinen
Mon e-mail, encodé ROT13: n qbg urvara ng rhebcrnayvax qbg pbz
La FAQ: http://www.cmla.ens-cachan.fr/Utilisateurs/dosreis/C++/FAQ/


Avatar
kanze
Pierre Maurette wrote:
Est ce que vous pouvez me dire ce que fait cette macro svp ?
je n'ai pas trouvé sur le net de description de celle ci
assert(condition)


assert est une macro définie dans assert.h

Je préfère écrire assert(expr), expr s'évaluant en un type
"scalar".


En fait, assert exige un type bool comme paramètre. Il s'avère
que tous les types scalars de C++ se convertissent en bool, mais
c'est une conversion implicite -- pour le lecteur, c'est bien
plus clair si on utilise un type bool directement.

Elle se développe différemment selon qu'une macro NDEBUG est
ou non définie *au moment de l'inclusion de assert.h*.
- Si NDEBUG est définie, elle se développe en .. que dalle ((void)0).
- Si NDEBUG n'est pas définie, la macro assert évalue expr. Si le
résultat est nul, elle affiche un message de diagnostic (sur stderr
généralement) puis appelle abort(). Si le résultat n'est pas nul, i l ne
se passe rien de plus.


Tout à fait. L'utilisation classique (des bon vieux temps de C),
c'est quand le profiler montre que l'assertion pose un problème
de performance, on l'encapsule la fonction où il pose le
problème par quelque chose du genre :

// Désactiver assert en mode release dans le code suivant.
#ifdef RELEASE
#define NDEBUG
#include <assert.h>
#endif

// ...

// Réactiver assert dans la suite.
#undef NDEBUG
#include <assert.h>

Mais évidemment, ce n'est à faire que quand le profiler insiste.

Attention, expr est ou non évaluée selon NDEBUG, avec les
effets de bord possibles de cette évaluation *éventuelle*.


Normalement, l'expression ne doit pas avoir des effets de bords.
(Je dirais même que dans le code bien écrit, une expression
paramètre d'une fonction ne doit pas avoir des effets de bords.)

On voit par exemple, je crois dans du code exemple Microsoft:

UneStructure TestA;
assert(UneFonctionApi(,,&TestA,,));
.....
assert(UneAutreFonction());


Sans savoir ce que font les fonctions en question, on ne peut
pas savoir si elles ont des effets de bords.

Personnellement, je préfèrerais, certainement parce que je
"lis" encore mal assert, utiliser des blocs #ifdef NDEBUG et
réduire les assert à de simples évaluations.


Il ne faut jamais mettre des #ifdef à l'intérieur d'une
fonction. Ça rend la fonction complètement illisible. (En fait,
dans le code de production, j'interdis des #ifdef complètement.
Il y a des cas, dans les en-têtes et au niveau de portée du
fichier, où on pourrait en discuter. Mais je n'accepterais
jamais un #ifdef qui n'est pas à la portée du fichier.)

--
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
nico
wrote:


Il ne faut jamais mettre des #ifdef à l'intérieur d'une
fonction. Ça rend la fonction complètement illisible. (En fait,
dans le code de production, j'interdis des #ifdef complètement.
Il y a des cas, dans les en-têtes et au niveau de portée du
fichier, où on pourrait en discuter. Mais je n'accepterais
jamais un #ifdef qui n'est pas à la portée du fichier.)


C'est curieux, c'est pourtant pratique pour écrire du code multiplatforme.
Le premier exemple qui me vient à l'esprit, c'est l'utilisation des
sockets : car ça se fait de la même façon sous windows et unix au détail
près que sous windows il faut appeler WSAStartup() au démarrage et
WSACleanUp() à l'arret.

Donc je vois bien un truc du genre (dans le .cpp)

MonSocket::MonSocket(...)
{
#ifdef ON_EST_SOUS_WINDOWS
//...
WSAStartup(...);
//...
#endif
}

MonSocket::~MonSocket()
{
#ifdef ON_EST_SOUS_WINDOWS
WSACleanUp();
#endif
}

et evidemment dans le .h on aurait quelque chose comme ca :

#ifdef ON_EST_SOUS_UNIX
# include <unistd.h>
# include <netdb.h>
#endif

#ifdef ON_EST_SOUS_WINDOWS
# include <winsock2.h>
#endif

Enfin voila c'est juste un exemple, il me semble que procéder autrement
compliquerait la chose.

--
nico

Avatar
Pierre Maurette
wrote:


Il ne faut jamais mettre des #ifdef à l'intérieur d'une
fonction. Ça rend la fonction complètement illisible. (En fait,
dans le code de production, j'interdis des #ifdef complètement.
Il y a des cas, dans les en-têtes et au niveau de portée du
fichier, où on pourrait en discuter. Mais je n'accepterais
jamais un #ifdef qui n'est pas à la portée du fichier.)


C'est curieux, c'est pourtant pratique pour écrire du code multiplatforme.
Le premier exemple qui me vient à l'esprit, c'est l'utilisation des
sockets : car ça se fait de la même façon sous windows et unix au détail
près que sous windows il faut appeler WSAStartup() au démarrage et
WSACleanUp() à l'arret.

Donc je vois bien un truc du genre (dans le .cpp)

MonSocket::MonSocket(...)
{
#ifdef ON_EST_SOUS_WINDOWS
//...
WSAStartup(...);
//...
#endif
}

MonSocket::~MonSocket()
{
#ifdef ON_EST_SOUS_WINDOWS
WSACleanUp();
#endif
}

et evidemment dans le .h on aurait quelque chose comme ca :

#ifdef ON_EST_SOUS_UNIX
# include <unistd.h>
# include <netdb.h>
#endif

#ifdef ON_EST_SOUS_WINDOWS
# include <winsock2.h>
#endif

Enfin voila c'est juste un exemple, il me semble que procéder autrement
compliquerait la chose.
Peut-être James préfère-t-il choisir la fonction (voire la classe)

compilée en fonction du contexte ? A ce momemnt-là, si comme dans votre
exemple il y a beaucoup de code commun, insensible au contexte, il y
aura un problème de synchronisation des modifications (je ne suis pas
certain d'être clair). Il faudra alors séparer dans des fonctions
différentes le code contexte-dépendant et le code commun (en fait,
celui qui fait le boulot). Bon, ceci dit, j'ai tellement peu
d'expérience dans ce domaine...

--
Pour répondre directement: enlever une lettre sur deux
wwaannaaddoooo -> wanadoo

Pierre Maurette


Avatar
kanze
nico wrote:
wrote:

Il ne faut jamais mettre des #ifdef à l'intérieur d'une
fonction. Ça rend la fonction complètement illisible. (En
fait, dans le code de production, j'interdis des #ifdef
complètement. Il y a des cas, dans les en-têtes et au
niveau de portée du fichier, où on pourrait en
discuter. Mais je n'accepterais jamais un #ifdef qui n'est
pas à la portée du fichier.)


C'est curieux, c'est pourtant pratique pour écrire du code
multiplatforme.


Je n'en ai jamais vu trop d'intérêt.

Dans du code expérimental, il m'arrive de faire des #ifdef
rapide pour parier aux différences dans des compilateurs, du
genre :

#ifndef OLD
#include <ostream>
#include <iostream>
#else
#include <iostream.h>
#define std
#endif

Mais ça s'arrête là. (Et je ne m'en servirais jamais dans du
code de la production, où tout ce genre de détail est de toute
façon encapsulée.)

Le premier exemple qui me vient à l'esprit,
c'est l'utilisation des sockets :


J'encapule dans une class Socket. C'est tout.

car ça se fait de la même façon sous windows et unix au détail
près que sous windows il faut appeler WSAStartup() au
démarrage et WSACleanUp() à l'arret.

Donc je vois bien un truc du genre (dans le .cpp)

MonSocket::MonSocket(...)
{
#ifdef ON_EST_SOUS_WINDOWS
//...
WSAStartup(...);
//...
#endif
}

MonSocket::~MonSocket()
{
#ifdef ON_EST_SOUS_WINDOWS
WSACleanUp();
#endif
}


Je verrais plutôt deux .cc différentes, dans des répertoires
différentes. Tu compiles l'un ou l'autre selon la platforme
ciblée, au moyen des options dans le fichier de make.

(En passant, je ne sais pas comment tu peux dire que ça se fait
de la même façon sous Windows que sous Unix, quand ça ne se fait
pas de la même façon entre deux Unix.)

et evidemment dans le .h on aurait quelque chose comme ca :

#ifdef ON_EST_SOUS_UNIX
# include <unistd.h>
# include <netdb.h>
#endif

#ifdef ON_EST_SOUS_WINDOWS
# include <winsock2.h>
#endif


Ni l'un ni l'autre dans le .hh. Ce genre de détail ne concerne
pas le client.

--
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
nico
Salut,

Je verrais plutôt deux .cc différentes, dans des répertoires
différentes. Tu compiles l'un ou l'autre selon la platforme
ciblée, au moyen des options dans le fichier de make.


Ah ok je n'avais pas pensé à cela. Il est vrai que c'est une méthode plus
propre.


(En passant, je ne sais pas comment tu peux dire que ça se fait
de la même façon sous Windows que sous Unix, quand ça ne se fait
pas de la même façon entre deux Unix.)


Oui enfin je parlais de certaines fonctions que j'utilise couramment dans
mes progs du genre de gethostname(), gethostbyname()...

Ni l'un ni l'autre dans le .hh. Ce genre de détail ne concerne
pas le client.


Ok.


--
nico

Avatar
kanze
Pierre Maurette wrote:
wrote:


Peut-être James préfère-t-il choisir la fonction (voire la
classe) compilée en fonction du contexte ? A ce momemnt-là, si
comme dans votre exemple il y a beaucoup de code commun,
insensible au contexte, il y aura un problème de
synchronisation des modifications (je ne suis pas certain
d'être clair). Il faudra alors séparer dans des fonctions
différentes le code contexte-dépendant et le code commun (en
fait, celui qui fait le boulot). Bon, ceci dit, j'ai tellement
peu d'expérience dans ce domaine...


Une technique que j'ai parfois trouvé utile, c'est effectivement
que MaClasse.cc comporte un « #include <MaClasse.mcc> », où
MaClasse.mcc se trouve dans un répertoire qui dépend du système,
et contient la partie qui varie. C'est une technique qui m'a
servi beaucoup par la passée.

Mais pour dire la vérité, je me rend compte que cette technique
est surtout utile quand il y a un mauvais découpage de la
fonctionnalité ; plusieurs fois, déjà, elle a disparu suite à
une réécriture pour faire plus propre. Donc, par exemple,
autrefois, ma classe GB_StackTrace, ou la génération de la trace
se trouvait dans une StackTrace.mcc, mais toutes les autres
fonctions dans StackTrace.cc (qui incluait StackTrace.mcc). Lors
de la réécriture qui accompagniait l'introduction des namespace,
GB_StackTrace est devenu Gabi::StackTrace, et StackTrace.mcc a
disparu, en faveur d'une classe à part,
Gabi::StackTracePrivate::StackFrameIter, qui elle dépend 100% de
l'architecture de la plateforme (et donc, se trouve dans le
sous-répertoire arch/i80x86, ou arch/sparc, ou ...).

Dans l'exemple des Socket, je suis personnellement un peu
sceptique d'une partie commune, d'autant plus que dans le temps,
j'avais bien une implémentation différente pour Sun OS et
Solaris (deux versions différentes du même fournisseur de Unix),
sans parler de HP/UX. Si c'est vrai qu'il y a beaucoup de code
qui s'y ressemble, il y a aussi des différences subtiles à
chaque coins, et que j'ai l'impression qu'il vaut mieux
maintenir des sources carrément à part. (Mais j'avoue ne pas
avoir travaillé à ce niveau depuis un moment, et c'est possible
qu'il y a eu des convergences depuis.) Mais en admettant... rien
n'empèche l'existance d'une classe à part qui ne s'occupe que de
l'initialisation et de la destruction des sockets; ce qui vaut
bien mieux que quelque chose comme :

Socket::Socket( /* ... */ )
{
#if defined( Unix )
...
#elif defined( Windows )
...
#elif defined( VMS )
...
#else
#error OS not supported
#endif
}

Sans parler du cas où une partie du traitement VMS et Unix
s'avère en commun, et une autre partie est commune entre VMS et
Windows.

--
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


1 2 3 4