C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

design question (converting class data to csv)

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language (comp.lang.c++)
View previous topic :: View next topic  
Author Message
Guest






PostPosted: Mon Oct 16, 2006 7:15 am    Post subject: design question (converting class data to csv) Reply with quote



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
Back to top
Heinz Ozwirk
Guest





PostPosted: Mon Oct 16, 2006 9:10 am    Post subject: Re: design question (converting class data to csv) Reply with quote



<alacrite (AT) gmail (DOT) com> schrieb im Newsbeitrag
news:1160964911.209165.303120 (AT) k70g2000cwa (DOT) googlegroups.com...
Quote:
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.

Writing data to a file (or whatever else) can be split into two tasks -
providing the data to be written and formating (and writing) data
"somewhere".

The provider of your data usually doesn't care (or shouldn't) where its data
will be written to. To write data to a CSV file or some other record
oriented medium, a set of functions like OpenRecord, CloseRecord, WriteInt,
WriteString etc. are all your data source needs to write its data. Such
functions can easyly be defined by an (abstract) interface class.

The consumer of such data usually shouldn't know where its input comes from.
But it might need additional information like delimiters or filenames, which
cannot be supplied by the data source itself. Also, such additional
information depends on the type of medium where the (formated) data should
be written to. That information must be supplied by that part of a program,
that actually wants data to be written to a well-known medium.

Your second idea is a step into that direction, but the fixed connection of
a given class X and a single medium (CSV files) makes it difficult to write
data from another class, or to another medium. You would end with x*y
ConvertXToY classes, where x is the number of data classes to write and y
the number of supported media. With an abstract writer class used by all
data classes and implementations for different media, otoh, you only need y
classes and one additional function in each data class.

The C++ stream library is a small step in this direction, but it only
abstracts the representation of data streams (as a file, a string or a
console device).

Consider this small (incomplete and buggy) example:

class Writer
{
public:
virtual void OpenRecord() = 0;
virtual void CloseRecord() = 0;
virtual void Write(int) = 0;
virtual void Write(std::string const&) = 0;
};

class CsvWriter: public Writer
{
// Implementation of Writer class
void OpenRecord()
{
first = true;
}
void CloseRecord()
{
file << std::endl;
}
void Write(int x)
{
NextField();
file << x;
}
void Write(std::string const& x)
{
NextField();
file << '"' << x << '"';
}
public:
CsvWriter(char const* fn, char d)
: file(fn)
, delimiter(d)
{}
private:
void NextFiled()
{
if (!first) file << delimiter;
first = false;
}
private:
std::ofstream file;
char delimiter;
bool first;
};
class MyData
{
public:
...
void Write(Writer& writer)
{
writer.OpenRecord();
writer.Write(name);
writer.Write(age);
writer.CloseRecord();
}
private:
std::string name;
int age;
};
int main()
{
MyData x;
MyData y;
...
CsvWriter datafile("datafile.csv", ';');
x.Write(datafile);
y.Write(datafile);
}

Regards
Heinz
Back to top
Salt_Peter
Guest





PostPosted: Mon Oct 16, 2006 9:10 am    Post subject: Re: design question (converting class data to csv) Reply with quote



alacrite (AT) gmail (DOT) com wrote:
Quote:
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
*/
Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ language (comp.lang.c++) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.