Listing D
// properties.h
#if !defined(basic_properties___H)
#define basic_properties___H
 
 
#include <map>
#include "properties_base.h"
#include <fstream>
#include <stdexcept>
#include <sstream>
 
 
namespace Private
{
    // reads a string from stream:
    // the string starts with double-quote (") and ends with double-quote
    //
    // escapes: double-quote is escaped like this (\")
    //          backslash is escaped like this (\\)
    template< class char_type, class char_traits>
        void read_str_from_stream( std::basic_istream< char_type, char_traits> & in, std::basic_string< char_type, char_traits> & strDest)
    {
        // ignore spaces
        char_type ch = 0;
        while ( in.get( ch))
            if ( !isspace( ch))
            {
                in.putback( ch);
                break;
            }
 
 
        in >> ch;
        if ( ch != '"')
        {
            // error: string did not start with '"'
            in.setstate( std::ios_base::failbit);
            return;
        }
 
 
        // clear old string, and insert chars into it
        strDest.erase();
        while ( in.get( ch))
        {
            if ( ch == '"')
               // end of string found
                return;
            else if ( ch == '\\')
            {
                // see if it's an escaped quote
                char_type chNext = 0;
                in.get( chNext);
                if ( ( chNext != '"') && ( chNext != '\\') )
                    strDest += ch;
                strDest += chNext;
            }
            else
                strDest += ch;
        }
 
 
        // did not encounter ending '"'
        in.setstate( std::ios_base::failbit);
    }
};
 
 
 
 
// default error policy for properties
// (throw if a line is bad)
template< class char_type>
struct throw_on_bad_line
{
    typedef std::basic_string< char_type> string_type;
    void on_bad_line( const string_type & strFileName, const string_type & strLine)
    {
        throw std::runtime_error( "invalid props file: " + strFileName);
    }
};
 
 
// error policy for properties
// (log error somewhere
template< class char_type>
struct logerror_on_bad_line
{
    typedef std::basic_string< char_type> string_type;
    logerror_on_bad_line() : m_pLog( NULL) {}
 
 
    void set_log( std::basic_ostream< char_type> & log)
    { m_pLog = &log; }
 
 
    void on_bad_line( const string_type & strFileName, const string_type & strLine)
   {
        if ( m_pLog)
            *m_pLog << "invalid props file: " << strFileName
                << "(line=" << strLine << ")";
    }
private:
    std::basic_ostream< char_type> *m_pLog;
};
 
 
 
 
 
 
/*
    contains application properties:
    that are constant throughout the application
*/
template<
        class char_type,
        class error_policy = throw_on_bad_line< char_type> >
class basic_properties
    : public basic_properties_base< char_type>,
      protected error_policy
{
    typedef std::basic_string< char_type> string_type;
 
 
public:
    basic_properties( const std::string & strFileName)
        : m_strFileName( strFileName)
    {
        std::basic_ifstream< char_type> in( strFileName.c_str());
 
 
        while ( in.good())
        {
            string_type strLine;
            std::getline( in, strLine);
            // ... ignore empty lines
            if ( strLine.empty())
                continue;
 
 
            std::basic_stringstream< char> line( strLine);
            char_type ch = 0;
            line.get( ch);
            if ( ch == '#')
                // comment
                continue;
            line.putback( ch);
 
 
            string_type strName;
            string_type strValue;
            Private::read_str_from_stream( line, strName);
            Private::read_str_from_stream( line, strValue);
 
 
            if ( !line.good())
                on_bad_line( strFileName, strLine);
 
 
            m_collProps[ strName] = strValue;
        }
    }
    ~basic_properties() {}
 
 
   void internal_get( const string_type & strName, string_type & val) const
    {
        std::map< std::string, std::string>::const_iterator
            found = m_collProps.find( strName);
        if ( found != m_collProps.end())
            val = found->second;
        else
            throw std::runtime_error( "property not found " + strName);
    }
 
 
private:
    // the file to read the properties from
    std::string m_strFileName;
 
 
    // property name (key) -> property value (value)
    std::map< string_type, string_type> m_collProps;
};
 
 
typedef basic_properties< char> properties;
typedef basic_properties< wchar_t> wproperties;
 
 
 
 
#endif