OVH Cloud OVH Cloud

Création de macro

27 réponses
Avatar
Michaël Delva
Bonjour à tous,

j'utilise DirectShow, et je me retrouve très souvent à employer ce genre de
syntaxe pour vérifier le bon déroulement du code:

hr = this->ConnectFilters(pGraph,pInfTee,pMux);
if (FAILED(hr))
{
ShowMessage("Impossible de connecter pInfTee à pMux");
return false;
}

FAILED étant une macro de DIrectShow permettant de vérifier que hr est
différent de S_OK, la valeur de retour indiquant que tout s'est bien passé.

Je voudrais créer une macro me permettant de réduire le code, quelque chose
du style:

TEST_FAILED(this->ConnectFilters(pGraph,pInfTee,pMux),"Impossible de
connecter pInfTee à pMux");

et qui ait le même comportement que le code ci-dessus...

Seulement je sais absolument pas utiliser les macros...

Une âme charitable pourrait-elle m'aider?

Merci d'avance!

10 réponses

1 2 3
Avatar
Richard Delorme
Bonjour à tous,

j'utilise DirectShow, et je me retrouve très souvent à employer ce genre de
syntaxe pour vérifier le bon déroulement du code:

hr = this->ConnectFilters(pGraph,pInfTee,pMux);
if (FAILED(hr))
{
ShowMessage("Impossible de connecter pInfTee à pMux");
return false;
}

FAILED étant une macro de DIrectShow permettant de vérifier que hr est
différent de S_OK, la valeur de retour indiquant que tout s'est bien passé.

Je voudrais créer une macro me permettant de réduire le code, quelque chose
du style:

TEST_FAILED(this->ConnectFilters(pGraph,pInfTee,pMux),"Impossible de
connecter pInfTee à pMux");

et qui ait le même comportement que le code ci-dessus...

Seulement je sais absolument pas utiliser les macros...


#define TEST_FAILED(hr, msg)
if (FAILED(hr)) {
ShowMessage(msg);
return false;
} else (void) 0

Je précise : les macros, c'est mal... surtout en C++. A mon avis, celle
ci est particulièrement retors car elle cache un return et donc le flow
réel du programme.

--
Richard

Avatar
Loïc Joly
Michaël Delva wrote:
Bonjour à tous,

j'utilise DirectShow, et je me retrouve très souvent à employer ce genre de
syntaxe pour vérifier le bon déroulement du code:

hr = this->ConnectFilters(pGraph,pInfTee,pMux);
if (FAILED(hr))
{
ShowMessage("Impossible de connecter pInfTee à pMux");
return false;
}

FAILED étant une macro de DIrectShow permettant de vérifier que hr est
différent de S_OK, la valeur de retour indiquant que tout s'est bien passé.

Je voudrais créer une macro me permettant de réduire le code, quelque chose
du style:

TEST_FAILED(this->ConnectFilters(pGraph,pInfTee,pMux),"Impossible de
connecter pInfTee à pMux");


Pourquoi une macro ? Voici une fonction qui fait une chose assez semblable :

void testFailed(type_de_hr hr, std::string const &message)
{
if FAILED(hr)
throw (DirectShowException(message));
}

Avec type_de_hr et DirectShowException des types qui vont bien.

--
Loïc

Avatar
Michaël Delva
Pourquoi une macro ? Voici une fonction qui fait une chose assez
semblable :

void testFailed(type_de_hr hr, std::string const &message)
{
if FAILED(hr)
throw (DirectShowException(message));
}

Avec type_de_hr et DirectShowException des types qui vont bien.



Les exceptions sont assez inconnues pour moi... En quoi throw
(DirectShowException(message)) peut retourner un false?

Et à quoi doit ressembler DirectShowException?

Merci d'avance!

Avatar
Fabien LE LEZ
On 07 Sep 2004 23:24:37 GMT, "Michaël Delva"
:

Les exceptions sont assez inconnues pour moi...


Ben faut apprendre. Ou faire du C.

En quoi throw
(DirectShowException(message)) peut retourner un false?


Il ne peut pas. Mais une exception est a priori plus adaptée qu'un
"return false".

Au pire, tu peux faire un :

void testFailed(type_de_hr hr, std::string const &message)
{
if FAILED(hr)
throw (DirectShowException(message));
}

void f_impl (...)
{
...
testFailed (...);
...
testFailed (...);
...
testFailed (...);
}

bool f (...)
{
try
{
f_impl (...);
return true;
}
catch (DirectShowException const& e)
{
cerr << e.what() << endl;
return false;
}
}

...mais j'ai bien dit "au pire". L'idée des exceptions, c'est d'éviter
de se coltiner des tests de valeurs de retour à tout bout de champ --
on teste l'erreur (i.e. try...catch...) au moment où on sait la
traiter.

Et à quoi doit ressembler DirectShowException?


class DirectShowException: public std::exception
{
public:
DirectShowException (std::string const& msg) : message (msg) {}
char const* what() const { return message.c_str(); }
private:
std::string message;
};


--
;-)

Avatar
Michaël Delva
...mais j'ai bien dit "au pire". L'idée des exceptions, c'est d'éviter
de se coltiner des tests de valeurs de retour à tout bout de champ --
on teste l'erreur (i.e. try...catch...) au moment où on sait la
traiter.

Ok, parce que moi j'ai des fonctions qui ressemblent à ça:


bool __fastcall Create_Graph()
{
CComPtr<IBaseFilter> pInfTee;
hr = pInfTee.CoCreateInstance(CLSID_InfTee);
if (FAILED(hr))
{
ShowMessage("blabla");
return false;
}

hr = pGraph->AddFilter(pInfTee,L"Infinite Pin Tee Filter");
if (FAILED(hr))
{
ShowMessage("blabla");
return false;
}

// Add the File Source filter to the graph.
CComPtr<IBaseFilter> pFileSource;
hr = pGraph->AddSourceFilter(path_in, L"Source",&pFileSource);
if (FAILED(hr))
{
ShowMessage("blabla");
return false;
}

return true;
}

Comme ça dès qu'il y a une erreur je sors de la fonction...
Ce qui visiblement est un manière de faire à éviter...

Si je remplace par ce que vous venez de me dire:

class DirectShowException: public std::exception
{
public:
DirectShowException (const AnsiString & message) : message (msg)
{}
AnsiString what() const { return message; }
private:
AnsiString message;
};

void testFailed(HRESULT hr, const AnsiString & message)
{
if FAILED(hr)
throw (DirectShowException(message));
}

void __fastcall Create_Graph()
{
CComPtr<IBaseFilter> pInfTee;

testFailed(pInfTee.CoCreateInstance(CLSID_InfTee),"blabla pInfTee");
testFailed(pGraph->AddFilter(pInfTee,L"Infinite Pin Tee
Filter"),"blabla");

CComPtr<IBaseFilter> pFileSource;
testFailed(pGraph->AddSourceFilter(path_in,
L"Source",&pFileSource),"blabla"); }

Et c'est à l'endroit où je lance ma fonction Create_Graph que je fais
comme suit:

void toto()
{
try
{
Create_Graph();
}
catch (DirectShowException const& e)
{
ShowMessage(e.what());
}
}

C'est bien ça?

Avatar
Michaël Delva
Et sachant que mon main() est enchassé dans un bloc comme celui-ci (par
défaut avec BCB6, mais je crois que c'est très recommandé de procéder de la
sorte):

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->Title = "toto";
Application->CreateForm(__classid(TMainForm), &MainForm);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
catch (...)
{
try
{
throw Exception("");
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
}
return 0;
}

Ai-je besoin d'écrire toto comme ceci:

void toto()
{
try
{
Create_Graph();
}
catch (DirectShowException const& e)
{
ShowMessage(e.what());
}
}

Ou bien cela suffit-il??

void toto()
{
Create_Graph();
}

Et dernières questions:
* lancer une exception comme ce que vous me conseillez permet-il de sortir
de la fonction sans encombres? (tous mes pointeurs sont encapsulés par ATL,
donc je pense ne pas avoir de soucis avec ça)
* Dans le constructeur de certaines classes je pourrai donc être amené à
lancer une exception... Avant d'aller lire le chapitre qui y est consacré
dans le livre de Stroustrup demain, comment ça se passe dans ce cas?

Merci d'avance
Avatar
Fabien LE LEZ
On 08 Sep 2004 00:03:40 GMT, "Michaël Delva"
:

Comme ça dès qu'il y a une erreur je sors de la fonction...
Ce qui visiblement est un manière de faire à éviter...


Avec les exceptions aussi, on sort de la fonction. Mais utiliser des
valeurs de retour à tire-l'arigot est sans conteste une manière très
lourde de s'en sortir.

Je te propose un exemple pour illustrer l'utilisation des exceptions
et des hiérarchies d'exceptions. Note que le style du reste du
programme (notamment la gestion de fichiers à la C) laisse à désirer,
mais c'est juste pour l'exemple.

Supposons que tu veuilles utiliser les fonctions de gestion de
fichiers de l'API Windows pour ouvrir un fichier, y lire deux nombres,
et y écrire la somme de ces deux nombres, quand l'utilisateur clique
sur un bouton.

class ExceptionGeneraleFichier: public std::exception
{
public:
ExceptionGeneraleFichier (std::string const& nom_fonction)
{
std::string message_erreur= FormatMessage (GetLastError()); /*
L'appel de ces deux fonctions de l'API Windows est plus compliqué,
mais tu vois le principe... */
message= "La fonction " + nom_fonction
+ " a renvoyé l'erreur " + message_erreur + ".";
}

char const* what() const { return message.c_str(); }

private:
std::string message;
};

class ExceptionManqueDePlace: public ExceptionGeneraleFichier
{
public: ExceptionManqueDePlace (std::string const& nom_fonction)
: ExceptionGeneraleFichier (nom_fonction) {}
};

void EnregistreChaine (...)
{
if (WriteFile (...) == ERROR)
{
if (GetLastError() == DISK_IS_FULL)
{
throw ExceptionManqueDePlace ("WriteFile");
}
else
{
throw ExceptionGeneraleFichier ("WriteFile");
}
}
}

void EnregistreNombre (..., int n)
{
EnregistreChaine (..., Formate (n));
}

std::string LireChaine (...)
{
if (ReadFile (...) == ERROR)
{
throw ExceptionGeneraleFichier ("WriteFile");
}
return resultat;
}

int LireNombre (...)
{
return Convertir (LireChaine (...));
}

Fichier OuvrirFichier (...)
{
if (CreateFile (...) == ERROR)
{
throw ExceptionGeneraleFichier ("CreateFile");
}

return ...;
}

void FermerFichier (...)
{
if (CloseHandle (...) == ERROR)
{
throw ExceptionGeneraleFichier ("CloseHandle");
}

return ...;
}

void g (...)
{
...= OuvrirFichier (...);
int n1= LireNombre (...);
int n2= LireNombre (...);
EnregistreNombre (..., n1 + n2);
FermerFichier (...);
}

void f (...)
{
try
{
g (...);
}
catch (ExceptionManqueDePlace const&)
{
SupprimerFichiersTemporaires(); /* Si on manque de place sur
disque dur, on supprime des fichiers inutiles... */
g(); /* ... puis on tente à nouveau */
}
}

void ClicBouton()
{
try
{
f();
}
catch (std::exception const& e)
{
AfficherMessage (e.what());
}
}




--
;-)

Avatar
Fabien LE LEZ
On 08 Sep 2004 00:15:56 GMT, "Michaël Delva"
:

* Dans le constructeur de certaines classes je pourrai donc être amené à
lancer une exception... Avant d'aller lire le chapitre qui y est consacré
dans le livre de Stroustrup demain, comment ça se passe dans ce cas?


L'objet n'est pas construit. Ce qui ne pose pas de problème, puisqu'en
sortant du bloc "try", l'objet aurait été détruit de toutes façons.

Exemple :

struct Machin
{
Machin() { if (quelque_chose) throw MonException(); }
};

void f()
{
try
{
Machin m;
m.f(); // [1]
}
catch (...)
{
cerr << "Une exception lancée !" << endl;
}

};

Dans la ligne [1], m est forcément construit, car si Machin::Machin()
a lancé une exception, on n'atteint jamais cette ligne.


--
;-)

Avatar
Fabien LE LEZ
On 08 Sep 2004 00:15:56 GMT, "Michaël Delva"
:

Ou bien cela suffit-il

??


Fais gaffe, ta touche "?" se coince.

void toto()
{
Create_Graph();
}


Si les exceptions lancées sont des erreurs fatales, qui nécessitent
l'arrêt du programme, OK. Sinon, ça ne va pas.

Je me permets de modifier un peu mon programme précédent :

void ClicBouton()
{
try
{
f();
}
catch (ExceptionGeneraleFichier const& e)
{
AfficherMessage (e.what());
}
}

int main()
{
try
{
...
}
catch (std::exception const& e)
{
cerr << e.what() << endl;
}
}

Ainsi, si au cours de g() une exception non liée aux fichiers (du
style, manque de mémoire), on considère ça comme une erreur fatale et
le programme se quitte après avoir affiché le message sur cerr.


--
;-)

Avatar
Jonathan Mcdougall
Bonjour à tous,

j'utilise DirectShow, et je me retrouve très souvent à employer ce genre
de

syntaxe pour vérifier le bon déroulement du code:

hr = this->ConnectFilters(pGraph,pInfTee,pMux);


Tu es au courant que this-> est redondant dans la plupart des cas?

if (FAILED(hr))
{
ShowMessage("Impossible de connecter pInfTee à pMux");
return false;
}

FAILED étant une macro de DIrectShow permettant de vérifier que hr est
différent de S_OK, la valeur de retour indiquant que tout s'est bien
passé.


Je voudrais créer une macro me permettant de réduire le code, quelque
chose

du style:


Mauvaise idée, les macros sont à éviter en C++.

TEST_FAILED(this->ConnectFilters(pGraph,pInfTee,pMux),"Impossible de
connecter pInfTee à pMux");

et qui ait le même comportement que le code ci-dessus...

Seulement je sais absolument pas utiliser les macros...


Concernant la macro, tu as déjà eu des réponses.

Une âme charitable pourrait-elle m'aider?


Une simple fonction serait plus adaptée à ton problème :

bool test_failed(HRESULT hr, std::string message)
{
if (FAILED(hr))
{
ShowMessage(message); // possiblement .c_str()
return false;
}

return true;
}


void f()
{
if ( test_failed( mafonction(), "probleme avec mafonction()") )
return;
}

ou encore

class MonException {};

void check(HRESULT hr, std::string message) throw (MonException)
{
if (FAILED(hr))
{
ShowMessage(message); // possiblement .c_str()
throw MonException;
}
}

void f()
{
try
{
check(mafonction(), "probleme avec mafonction()");
check(mafonction2(), "probleme avec mafonction2()");
check(mafonction3(), "probleme avec mafonction3()");
}
catch(MonException &)
{
// oups, clean up et sortie
}
}

Plusieurs designs sont possible, entre autre que check() mette le message
dans l'exception et le catch() pourrait être chargé de l'afficher. Tout
ceci dépend de ta situation et de tes connaissances.


Jonathan Mcdougall
Montréal, Québec

1 2 3