Persistant Properties With C++ - the Readable Way

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

.

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:00am

    Originally posted by: Patr�cia Nunes

    It doesn't work for Windows CE environment...

    Some suggestion?

    TIA

    Reply
  • Could be more generic

    Posted by Legacy on 05/28/2002 12:00am

    Originally 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!

    Reply
  • Useful and to the point

    Posted by Legacy on 12/01/2001 12:00am

    Originally 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

    Reply
  • stl include?

    Posted by Legacy on 08/31/2001 12:00am

    Originally 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,
    edgar

    Reply
  • And what about default values?

    Posted by Legacy on 08/16/2001 12:00am

    Originally posted by: Dmitriy Sholomov

    Hi!
    
    

    Your idea is right, but as for me, there are two lacks in the implementation.

    1) GetPrivateProfileString function has lpDefault parameter. In the implementation of your classes you don't use it. In the situation when INI file contains SomeKey= (the value="") you'll get the same value as if there is no record in the INI file. I suggest to add
    const Value CPropertiesStorage::operator() (const Key& k, const Value& v_def) to the CPropertiesStorage class.

    2) When you use ini_file_section["key"]=val; construction to write data you also read data from INI file in the operator []. Is it necessary?

    Bye!

    Reply
  • Minor detail

    Posted by Legacy on 08/14/2001 12:00am

    Originally 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.

    Reply
  • Brilliant Idea

    Posted by Legacy on 08/10/2001 12:00am

    Originally posted by: Robert Liu

    A Great Idea! Could be roll out as a util Lib

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date