Is there a better way to access the data of a template class?
Hi!
I try to make a tiny wrapper for the sqlite3 database.
My idea for declaring a record
class TRecord
{
public:
DBColumn<Int> mKey;
DBColumn<std::wstring> mText;
DBColumn<CTimeDate> mDate;
};
the DBColumn template declaration would then look like this:
template<class T>
class DBColumn
{
public:
DBColumn ();
~DBColumn () { }
operator T () const { return mData; }
T operator= (const T&);
T operator+ (const T&) const;
T operator+= (const T&);
bool operator== (const T&) const;
bool operator!= (const T&) const;
bool operator< (const T&) const;
bool operator> (const T&) const;
bool operator<= (const T&) const;
bool operator>= (const T&) const;
private:
T mData;
};
When I started implementing this idea I had some problems to use, and
to access the data of the template. The only (some what clumsy)
solution I found looks like this:
I declared an abstracte class for a record column:
class VColumn
{
public:
typedef enum EColType { eCol_DATE,
eCol_INT,
eCol_DOUBLE,
eCol_TEXT
};
VColumn (const std::wstring& colName,
EColType type,
bool primaryKey);
virtual ~VColumn () = 0;
std::wstring ColName () const { return mName; }
EColType ColType () const { return mType; }
bool PrimaryKey () const { return mPrimaryKey; }
void* DataPtr () const { return mDataPtr; }
protected:
std::wstring mName;
EColType mType;
bool mPrimaryKey;
void* mDataPtr;
};
the record is now a vector of VColum
typedef std::vector<VColumn*> DBRecord;
and the template for the real columns looks now like this:
template<class T>
class DBColumn : public VColumn
{
public:
DBColumn (const std::wstring& colName,
EColType type,
bool primaryKey);
~DBColumn () { }
operator T () const { return mData; }
T operator= (const T&);
T operator+ (const T&) const;
T operator+= (const T&);
bool operator== (const T&) const;
bool operator!= (const T&) const;
bool operator< (const T&) const;
bool operator> (const T&) const;
bool operator<= (const T&) const;
bool operator>= (const T&) const;
private:
T mData;
};
In the constructor of the DBColum template the VColumn member mDataPtr
is set with the address of the mData template member.
template<class T>
DBColumn<T>::DBColumn (const std::wstring& name,
EColType type,
bool primaryKey)
: VColumn (name, type, primaryKey)
{
mDataPtr = static_cast<void*>(&mData);
}
to complete, I need also an abstracte class for the record
class VRecord
{
public:
VRecord () {}
virtual ~VRecord () = 0;
void AddCol (VColumn* col);
DBRecord Record () const { return mRecord; }
private:
DBRecord mRecord;
};
void VRecord::AddCol (VColumn* col)
{
mRecord.push_back (col);
}
Within the application I have to define the record like this:
class TRecord : public VRecord
{
public:
TRecord ();
~TRecord () {}
DBColumn<Int> mKey;
DBColumn<std::wstring> mText;
DBColumn<CTimeDate> mDate;
};
and to declare it like that:
TRecord::TRecord ()
: mKey (_T("KEY"), VColumn::eCol_INT, true),
mText (_T("TEXT"), VColumn::eCol_TEXT, false),
mDate (_T("DATE"), VColumn::eCol_DATE, false)
{
AddCol (&mKey);
AddCol (&mText);
AddCol (&mDate);
}
To make not a longer post as really needed, here only the Database
AddRecord function:
class TSimpleDataBase
{
public:
void AddRecord (const VRecord& rec);
};
using namespace std;
void TSimpleDataBase::AddRecord (const VRecord& rec)
{
LPCTSTR ctx = _T("TSimpleDB::AddRecord");
if (mDB == NULL)
throw TException (_T("DB not open!"), ctx);
TFormat sci;
sci.Scientific ();
sci.Precision (16);
wstring s = _T("INSERT INTO ") + mTableName + _T(" VALUES (");
DBRecord dbr = rec.Record ();
for (UInt i = 0; i < dbr .size (); ++i)
{
Tstringstream ss; // typedef basic_stringstream<TCHAR>
VDBColumn* col = rec[i];
switch (col->FieldType ())
{
case eDBField_DATE:
{
TTimeDate val = *static_cast<TTimeDate*>(col->DataPtr ());
ss << val.GetTime ();
}
break;
case eDBField_INT:
{
Int val = *static_cast<Int*>(col->DataPtr ());
ss << val;
}
break;
case eDBField_DOUBLE:
{
Double val = *static_cast<Double*>(col->DataPtr ());
ss << sci(val);
}
break;
case eDBField_TEXT:
{
Tstring* p = static_cast<Tstring*> (col->DataPtr ());
Tstring val = *p;
ss << _T("'") << val << _T("'");
}
break;
default:
throw TException (_T("create table, illegal column type!"),
ctx);
}
s += ss.str ();
if (i < mRecordDef.size () - 1)
s += _T(", ");
}
s += _T(")");
sqlite3_stmt* stmt;
int rc = sqlite3_prepare16_v2 (mDB, s.c_str (), -1, &stmt, NULL);
if (rc != SQLITE_OK)
throw TException (_T("prepare add record failed!"), ctx);
sqlite3_step (stmt);
if (sqlite3_finalize (stmt) != SQLITE_OK)
throw TException (_T("add record failed!"), ctx);
}
here you see the access to the template value via the VColumn DataPtr
function as example for a double column.
Double val = *static_cast<Double*>(col->DataPtr ());
Looks clumsy and in my eyes a little bit dangerous ;-)
What I like is how easy I can use the Database within an application.
What I dont like is the complicatet implementation to reach that goal.
Maybe there is no other way to do it, then i can live with it because
it works.
All comments are welcome, mostly preferred good ideas ;-)
Kind regards
Walter