Mentre riesco ad apprezzare l'idea di sovraccaricare l'operatore del flusso, metterei in dubbio la pratica per il problema in questione.
1. Object-Oriented approccio
Se siete disposti a scrivere in un file .csv
, quindi ogni linea dovrebbe probabilmente avere lo stesso formato di rispetto agli altri? Sfortunatamente il tuo operatore di flusso non lo controlla.
Penso che sia necessario creare un oggetto Line
, che sarà filtrabile e convaliderà ogni campo prima di scriverli sul file (e scriverli con il formato corretto). Sebbene non siano di moda, avrai molte più possibilità di raggiungere un'implementazione solida qui.
Diciamo che (per esempio) che si desidera uscita 2 interi e una stringa:
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;
};
E l'utilizzo rimane molto semplice:
std::cout << Line(1,3,"Tom") << Line(2,4,"John") << Line(3,5,"Edward");
2. wanna have fun?
Ora, questo può sembrare noioso, e si potrebbe desiderare di giocare, eppure hanno ancora un certo controllo su ciò che è scritto ... beh, mi permetta di introdurre programmazione meta modello nella mischia;)
Ecco l'uso previsto:
// 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
Ora sarebbe interessante?Formattare la linea e garantire una misura di convalida ... Uno potrebbe sempre complicare la progettazione in modo che faccia di più (come la registrazione dei validatori per ogni campo, o per l'intera linea, ecc ...) ma è già abbastanza complicato.
// 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
Ecco una scrittrice flusso che assicura che il file cvs è formattata correttamente ... scoprendo di ritorno a capo caratteri nelle stringhe;)
Sì, in effetti avevo già pensato a questa soluzione ma non avrebbe funzionato con i manipolatori (gli altri problemi con virgole ovunque e citando un'intera cella se conteneva delimitatore potevano essere facilmente risolti all'interno della funzione dell'operatore sovraccarico). Ora, ripensandoci, suppongo di poter scavalcare anche << per i manipolatori. Mi sto solo chiedendo se questo è il modo giusto per risolvere il problema :) – Tom
Ho fatto un po 'più di lavoro su di esso. È ora al punto che probabilmente è almeno ragionevolmente utilizzabile. Non gestirà manipolatori che prendono parametri, ma ciò dovrebbe semplicemente essere una questione di aggiungere (ancora) un altro sovraccarico. Non dovrebbe essere troppo orribile, ma un dolore comunque. –
Questa è una buona soluzione. Supponiamo che ci fosse qualche alternativa alla definizione di una classe wrapper (ad esempio, ereditando da 'std :: ostream' e sovrascrivendo qualcosa, o un magico manipolatore' csv_mode'). Se si desidera inserire tipi definiti dall'utente, si sarebbe nei guai. I metodi personalizzati dell'operatore << 'sono di solito implementati usando i metodi built-in' operator << ', così si finirebbe con delle virgole extra. – Dan