Listing 2

  class IDbEnvConfigPolicy
  {
  protected:
    // Constructor is protected because this is a base-class for a singleton.
    // Constructor takes the most important config data as arguments.  
    // All others parameters are changed via optional 'set' methods.
    IDbEnvConfigPolicy( const std::string & sHomeDirectoryName, size_t 
      uSizeOfMemoryCache );
  public:
    virtual               ~IDbEnvConfigPolicy();
    // CDatabaseEnvironment calls these query methods at appropriate time.
    inline std::string    GetHomeDirectoryName() const;
    inline size_t         GetSizeOfMemoryCache() const;
    // Derived classes can override all these to change defaults.
    virtual std::string   GetDatabaseDirectoryName() const;
    virtual std::string   GetTransactionLogsDirectoryName() const;
    virtual std::string   GetBackingFilesDirectoryName() const;
    virtual u_int32_t     GetDbEnvConstructionFlags() const;

    // The base-class version of this method applies all of the parameters
    // captured here.
    // Derived classes may override if necessary; also call base-class version
    // if desired.
    virtual void          ConfigureDbEnvBeforeOpen( DbEnv * pDbEnv );

    virtual u_int32_t     GetDbEnvOpenFlags() const;
    virtual uint          GetMaxNumLockObjects() const;
    virtual uint          GetMaxNumLocks() const;
    virtual size_t        GetMaxSizeOfTransactionLogFile() const;
    virtual u_int32_t     GetAutoDeadlockResolvePolicy() const;
    virtual void          SetLogOutput( ILogFunctor * pLogFunctor );
    // ... add more config parameter accessors as neeeded

    // The database wrapper uses this to log errors during operation.
    virtual void          Log( const std::string & sLog );
  private:
    std::string           m_sHomeDirectoryName;
    size_t                m_uSizeOfMemoryCache;
    ILogFunctor *         m_pLogFunctor;
    // Disable copying and assignment
                         IDbEnvConfigPolicy( const IDbEnvConfigPolicy  & );
    IDbEnvConfigPolicy & operator=( const IDbEnvConfigPolicy  & );
  };
  typedef CThreadSafeCountedPtr< IDbEnvConfigPolicy >  tsrcpDbEnvConfigPolicy_t;

  // Base class for type-safe DbEnv wrapper singletons (declared immediately 
  // after). This enforces calling DbEnv::close() when the database is 
  // destroyed.
  class IDatabaseEnvironment
  {
  public:
    virtual ~IDatabaseEnvironment();
    inline tsrcpDbEnvConfigPolicy_t GetConfigPolicy() const;

    // Get a new Db handle in this environment.
    inline std::auto_ptr< Db >      NewDb();
  protected:
    // Constructor is protected as this is a base class for a singleton.
    explicit IDatabaseEnvironment( tsrcpDbEnvConfigPolicy_t 
      tsrcpDbEnvConfigPolicy );

    // Keep a reference open to the config-policy singleton (we need it to 
    // handle logging)
    tsrcpDbEnvConfigPolicy_t        m_tsrcpDbEnvConfigPolicy;

    std::auto_ptr< CWin32Mutex >    m_apRunRecoveryProcessMutex;

    // Hold DbEnv object by a ptr so wrapper ctor can convert any DbException 
    // that the DbEnv ctor throws (on compilers without support for 
    // function-try blocks)
    std::auto_ptr< DbEnv >          m_apDbEnv;
  private:
    // Disable copying and assignment
                            IDatabaseEnvironment( const IDatabaseEnvironment & );
    IDatabaseEnvironment & operator=( const IDatabaseEnvironment & );
  };

  typedef  CThreadSafeCountedPtr< IDatabaseEnvironment >  tsrcpDbEnvBase_t;

  // Berkeley DbEnv wrapper (singleton).
  template< class TConfigPolicy >
  class CDatabaseEnvironment : public IDatabaseEnvironment
  {
  public:

    // The individual databases may use the DbEnv::ConfigPolicy::Log() method.
    typedef TConfigPolicy  ConfigPolicy_t;

    // Thread-safe reference counted ptr.
    typedef CThreadSafeCountedPtr< CDatabaseEnvironment<TConfigPolicy> > 
      tsrcpDbEnv_t;

    // Singleton accessor.
    // This takes a command; whether to return a new reference (as expected) 
    // or actually relinquish the one we have. This is to allow the 
    // application to release all references (and so run the destructor) 
    // without closing down completely.
    enum EInstanceCommand{ eReturnNewRef, eRelinquishInstance };
    static tsrcpDbEnv_t  Instance( EInstanceCommand eInstanceCommand = 
      eReturnNewRef );
  private:
    // Only access is via Instance()
    CDatabaseEnvironment();
    // Disable copying and assignment
                           CDatabaseEnvironment( const CDatabaseEnvironment & );
    CDatabaseEnvironment & operator=( const CDatabaseEnvironment & );
  };

  // Berkeley DB wrapper (singleton).
  // This enforces calling Db::close() when the database is destroyed.
  template< class TSerializableKey, class TSerializableRecord, class 
    TConfigPolicy >
  class CDatabase
  {
  public:
    typedef TConfigPolicy            ConfigPolicy_t;
    typedef TSerializableKey         SerializableKey_t;
    typedef TSerializableRecord      SerializableRecord_t;

    // The TConfigPolicy class specifies the type of the DbEnv we must use.
    typedef TConfigPolicy::DbEnv_t   DbEnv_t;
    typedef CTransaction< DbEnv_t >  Transaction_t;
    typedef DbEnv_t::tsrcpDbEnv_t    tsrcpDbEnv_t;
    typedef CThreadSafeCountedPtr<  CDatabase<TSerializableKey,
           TSerializableRecord,TConfigPolicy> > tsrcpDb_t;
    // Singleton accessor.
    enum EInstanceCommand{ eReturnNewRef, eRelinquishInstance };
    static tsrcpDb_t Instance( EInstanceCommand eInstanceCommand = 
      eReturnNewRef );

    ~CDatabase();
    // Access functions
    enum EOverwriteMode{ eAllowOverwrites = 0, eDisallowOverwrites = 
      DB_NOOVERWRITE };
    enum EGetLockMode{ eReadOnly = 0, eReadModifyWrite = DB_RMW ;
  
    void SerializeAndStoreRecord
           ( 
           const TSerializableKey &     Key,
           const TSerializableRecord &  Record,
           EOverwriteMode               eOverwriteMode,
           Transaction_t *              pOptionalTransaction = 0
           );
    TSerializableRecord    GetRecordBelongingToKey
                             ( 
                             const TSerializableKey &  Key,
                             Transaction_t *           pOptionalTransaction = 0,
                             EGetLockMode              eGetLockMode = eReadOnly
                             );
    void DeleteRecordBelongingToKey
           ( 
           const TSerializableKey &  Key,
           Transaction_t *           pOptionalTransaction = 0
           );
private:
    // Disallow public construction; access is via Instance() method (singleton).
    CDatabase();

    // We store a ref-counted ptr to the DB environment to hold it open while 
    // DB is open.
    tsrcpDbEnv_t         m_tsrcpDbEnv;

    // This object owns the Berkeley API 'Db' object returned by 
    // DbEnvWrapper->NewDb().
    std::auto_ptr< Db >  m_apDb;

    // Disable copying and assignment.
                CDatabase( const CDatabase & );
    CDatabase & operator=( const CDatabase & );


  };
  // Cursor wrapper
  template< class TDatabase >
  class CCursor
  {
  public:
    typedef TDatabase::tsrcpDb_t            tsrcpDb_t;
    typedef TDatabase::Transaction_t        Transaction_t;
    typedef TDatabase::SerializableKey_t    SerializableKey_t;
    typedef TDatabase::SerializableRecord_t SerializableRecord_t;
    // When constructed, the cursor has no defined position in the DB. 
    // Its position is determined by the access functions below.
    inline explicit CCursor( Transaction_t * pOptionalTransaction = 0 );
    inline void Close();
    inline ~CCursor();
    // Copy constructor uses dup() to get a duplicate cursor at the same 
    // position, sharing the same locks.
    inline CCursor( const CCursor< TDatabase > & other );

    // Database access methods (similar to Berkeley DBWrapper functions with 
    // same names). get/put via cursor have a lot of modes for managing 
    // multiple records with the same key. We have no need to support that yet.
    void SerializeAndOverwriteCurrentRecord( const SerializableRecord_t & Record );

    enum EGetLockMode{ eReadOnly = 0, eReadModifyWrite = DB_RMW };
    SerializableRecord_t  MoveToKeyAndGetRecord
                            ( 
                            const SerializableKey_t &  Key,
                            EGetLockMode               eGetLockMode = eReadOnly
                            );
    enum ESetCursorPosition { eFirstRecord, eLastRecord, eCurrentRecord, 
                                                 eNextRecord, ePreviousRecord };
    SerializableRecord_t  MoveToPositionAndGetKeyAndRecord
                            ( 
                            ESetCursorPosition   eSetCursorPosition,
                            SerializableKey_t *  pOptionalReceiveCurrentKey = 0,
                            EGetLockMode         eGetLockMode = eReadOnly
                            );
    void DeleteCurrentRecord();
  private:
    tsrcpDb_t        m_tsrcpDb;
    Transaction_t *  m_pOptionalTransaction;
    Dbc *            m_pDbc;

    // Disable assignment. Copying is allowed but assignment is not needed.
    CCursor & operator=( const CCursor & );

    // Disable array new and delete.
    static void *  operator new[]( size_t nSize );
    static void    operator delete[]( void *pArray );
  };