Monday, 2 December 2013

Data Access Layer for reading and writing files in C++

Data Access Layer :

Data access layer (DAL) Provides simplified access to data stored in persisted storage of some kind. DAL may provide a reference to an object in terms of oops.This allows the client (or user) modules to be created with a higher level of abstraction. Achieving data abstraction for writing data to files and reading data from files is a big challenge ahead of us.

Let us see how we can provide abstraction for reading and writing files in c++. Best way of encapsulation of data is done through classes. 

DataWriter class: 

DataWriter class provides a way to write any type of data into a file.

1. Create DataWriter object.
2. Call WriteData method.
3. Close the writer.

source code :
class DataWriter
{
public:
DataWriter()
{
m_in = nullptr;
m_error = 0;
}

DataWriter(const wchar_t* fileName, const wchar_t* fileMode)
{
OpenWriter(fileName, fileMode);
}

~DataWriter()
{
CloseWriter();
}

inline void OpenWriter(const wchar_t* fileName, const wchar_t* fileMode)
{
if(!fileName)
throw std::exception("file name cant be empty");
if(!fileMode)
throw std::exception("file mode cant be empty");

m_error =  _wfopen_s(&m_in, fileName, fileMode);

if(m_error == EINVAL)
{
CloseWriter();
throw std::exception("error opening file");
}
}

inline void CloseWriter()
{
if(m_in)
{
m_error = _fclose_nolock(m_in);
m_in = nullptr;
}
}

template <typename T> inline void WriteData(T* ptr, long long lengthInBytes)
{
m_error =  _fwrite_nolock(ptr, lengthInBytes, 1, in);
if(m_error != lengthInBytes)
{
CloseWriter();
throw std::exception("error writing to file");
}
}

private:
FILE*  m_in;
errno_t m_error;

};

DataReader class: 

DataReader class provides a way to read any type of data from a file.

1. Create DataReader object.
2. Call ReadData method.
3. Close the reader.

source code :
class DataReader
{
public:
DataReader()
{
m_in = nullptr;
m_error = 0;
}

DataReader(const wchar_t* fileName, const wchar_t* fileMode)
{
OpenReader(fileName, fileMode);
}

~DataReader()
{
CloseReader();
}

inline void OpenReader(const wchar_t* fileName, const wchar_t* fileMode)
{
if(!fileName)
throw std::exception("file name cant be empty");
if(!fileMode)
throw std::exception("file mode cant be empty");

m_error =  _wfopen_s(&m_in, fileName, fileMode);

if(m_error == EINVAL)
{
CloseReader();
throw std::exception("error opening file");
}
}

inline void CloseReader()
{
if(m_in)
{
m_error = _fclose_nolock(m_in);
m_in = nullptr;
}
}

template <typename T> inline void ReadData(T* ptr, long long lengthInBytes)
{
m_error =  _fread_nolock(ptr, lengthInBytes, 1, in);
if(m_error != lengthInBytes)
{
CloseReader();
throw std::exception("error writing to file");
}
}

private:
FILE*  m_in;
errno_t m_error;

};


File Modes:


Characters
Equivalent oflag
a
_O_WRONLY | _O_APPEND (usually _O_WRONLY | _O_CREAT |_O_APPEND)
a+
_O_RDWR | _O_APPEND (usually _O_RDWR | _O_APPEND | _O_CREAT )
r
_O_RDONLY
r+
_O_RDWR
w
_O_WRONLY (usually _O_WRONLY |_O_CREAT | _O_TRUNC)
w+
_O_RDWR (usually _O_RDWR | _O_CREAT | _O_TRUNC)
b
_O_BINARY
t
_O_TEXT
S
_O_SEQUENTIAL
R
_O_RANDOM
T
_O_SHORTLIVED
D
_O_TEMPORARY
ccs=UNICODE
_O_WTEXT
ccs=UTF-8
_O_UTF8
ccs=UTF-16LE
_O_UTF16


How do and where do you use these classes?
1. Use these classes when there is need to write/read huge amounts of data from network.
2. Serialized objects can be presisted with these classes.
3. Your code contains huge collections that exceeds limit of a process boundary? above is the solution.
4. Your app contains frequent I/O operations, here is the solution.

 What gain you get ?
1. These classes are nearly 8 times faster than Microsoft' s BinaryReader and BinaryWriter.
2. Can write and read any format of data, unlike  Microsoft' s BinaryReader and BinaryWriter.
3. Support for UNICODE.
4. Smart class, developer need not worry about the memory release of file handle.All resources     used by the class are closed/deleted when class object goes out of scope.

1 comment: