OVH Cloud OVH Cloud

Problème d'écriture avec ofstream

20 réponses
Avatar
Marc G
bonjour,
j'ai un problème (a priori simple mais je ne trouve pas la solution :-))

j'ai la fonction

std::ofstream& html_header(std::ofstream& flux)
{
flux<<"<html>\n<head>\n</head>\n<body>\n";
return flux;
}

ensuite quand je veux l'appeler :

std::ofstream fout("test.html",std::ios::out);
html_header(fout); // là ça marche
fout << html_header; // là j'ai des chiffres dans le fichier....
!

est-ce que quelqu'un sait pourquoi ?
Merci
Marc

10 réponses

1 2
Avatar
Jean-Marc Bourguet
"Marc G" writes:

j'essaye de généraliser ta méthode avec des templates

template <class T>
class toHTML {
public :
toHTML(const T &t) : _t(t) {}
friend std::ostream& operator<<(std::ostream&,const toHTML&);
private :
const T &_t;
};

// cette fonction template doit être spécialisée pour les classes
// avec une sortie HTML possible
template <class T>
std::ostream& operator<<(std::ostream &flux,const toHTML<T> &source)
{
flux<<"sortie HTML non définie";
return flux;
}


UnObjet objet;
std::ofstream fout("test.html",std::ios::out);
fout << toHTML<UnObjet >(objet) ; // <--- là, ça coïnce !

Est-ce que tu saurais comment s'y prendre ?


Tu ne dis pas exactement ce qui coince et tu ne donnes pas un code
complet... je suppose qu'il s'agit du problème classique: tu déclares comme
amie une fonction non template et tu t'attends à ce que la fonction
template le soit.

template <typename T> void friendFn2(Foo<T> const&);
template <typename T> void friendFn3(T const&);
template <typename T> void friendFn4(Foo<T> const&);
template <typename T> void friendFn4(Foo<int> const&);
template <typename T> void friendFn5(Foo<int> const&, T const&);

template <typename T> struct Foo
{
friend void friendFn1(Foo const&); // 1
friend void friendFn2<T>(Foo const&); // 2
friend void friendFn3<>(Foo const&); // 3
friend void friendFn4(Foo const&); // 4
template <typename U> friend void friendFn5(Foo const&, U const&); // 5
private:
int x;
};

void friendFn1(Foo<int> const& f) { f.x; } // 6
template<typename T> void friendFn2(Foo<T> const& f) { f.x; } // 7
template<typename T> void friendFn3(Foo<T> const& f) { f.x; } // 8
template<typename T> void friendFn4(Foo<T> const& f) { f.x; } // 9
void friendFn4(Foo<int> const& f) { f.x; } // 10
template<typename T> void friendFn5(Foo<int> const& f, T const&) { f.x; } // 11

int main()
{
Foo<int> f;
friendFn1(f); // 12
friendFn2(f); // 13
friendFn3(f); // 14
friendFn4<>(f); // 15
friendFn4(f); // 16
friendFn5(f, 1); // 17
}

La ligne 1 déclare comme amie une fonction non template prenant donc une
instantiation de la classe comme paramètre. Cette fonction est définie à la
ligne 6 et appelée à la ligne 12.

La ligne 2 déclare comme amie une la spécialisation d'une fonction
template. La fonction est définie à la ligne 7 et appelée à la ligne 13.

La ligne 3 est une syntaxe alternative qui peut être utilisée quand tous
les paramètres templates peuvent être déduit des arguments.

La ligne 5 déclare comme amies toutes les spécialisation d'une fonction
template. La fonction est définie à la ligne 11 et appelée ligne 17.

La subtilité intervient avec la ligne 4. Le C++ permet de surcharger une
fonction template avec une fonction non template. Dans ce cas il peut y
avoir donc une spécialisation de la fonction template qui a le même
prototype que la fonction non template. Dans ce cas, la fonction non
template est préférée à la fonction template, mais on peut spécifier qu'il
faut appeler la fonction template avec la syntaxe de la ligne 15.

La ligne 4 est de même structure que la ligne 1, donc elle déclare comme
amie la fonction non template définie à la ligne 10 et appelée ligne 16.

La fonction template de même nom, définie à la ligne 9 et appelée ligne 15
n'est pas amie, donc le programme comme tel ne doit pas compiler tant
qu'elle tente d'accéder à un membre privé ou qu'on ne l'a pas déclaré amie
avec la syntaxe de la ligne 2 ou de la ligne 3.

Comme il est rare qu'on désigne déclarer comme amie des fonctions non
templates et que la subtilité ci-dessus fait des dégats, certains
compilateurs (dont g++ donnent) un avertissement.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
kanze
Marc G wrote:
c'est "simple" mais il faut quand même avoir l'habitude de
manier des écritures du genre

ostream& (MonObject::* const pf[])( ostream&)
ce qui n'est pas (encore) mon cas :-((


Il faut bien que je roule un peu la méchanique, quand même:-).
Dans la pratique, beaucoup de monde utilise des typedef pour
simplifier l'écriture, quelque chose du genre :

typedef ostream& (MonObject::*DisplayFunction)( ostream& ) const ;
DisplayFunction pf[ ] = { ... } ;

Et il y en a beaucoup qui n'aime pas du tout des pointeurs aux
fonctions membre, et qui se servirait simplement d'un switch sur
le mode.

Dans la pratique, je crois que j'imposerait une convention de
nommage, et que je génèrerais et l'implémentation de cette
fonction et les manipulateurs automatiquement, au moyen d'un
petit script (AWK) et une liste des formats supportés.
(Peut-être pas pour deux formats, mais si le nombre de formats
commençait à augmenter.)

mais je me suis accroché et j'ai compris.

En fait, je ne connaissait pas non plus l'existence de xalloc
et iword.


Les iostream sont pleins des fonctions utiles que prèsque
personne ne connaît.

--
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
Jean-Marc Bourguet
"Marc G" writes:

mon compilo refuse un truc du genre

template <>
template <class T>
std::ostream& operator<<(std::ostream &flux,const toHTML<
ClasseTemplate<T> > &source)
{
flux<<"blabla";
return flux;
}


template <class T>
std::ostream& operator<<(std::ostream &flux,const toHTML<
ClasseTemplate<T> > &source)
{
flux<<"blabla";
return flux;
}

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
kanze
Marc G wrote:
j'essaye de généraliser ta méthode avec des templates

template <class T>
class toHTML {
public :
toHTML(const T &t) : _t(t) {}
friend std::ostream& operator<<(std::ostream&,const toHTML&);
private :
const T &_t;
};

// cette fonction template doit être spécialisée pour les cla sses
// avec une sortie HTML possible
template <class T>
std::ostream& operator<<(std::ostream &flux,const toHTML<T> &source)
{
flux<<"sortie HTML non définie";
return flux;
}

UnObjet objet;
std::ofstream fout("test.html",std::ios::out);
fout << toHTML<UnObjet >(objet) ; // <--- là, ça coïnce !

Est-ce que tu saurais comment s'y prendre ?


Jean-Marc a expliqué pourquoi ça ne marche pas, mais j'aimerais
ajouter deux rémarques :

D'abord, dans de tels cas, je le trouve beaucoup plus simple de
simplement définir la fonction amie directement dans la classe.
Comme ça, j'évite le besoin des déclarations anticipées, etc.
Je crois que ça empèche la possibilité qu'un utilisateur la
spécialiser explicitement, mais à mon avis, ce n'est pas un
problème. On définit une convention de nommage pour l'affichage
HTML (disons, une fonction «void displayHTML( std::ostream& )
const »), et on l'appelle. Ce qui donne une erreur lors de la
compilation si la fonction n'existe pas ; si on veut l'erreur
lors de l'exécution seulement, comme dans ton exemple, il
faudrait se rebattre sur un peu de méta-programmation :

template< typename T >
class HTMLFormatter
{
public:
explicit HTMLFormatter( T const& obj )
: myObj( &obj )
{
}

friend
std::ostream& operator<<(
std::ostream& dest,
HTMLFormatter const&source )
{
HTMLFormatter::doDisplay( dest, *source.myObj ) ;
return dest ;
}

private:
T const* myObj ;

template< void (T::*pmf)( std::ostream& ) const > struct
Discriminator {};
template< typename U >
static void doDisplay(
std::ostream& dest,
U const& obj,
Discriminator< &U::displayHTML >* = 0 )
{
obj.displayHTML( dest ) ;
}

static void doDisplay(
std::ostream& dest,
... )
{
dest << "NOT SUPPORTED: Attempt to display HTML for "
<< typeid( T ).name()
}
} ;

Deuxièmement, du point de vue de l'utilisateur, c'est bien plus
agréable de ne pas avoir à spécifier le type. Ce qu'on peut
faire facilement en ajoutant une fonction templatée :

template< typename T >
inline HTMLFormatter< T >
asHTML( T const& obj )
{
return HTMLFormatter< T >( obj ) ;
}

L'avantage, c'est que pour les fonctions, le compilateur fait la
déduction du type automatiquement, à partir du type des
paramètres. Ceci permet donc une écriture :
std::cout << asHTML( monObjet ) << std::endl ;
Le compilateur instantierait asHTML sur le type de monObjet.

--
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
Jean-Marc Bourguet
"Marc G" writes:

template <class T>
std::ostream& operator<<(std::ostream &flux,const toHTML<
ClasseTemplate<T> > &source)
{
flux<<"blabla";
return flux;
}


le problème avec cette solution, c'est que ça compile très bien mais que le
compilateur préfère toujours la version

template <class T>
std::ostream& operator<<(std::ostream &flux,const toHTML<T> &source)
{
flux<<"sortie HTML non définie !";
return flux;
}

qui est dans le fichier de déclaration/définition de template <class T>
toHTML;

donc en fait, c'est pas utilisable :-((

si j'écris

template <>
std::ostream& operator<<(std::ostream &flux,const toHTML<
ClasseTemplate<std::string> > &source)
{
flux<<"ClasseTemplate -> std::string !";
return flux;
}
avec un paramètre déjà défini pour T, là c'est la bonne version de
l'opérateur qui est choisie !

c'est pour ça que je pressens que l'écriture est du type

template <>

template<class T>
std::ostream& operator<<(std::ostream &flux,const toHTML< ClasseTemplate<T>
&source)
{

flux<<"ClasseTemplate -> std::string !";
return flux;
}

mais mon compilo me met délcration de modèle incorrecte




#include <ostream>
#include <iostream>

template <class T>
class toHTML;

template <class T>
std::ostream& operator<<(std::ostream &flux, const toHTML<T> &source);

template <class T>
class toHTML
{
public :
toHTML(const T &t) : t_(t) {}
friend std::ostream& operator<< <T>(std::ostream&,const toHTML&);
private :
const T &t_;
};

template <typename T>
toHTML<T> makeHTML(T const& t)
{
return toHTML<T> (t);
}

template <class T>
std::ostream& operator<<(std::ostream &flux,const toHTML<T> &source)
{
flux << "sortie HTML non définie !";
return flux;
}

template <typename T>
class ClasseTemplate
{
};

template <class T>
std::ostream& operator<<(std::ostream &flux,const toHTML<ClasseTemplate<T> > &source)
{
flux << "blabla";
return flux;
}


int main()
{
ClasseTemplate<int> obj;
std::cout << makeHTML(obj);
}

Donne blabla avec les deux compilateurs que j'ai essayé.

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Marc G
C'est terrible, ça marche pas sur mon compilo...et je sens qu'il est
vraiment un peu faible pour gérer la compléxité des templates
1) d'abord, j'ai dû faire une petite modification pour que l'unité soit
compilée

Discriminator< &U::displayHTML >* = 0 ) ---> Discriminator<
&(U::displayHTML) > * = 0 )
{
obj.displayHTML( dest ) ;
}
donc j'ai dû mettre U::displayHTML entre parenthèses et aussi mettre un
espace entre * et = (sinon il interprète comme l'opérateur *= )
2) l'unité compile enfin
si je teste avec une classe qui a défini doDisplay, ça marche
si je teste avec une classe qui n'a pas défini doDisplay, j'ai un message
d'erreur...à l'édition de liens
Il me met :
Le qualificateur de type dépendant 'MaClasse' n'a aucun symbole nommé
'displayHTML'
Je ne comprends pas pourquoi le compilateur ne choisit pas la deuxième
version de doDisplay
Dommage, je trouve ta solution assez élégante...
Merci encore
Marc
Avatar
James Kanze
Marc G wrote:

C'est terrible, ça marche pas sur mon compilo...et je sens
qu'il est vraiment un peu faible pour gérer la compléxité des
templates


Le code présenté exige en effet un support des templates assez à
jour. Mais seulement si tu tiens vraiment à ce que le message
d'erreur apparaisse à l'execution. Si tu pourrais accepter une
erreur lors de la compilation (ce qui me semble même préférable
la plupart du temps), tu peux simplier énormement :

std::ostream& operator<<(
std::ostream& dest,
HTMLFormatter const&source )
{
source.myObj->displayHTML( dest ) ;
return dest ;
}

(toujours complètement dans la classe templatée HTMLFormatter).
Et supprimer les deux fonctions doDisplay (et le typedef tout de
suite au-dessus) complètement. (J'imagine que ce qui pose des
problèmes au compilateur, c'est la déduction du type lors de
l'appel de doDisplay, qui dépend des règles assez pointues de la
norme.)

1) d'abord, j'ai dû faire une petite modification pour que l'unité so it
compilée

Discriminator< &U::displayHTML >* = 0 ) ---> Discriminat or<
&(U::displayHTML) > * = 0 )
{
obj.displayHTML( dest ) ;
}
donc j'ai dû mettre U::displayHTML entre parenthèses et aussi mettre un
espace entre * et = (sinon il interprète comme l'opérateur *= )


Il me semble que j'avais un éspace entre le * et le =. Sinon, le
compilateur doit l'interpréter comme un *=, et donc générer une
erreur. (C'est possible qu'il y a une erreur dans g++, avec
lequel je l'ai essayé, mais je le doute. D'autant plus que mon
style habituel y met bien un espace avant et après l'opérateur
d'affectation, toujours.)

Pour la reste, je ne sais pas pourquoi le compilateur aurait
besoin des parenthèses ici, mais ben, si ce n'est que ça...

2) l'unité compile enfin
si je teste avec une classe qui a défini doDisplay, ça marche
si je teste avec une classe qui n'a pas défini doDisplay, j'ai un mess age
d'erreur...à l'édition de liens
Il me met :
Le qualificateur de type dépendant 'MaClasse' n'a aucun symbole nommé
'displayHTML'
Je ne comprends pas pourquoi le compilateur ne choisit pas la deuxième
version de doDisplay


Parce qu'apparemment, il a réussi à instancier le template de la
première version. Grosso modo, si la résolution du surcharge
voit les deux versions, elle doit bien en choisir la première.
L'astuce ici, c'est que sans la présence d'une fonction
U::displayHTML (avec la bonne signature), la déduction du type
doit échouer, et que donc la fonction ne serait pas ajoutée à
l'ensemble de surcharge. (Et que cet échec n'est pas une erreur
en soi ; il signifie seulement qu'il n'y aura pas
d'instantiation du template dans l'ensemble de surcharge.) Et
qu'ensuite, évidemment, l'ensemble de surcharge n'est quand même
pas vide (parce qu'il y a toujours la deuxième version de la
fonction), et que la résolution du surcharge choisit parmi les
fonctions dans l'ensemble de surcharge.

Dommage, je trouve ta solution assez élégante...


Je l'ai posté simplement parce qu'elle correspond exactement à
ce que tu as démandé. Je crois toujours que la plupart du temps,
une erreur de compilation serait préférable quand on essaie de
faire quelque chose qui n'est pas supporté.

La partie de ma solution que j'estîme importante dans tous les
cas, c'est l'utilisation de la fonction template qui renvoie
l'objet, plutôt que d'invoquer le constructeur directement.
Parce dans l'appel de la fonction, il y a bien déduction du
type, tandis que l'« appel » du constructeur est en fait
considéré comme une conversion, et il faut que tu donnes le type
explicitement. L'utilisation de la fonction asHTML ne doit poser
de problèmes à aucun compilateur qui supporte des templates, et
pourrait servir quelque soit la définition de la classe.

--
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
Marc G
je suis d'accord en fait,
une erreur est préférable à la compilation
et sinon j'aurais tendance facilement à "assimiler" classe template et
polymorphisme
(au moins "inconsciemment"), ou à penser qu'un template peut être instancié
à l'exécution
et là au moins comme ça c'est clair !

Une dernière question
dans la définition de asHTML,

template<typename T>
inline HTMLFormat<T>
asHTML(T const& t)
{
return HTMLFormat<T>(t);
}

tu as mis inline HTMLFormat<T>
je connais pas cette syntaxe de inline ?
(j'utilise en fait inline uniquement pour définir une méthode en dehors du
corps de la déclaration de la classe)
Merci
Marc
Avatar
kanze
Marc G wrote:

quel compilo me conseilles-tu POUR WINDOWS ?


Visual Studio 2005 est assez bien, et gratuit. Si tu veux
vraiment pousser, je crois que g++ est encore un peu mieux, mais
la différence est bien plus faible à ce qu'il a été, et VC++ 8
(le compilateur dans Visual Studio 2005) fait bien l'affaire
pour tout ce que j'ai essayé.

(j'ai pas le choix, c'est le client qui commande)


Alors, tu es plus ou moins coincé.

J'ai aussi Visual Studio.net 2002 pro


C'est sûrement une amélioration par rapport au vieux Borland.
Mais Microsoft ont bien mis du temps à maîtriser les templates.

je sais que c'est une question souvent posée...mais j'ai du
mal à me décider de changer. L'IDE de BCB est conviviale et
intuitive et je trouve la VCL pratique et fournie pour
développer vite des applications "pas trop" volumineuses.
Mais là, je suis sur un plus gros projet et je veux en
profiter pour apprendre quelque chose (je ne suis pas
informaticien...) mais je suis aussi pressé par le temps et
j'ai un peu peur des compilos non documentés...


Là aussi : les compilateurs Microsoft ont toujours été
accompagné d'une documentation énorme. En ce qui concerne le
Visual Studio gratuit, je l'ai trouvé même utile. (Je ne suis
peut-être pas typique, mais j'ai beaucoup apprécié leur
documentation sur comment migrer de Unix et comment utiliser le
compilateur depuis la ligne de commande. Je peux dire au moins
que ce dernier est très bien 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
Marc G
Merci
oublie ma question sur inline que je viens de poster,
elle est stupide
je suis fatigué avant d'avoir commencé ma journée
:-(
1 2