al******@gmail.com wrote:
I have a class that I want to turn its contents into csv file. I want
to be able to set the value of the delimiter, the name of the file it
gets saved to, the path of that file, and maybe a few other things.
What would be a good design to accomplish these goals? Here are the
ideas that I have come up with:
Class X = class with data that I want to put into csv.
1. Nested Function Object
use a nested function object to do the conversion.
+ X does not get polluted with functions that really have no place.
e.g. setDelimeter()
+ behaves the way you would expect. e.g. X.toCsv() to convert X into a
csv file
- Setting state may not be clear to those not familar with function
objects e.g. X.toCsv.setDelimeter() Some may ask why a function is
being used like an object.
2. Create a class ConvertXToCsv that gets passed X to do the
conversion.
+ very straight forward and gets the job done.
- proliferation of classes.
Any suggestions?
-Thanks,
Jake
First item, if you have a class that needs to stream its members,
overload the global stream operator and make it a friend of the class.
That way you might one day come along and write a container class to
store all your X instances and you'll be able to stream the entire
container seamlessly using another overload of global op<<.
As far as conversion to csv and delimeters, all you are doing is
opening a std::ofstream to some specific file somewhere. Why not write
a container that will:
1. store your records into a well-known and common container (list,
vector, deque)
2. stream records around using std::ostreams for compatibility and
future expandeability
3. provide the container with a function to serialize the records to
file
I'ld suggest a new line at the end of each record to help later when
using getline(...) to read that file and load the container from file
records. A solution with exception handling would have been preferable
and error checking is kept to a minimum.
Since you have not shown your precious class, i've made one up called
X.
#include <iostream>
#include <ostream>
#include <string>
#include <vector>
#include <fstream>
#include <iterator// for std::ostream_iterator
class X
{
int n;
double d;
std::string s;
public:
X() : n(0), d(0.0), s() { }
X(int n_, double d_, std::string s_)
: n(n_), d(d_), s(s_) { }
/* member functions */
void serialize( std::ostream& ) const;
/* friends */
friend std::ostream& operator<<( std::ostream&, const X& );
};
void X::serialize(std::ostream& os) const
{
os << n << '$' << d << '$' << s << std::endl;
}
std::ostream& operator<<(std::ostream& os, const X& r_x)
{
os << "n = " << r_x.n;
os << "\nd = " << r_x.d;
os << "\ns = " << r_x.s << std::endl;
return os;
}
class XContainer
{
std::vector< X vx;
public:
XContainer() : vx() { }
void push_back( const X& r_x ) { vx.push_back( r_x ); }
size_t size() const { return vx.size(); }
/* member functions */
void serialize( std::ostream& ) const;
bool write( const std::string& ) const;
/* friends */
friend
std::ostream& operator<<( std::ostream&, const XContainer& );
};
void XContainer::serialize(std::ostream& os) const
{
typedef std::vector< X >::const_iterator VIter;
for (VIter xiter = vx.begin(); xiter != vx.end(); ++xiter)
{
(*xiter).serialize(os);
}
}
bool XContainer::write(const std::string& r_filename) const
{
std::ofstream ofs(r_filename.c_str());
if ( !ofs.is_open() )
{
return true;
} else {
serialize(ofs);
}
return false;
}
std::ostream&
operator<<(std::ostream& os, const XContainer& r_con)
{
std::copy( r_con.vx.begin(),
r_con.vx.end(),
std::ostream_iterator< X >(os) );
return os;
}
int main()
{
XContainer xcontainer;
xcontainer.push_back( X(0, 0.0, "string 0") );
xcontainer.push_back( X(1, 1.1, "string 1") );
xcontainer.push_back( X(2, 2.2, "string 2") );
std::cout << xcontainer;
// show what file contents will look like on console
std::cout << "\nnumber of records = " << xcontainer.size();
std::cout << std::endl;
xcontainer.serialize( std::cout );
// write the records to file - write protect file to test
const std::string sfilename( "data.cvs" );
if ( xcontainer.write( sfilename ) )
{
std::cout << "\nerror: failed to open " << sfilename;
std::cout << " !!!\n";
} else {
std::cout << "\nsuccess: records written to " << sfilename;
std::cout << std::endl;
}
return 0;
}
/*
n = 0
d = 0
s = string 0
n = 1
d = 1.1
s = string 1
n = 2
d = 2.2
s = string 2
number of records = 3
0$0$string 0
1$1.1$string 1
2$2.2$string 2
success: records written to data.cvs
*/