OVH Cloud OVH Cloud

Classes Templates imbriquées

3 réponses
Avatar
Fred
Bonjour,

J'essaye de déclarer une classe "Exception" imbriquée à l'intérieur
d'une classe template "Foo", et j'ai quelques problèmes de compilation,
j'ai recréé un petit programme d'exemple pour essayer d'isoler le
problème, mais je ne suis arrivé à le résoudre jusqu'à présent, et
google ne m'a pas donné grand chose. J'ai mis les sources du programme à
la fin du mail.
Ma classe exception dérive std::exception, et je voudrais qu'elle garde
une copie de l'objet qui a provoqué l'exception, pour afficher des infos
dessus. Le problème c'est que l'objet en question étant un template,
cela implique a priori que ma classe imbriquée soit aussi un template.
Donc en gros j'ai une classe Foo<NUMBER> où est déclarée
Exception<NUMBER2>.
J'aimerai définir le corps de ma fonction hors du fichier d'en tête, et
demander explicitement l'instanciation de cette classe pour quelques
types. Mon problème concerne justement la syntaxe de définition des
fonctions de cette classe imbriquée.
J'ai trouvé un site qui parlait vaguement de la syntaxe de définition
des membres imbriqués dans une classe template:
http://www.comeaucomputing.com/techtalk/templates/
il est dit:
<quote>
If you have a nested member template within a template:

template <typename T>
struct xyz {
// ...
template <typename U>
struct abc;
// ...
};

[...]

You would have to do this:

template <typename T>
template <typename U>
struct xyz<T>::abc {
// ...
};
</quote>

J'ai utilisé cette syntaxe (et tenté plein d'autres), or le problème,
c'est qu'aucune ne semble reconnue par le compilateur (VC++ 7.0).
Est ce que quelqu'un pourrait m'éclairer sur comment faire marcher ce
prog ?

Merci d'avance !

Fred

Sources:
// Fichier Foo.h

#include <exception>
#include <string>
#include <sstream>

using namespace std ;

template<class NUMBER> class Foo ;

template<class NUMBER>
class Foo
{
public:
template<class NUMBER2>
class Exception : public std::exception
{
public:
Exception( string msg, Foo<NUMBER2> obj ) throw() ;
const char *what() const throw() ;
private:
string userErrorMsg, fullErrorMsg ;
Foo<NUMBER2> errorObject ;
} ;

Foo( string name ) ;
void DoStuff( NUMBER x ) ;

friend ostream& operator<<( ostream& stream, const Foo<NUMBER>& obj ) ;

protected:
string objectName ;
};



// Fichier Foo.cpp

#include "foo.h"
#include <iostream>

// ***************************************
// La le compilo aime pas du tout:
// error C2988: impossible de reconnaître la définition/déclaration de
modèle
// error C2059: erreur de syntaxe : ''template<''
// error C2955: 'Foo<NUMBER>::Exception' : l'utilisation d'un modèle de
classe nécessite une liste d'arguments de modèle
// *************************************
template<class NUMBER> template<class NUMBER2>
Foo<NUMBER>::Exception::Exception( string msg, Foo<NUMBER> obj )
throw()
: userErrorMsg( msg ), errorObject( obj )
{
}

template<class NUMBER> template<class NUMBER2>
const char *Foo<NUMBER>::Exception::what() const throw()
{
ostringstream oss ;
oss << "problem with objet: " << errorObject << endl
<< objecterrorMsg << endl ;
fullErrorMsg = oss.str() ;
return fullErrorMsg.c_str() ;
}


template<class NUMBER>
Foo<NUMBER>::Foo( string name )
: objectName( name )
{
}

template<class NUMBER>
void Foo<NUMBER>::DoStuff( NUMBER x )
{
if( x < 0 )
{
throw Exception<NUMBER>( "Error", *this ) ;
}
cout << "Hello" << endl ;
}

template<class NUMBER>
ostream& operator<<( ostream& stream, const Foo<NUMBER>& obj )
{
return stream << ' ' << obj.objectName ;
}

template Foo<double> ;
template ostream& operator<<( ostream& stream, const Foo<double>& obj ) ;



// Main.cpp

#include "Foo.h"
#include <iostream>

int main(void)
{
try
{
Foo<double> bar( "Test object" ) ;

bar.DoStuff( -1.0 ) ;
cout << bar << endl ;
}
catch( const exception& e )
{
cerr << e.what() << endl ;
}
}

3 réponses

Avatar
Olivier Azeau
Fred wrote:
Bonjour,

J'essaye de déclarer une classe "Exception" imbriquée à l'intérieur
d'une classe template "Foo", et j'ai quelques problèmes de compilation,
j'ai recréé un petit programme d'exemple pour essayer d'isoler le
problème, mais je ne suis arrivé à le résoudre jusqu'à présent, et
google ne m'a pas donné grand chose. J'ai mis les sources du programme à
la fin du mail.
Ma classe exception dérive std::exception, et je voudrais qu'elle
garde une copie de l'objet qui a provoqué l'exception, pour afficher des
infos dessus. Le problème c'est que l'objet en question étant un
template, cela implique a priori que ma classe imbriquée soit aussi un
template.
Donc en gros j'ai une classe Foo<NUMBER> où est déclarée
Exception<NUMBER2>.
J'aimerai définir le corps de ma fonction hors du fichier d'en tête,
et demander explicitement l'instanciation de cette classe pour quelques
types. Mon problème concerne justement la syntaxe de définition des
fonctions de cette classe imbriquée.


Cette solution me parait bien compliquée.
Ca ne suffirait pas un truc comme ça ?

template<class ERROBJ>
class Exception : public std::exception
{
public:
Exception( string msg, ERROBJ obj ) throw()
....
private:
....
ERROBJ errorObject;
};

template<class NUMBER>
class Foo
{
....
void DoStuff( NUMBER x )
{
....
throw Exception< Foo<NUMBER> >( "Error", *this ) ;
....
}
....
};

Avatar
Fred
Olivier Azeau wrote:
Cette solution me parait bien compliquée.
Ca ne suffirait pas un truc comme ça ?

template<class ERROBJ>
class Exception : public std::exception
{
public:
Exception( string msg, ERROBJ obj ) throw()
....
private:
....
ERROBJ errorObject;
};

template<class NUMBER>
class Foo
{
....
void DoStuff( NUMBER x )
{
....
throw Exception< Foo<NUMBER> >( "Error", *this ) ;
....
}
....
};


Oui, je reconnais que c'est peut être un peu compliqué, mais en fait la
limitation que je vois dans ta solution c'est, que la description de
l'erreur se limitera au message passé au constructeur de Exception, car
la classe Exception définie comme ceci tend à être commune à toutes les
exceptions lancées par le programme.

On pourrait toutefois quand même afficher également des infos sur
l'objet, mais ça supposerait implicitement qu'il devrait forcément avoir
son opérateur d'insertion dans ostream défini. De plus si celui ci est
déja utilisé mais que ce qu'il affiche n'est pas suffisant par rapport
aux messages d'erreurs, il faut trouver une autre solution.

Après je suis étudiant, et c'est la première fois que j'essaye
d'utiliser sérieusement les exceptions dans un programme: c'est à dire
en les utilisant systématiquement par rapport aux codes d'erreur, et en
essayant d'être cohérent au niveau des types d'exception lancée, donc
peut etre que la solution que j'avance n'est pas très pratique, et que
l'expérience préconise plutot d'autres approches. Je veux bien qu'on me
propose autre chose... :)

Avatar
Olivier Azeau
Fred wrote:
Olivier Azeau wrote:

Cette solution me parait bien compliquée.
Ca ne suffirait pas un truc comme ça ?

template<class ERROBJ>
class Exception : public std::exception
{
public:
Exception( string msg, ERROBJ obj ) throw()
....
private:
....
ERROBJ errorObject;
};

template<class NUMBER>
class Foo
{
....
void DoStuff( NUMBER x )
{
....
throw Exception< Foo<NUMBER> >( "Error", *this ) ;
....
}
....
};



Oui, je reconnais que c'est peut être un peu compliqué, mais en fait
la limitation que je vois dans ta solution c'est, que la description de
l'erreur se limitera au message passé au constructeur de Exception, car
la classe Exception définie comme ceci tend à être commune à toutes les
exceptions lancées par le programme.


Un template ça se spécialise, non ?

| template<SomeClass>
| class Exception : public std::exception
| {
| public:
| Exception( string msg, SomeClass obj ) throw() {
| // implémentation spécifique de l'exception pour la classe

On pourrait toutefois quand même afficher également des infos sur
l'objet, mais ça supposerait implicitement qu'il devrait forcément avoir
son opérateur d'insertion dans ostream défini. De plus si celui ci est
déja utilisé mais que ce qu'il affiche n'est pas suffisant par rapport
aux messages d'erreurs, il faut trouver une autre solution.


Mais l'opérateur d'insertion dans ostream, c'est déjà ce que tu fais
avec ta solution, non ? Tu devrais donc avoir le même problème si
l'opérateur d'insertion ne suffit pas ?

Après je suis étudiant, et c'est la première fois que j'essaye
d'utiliser sérieusement les exceptions dans un programme: c'est à dire
en les utilisant systématiquement par rapport aux codes d'erreur, et en
essayant d'être cohérent au niveau des types d'exception lancée, donc
peut etre que la solution que j'avance n'est pas très pratique, et que
l'expérience préconise plutot d'autres approches. Je veux bien qu'on me
propose autre chose... :)


Tout est question de bon dosage.
Le risque que je vois à vouloir trop spécialiser les exceptions c'est de
finir par remplacer la logique d'une application par des successions
interminables de blocs try-catch.

| try {
| // ...
| }
| catch( SomeExceptionThatTellsThingsHappenedThisWay & )
| {
| // ...
| }
| catch( SomeOtherExceptionThatTellsSomeOtherThing & )
| {
| try {
| // ...
| }
| catch( SomeExceptionThatHappensInThisSubCase & )
| {
| // ...
| }
| }

Pour moi, une exception ça s'utilise dans les cas exceptionnels !
Et je ne vois pas en quoi un comportement un peu alternatif d'une
fonction serait exceptionnel...

Une bonne mesure de l'utilisation des exceptions est, à mon avis, de
regarder où se situent les blocs try-catch correspondants.
S'ils sont dans la fonction immédiatement appelante, il y a de fortes
chances pour que l'exception soit injustifiée.
S'ils sont dans le main(), il y a de fortes chances pour que l'exception
soit justifiée.
Entre les 2, il faut analyser plus finement...
Par exemple en regardant si les appelants auraient pu se passer de
l'exception avec un contrôle a priori.