Persistant Properties With C++ - the Readable Way
Environment: VC6
Introduction
During my many years of C++ programming I ran into so many cases where there was a collection of persistant properties to be maintained by means of system registry, INI files, database tables etc. etc., and as a fanatic for clear and readable code I always hated to see how my code get cluttered each time I wanted to set a new property or query for a previously saved one.
When STL first came out it was fascinating to see how easy it was to set a new value in a map container and query for it later on, just by using the [] operator. I mean, if I could only do things like:
CDatabaseTable dbt("my_table");
dbt["Name"] = "Joe Shmoe";
cout << dbt["Name"] << endl;
...or...
CRegistry r(HKEY_CURRENT_USER, "Software\\MyApp\\Settings"); r["window_x"] = 112; r["window_y"] = 200;
After all, if you leave alone the read and write that fit each storage nature, this is all very much the same thing, wouldn't you agree ?
To make the long story short, I needed the means to work with any dictionary-like storage by using the [] operator for both read and write. Nothing too fancy, I agree, but certainly a life saver for a readable-code freak like myself!
So Here It Is...
template <class Key, class Value>
class CPropertiesStorage
{
public :
class CPropertiesStoragePair
{
public :
Key first;
Value second;
CPropertiesStoragePair(CPropertiesStorage& stg)
: m_storage(stg) {}
CPropertiesStoragePair(CPropertiesStorage& stg,
const Key& k)
: m_storage(stg), first(k) {}
CPropertiesStoragePair(CPropertiesStorage& stg,
const Key& k,
const Value& v)
: m_storage(stg), first(k), second(v) {}
CPropertiesStoragePair(const CPropertiesStoragePair& sp)
: m_storage(sp.m_storage), first(sp.first),
second(sp.second) {}
CPropertiesStoragePair& operator = (const Value& v)
{
second = v;
m_storage.Write(first, second);
return *this;
}
CPropertiesStoragePair& operator =
(const CPropertiesStoragePair& sp) const
{
if( &sp != this )
{
m_storage = sp.m_storage;
first = sp.first;
second = sp.second;
}
return *this;
}
operator const Value&() const
{
return (const Value&)second;
}
private :
CPropertiesStorage& m_storage;
};
friend class CPropertiesStoragePair;
CPropertiesStorage() {}
virtual ~CPropertiesStorage() {}
CPropertiesStoragePair operator[] (const Key& k)
{
CPropertiesStoragePair sp(*this, k);
Read(sp.first, sp.second);
return sp;
}
protected :
virtual void Read(const Key& k, Value& v) = 0;
virtual void Write(const Key& k, const Value& v) = 0;
};
There are two template parameters, the key type and the value type. This is just like what you should already know from your STL experience. The only thing that is left is two pure-virual methods where you can implement the actual read and write mechanism according to the storage you're using. I've also decided not to return any result form those two functions because at that stage I can't be totally sure of how you might implement those two methods. I figured that the actual implementation would throw some kind of exception it something bad happens.
Sample
Here is a sample code that uses an INI file as the storage media:
class CIniFileSection
: public CPropertiesStorage<std::string, std::string>
{
public :
CIniFileSection(const char* lpszIniFileName,
const char* lpszSection)
: m_sIniFileName(lpszIniFileName),
m_sSection(lpszSection)
{
}
protected :
const std::string m_sIniFileName;
const std::string m_sSection;
virtual void Read(const std::string& k, std::string& v)
{
char szBuf[200];
GetPrivateProfileString(m_sSection.c_str(),
k.c_str(),
"",
szBuf, sizeof(szBuf),
m_sIniFileName.c_str());
v = szBuf;
}
virtual void Write(const std::string& k,
const std::string& v)
{
WritePrivateProfileString(m_sSection.c_str(),
k.c_str(),
v.c_str(),
m_sIniFileName.c_str());
}
};
int main()
{
CIniFileSection r("d:\\test.ini", "main");
r["key1"] = "value1";
std::cout << r["key1"].second << std::endl;
return 0;
}
Finally...
What I'm presenting here is an idea. If any of you have a better way to do what I have tried to do, please let me know.

Comments
Great, but...
Posted by Legacy on 09/09/2003 12:00amOriginally posted by: Patr�cia Nunes
It doesn't work for Windows CE environment...
Some suggestion?
TIA
ReplyCould be more generic
Posted by Legacy on 05/28/2002 12:00amOriginally posted by: Shannon G Barber
Since you're making a template class, you could (dare I say should!) use a policy template parameter to provide read & write functors. This technique is used extensively in the Boost library and in Loki (i.e. it's an established and recongized method)
It's the generic approach rather than the OO approach you have constructed. Does the storage media need to change during run-time? Odds are good you'll always want the same storage for a given operation, so you can skip the OO overhead and use functors.
Also, the read/write concepts ought to return a value (bool is a good choice - true success, false failure), then you do not /require/ an exception to be thrown to indicate failure, unlike the example implementation.
Actually, you could even leave the return value nebulous and not specific it for the read/write concept, and then each policy could return an appropriate type!
ReplyUseful and to the point
Posted by Legacy on 12/01/2001 12:00amOriginally posted by: Giora Katz-Lichtenstein
This is the right way a code section should be provided. It adds value, it is clear and it does the job.
Giora
Replystl include?
Posted by Legacy on 08/31/2001 12:00amOriginally posted by: Edgar Tu
Hi,
I've never done STL programming before, but your class facinates me. I'm using VC++ 6 and trying to compile your sample. I'm getting errors on "string is not a member of std". Can someone help me out?
thx,
Replyedgar
And what about default values?
Posted by Legacy on 08/16/2001 12:00amOriginally posted by: Dmitriy Sholomov
ReplyMinor detail
Posted by Legacy on 08/14/2001 12:00amOriginally posted by: Martijn Dashorst
First I would like to say that this is a great class... I already used it! There is however a small detail which should be changed:
The operator CPropertiesStorage::CPropertiesStoragePair::operator=(const CPropertiesStoragePair&) const shouldn't be const:
CPropertiesStorage::CPropertiesStoragePair::operator=(const CPropertiesStoragePair&)
is the correct definition.
ReplyBrilliant Idea
Posted by Legacy on 08/10/2001 12:00amOriginally posted by: Robert Liu
A Great Idea! Could be roll out as a util Lib
Reply