Bien que je puisse comprendre l'idée de surcharger l'opérateur de flux, je m'interrogerais sur la pratique du problème en question.
1. orienté objet approche
Si vous êtes prêt à écrire dans un fichier .csv
, chaque ligne devrait probablement avoir le format même que les autres? Malheureusement, votre opérateur de flux ne le vérifie pas.
Je pense que vous devez créer un objet Line
, qui sera streamable, et valider chaque champ avant de les écrire dans le fichier (et les écrire avec le format approprié). Bien que ce ne soit pas aussi à la mode, vous aurez beaucoup plus de chance d'obtenir une implémentation robuste ici.
Disons que (par exemple) que vous voulez à la sortie 2 entiers et une chaîne:
class Line
{
public:
Line(int foo, int bar, std::string firstName):
mFoo(foo), mBar(bar), mFirstName(firstName)
friend std::ostream& operator<<(std::ostream& out, const Line& line)
{
return out << line.mFoo << ',' << line.mBar << ','
<< line.mFirstName << std::endl;
}
private:
int mFoo;
int mBar;
std::string mFirstName;
};
et son utilisation reste très simple:
std::cout << Line(1,3,"Tom") << Line(2,4,"John") << Line(3,5,"Edward");
2. Vous voulez vous amuser?
Maintenant, cela peut sembler terne, et vous pourriez vouloir jouer et encore ont encore un certain contrôle sur ce qui est écrit ... eh bien, permettez-moi de vous présenter la programmation méta modèle dans la mêlée;)
Voici l'utilisation prévue:
// Yeah, I could wrap this mpl_::vector bit... but it takes some work!
typedef CsvWriter< mpl_::vector<int,int,std::string> > csv_type;
csv_type(std::cout) << 1 << 3 << "Tom" << 2 << 4 << "John" << 3 << 5 << "Edward";
csv_type(std::cout) << 1 << 2 << 3; // Compile Time Error:
// 3 is not convertible to std::string
Maintenant, ce serait intéressant non?Cela formaterait la ligne et assurerait une mesure de validation ... On pourrait toujours compliquer la conception pour qu'elle fasse plus (comme enregistrer des validateurs pour chaque champ, ou pour toute la ligne, etc ...) mais c'est déjà assez compliqué.
// namespace mpl_ = boost::mpl
/// Sequence: MPL sequence
/// pos: mpl_::size_t<N>, position in the Sequence
namespace result_of {
template <class Sequence, class pos> struct operator_in;
}
template < class Sequence, class pos = mpl_::size_t<0> >
class CsvWriter
{
public:
typedef typename mpl_::at<Sequence,pos>::type current_type;
typedef typename boost::call_traits<current_type>::param_type param_type;
CsvWriter(std::ostream& out): mOut(out) {}
typename result_of::operator_in<Sequence,pos>::type
operator<<(param_type item)
{
typedef typename result_of::operator_in<Sequence,pos>::type result_type;
if (pos::value != 0) mOut << ',';
mOut << item;
if (result_type::is_last_type::value) mOut << std::endl;
return result_type(mOut);
}
private:
std::ostream& mOut;
}; // class CsvWriter
/// Lil' bit of black magic
namespace result_of { // thanks Boost for the tip ;)
template <class Sequence, class pos>
struct operator_in
{
typedef typename boost::same_type<
typename mpl_::size<Sequence>::type,
typename mpl_::next<pos>::type
> is_last_type;
typedef typename mpl_::if_<
is_last_type,
CsvWriter< Sequence, mpl_::size_t<0> >,
CsvWriter< Sequence, typename mpl_::next<pos>::type >
>::type;
}; // struct operator_in<Sequence,pos>
} // namespace result_of
Ici vous avez un écrivain de flux qui assure que le fichier cvs est correctement formaté ... mettant à nu les caractères de nouvelles lignes dans les cordes;)
Oui, j'ai d'abord pensé à cette solution mais elle ne fonctionnerait pas avec les manipulateurs (les autres problèmes avec des virgules partout et citant la cellule entière si elle contient un délimiteur pourrait être facilement résolu dans la fonction opérateur surchargée). Maintenant, quand j'y repense, je suppose que je pourrais remplacer << pour les manipulateurs aussi. Je me demande simplement si c'est la bonne façon de résoudre le problème :) – Tom
J'ai fait un peu plus de travail là-dessus. C'est maintenant au point que c'est probablement au moins raisonnablement utilisable. Il ne traitera pas les manipulateurs qui prennent des paramètres, mais cela devrait juste être une question d'ajout (encore) d'une surcharge. Ne devrait pas être trop horrible, mais une douleur de toute façon. –
Ceci est une bonne solution. Supposons qu'il existe une alternative à la définition d'une classe wrapper (par exemple, héritage de 'std :: ostream' et de quelque chose de surchargé, ou d'un manipulateur magique' csv_mode'). Si vous vouliez insérer des types définis par l'utilisateur, vous seriez en difficulté. Les méthodes personnalisées 'operator <<' sont généralement implémentées à l'aide des méthodes intégrées 'operator <<', ce qui se traduit par des virgules supplémentaires. – Dan