/*****************************************************************************/
/*!
* \file context.h
*
* Author: Clark Barrett
*
* Created: Tue Dec 31 19:07:38 2002
*
*
*
* License to use, copy, modify, sell and/or distribute this software
* and its documentation for any purpose is hereby granted without
* royalty, subject to the terms and conditions defined in the \ref
* LICENSE file provided with this distribution.
*
*
*
*/
/*****************************************************************************/
#ifndef _cvc3__include__context_h_
#define _cvc3__include__context_h_
#include
#include
#include "debug.h"
#include "memory_manager_context.h"
namespace CVC3 {
/****************************************************************************/
/*! \defgroup Context Context Management
* \ingroup BuildingBlocks
* Infrastructure for backtrackable data structures.
* @{
*/
/****************************************************************************/
class Context;
class ContextManager;
class ContextNotifyObj;
class ContextObj;
class ContextObjChain;
/****************************************************************************/
/*!
* Author: Clark Barrett
*
* Created: Thu Feb 13 00:19:15 2003
*
* A scope encapsulates the portion of a context which has changed
* since the last call to push(). Thus, when pop() is called,
* everything in this scope is restored to its previous state.
*/
/****************************************************************************/
class Scope {
friend class ContextObj;
friend class ContextObjChain;
friend class CDFlags;
//! Context that created this scope
Context* d_context;
//! Memory manager for this scope
ContextMemoryManager* d_cmm;
//! Previous scope in this context
Scope* d_prevScope;
//! Scope level
int d_level;
/*! @brief Linked list of objects which are "current" in this scope,
and thus need to be restored when the scope is deleted */
ContextObjChain* d_restoreChain;
//! Called by ContextObj when created
void addToChain(ContextObjChain* obj);
public:
//! Constructor
Scope(Context* context, ContextMemoryManager* cmm,
Scope* prevScope = NULL)
: d_context(context), d_cmm(cmm), d_prevScope(prevScope),
d_restoreChain(NULL)
{ if (prevScope) d_level = prevScope->level() + 1; else d_level = 0; }
//! Destructor
~Scope() {}
//! Access functions
Scope* prevScope() const { return d_prevScope; }
int level(void) const { return d_level; }
bool isCurrent(void) const;
Scope* topScope() const;
Context* getContext() const { return d_context; }
ContextMemoryManager* getCMM() const { return d_cmm; }
void* operator new(size_t size, MemoryManager* mm)
{ return mm->newData(size); }
void operator delete(void* pMem, MemoryManager* mm) {
mm->deleteData(pMem);
}
void operator delete(void*) { }
//! Restore all the values
void restore(void);
//! Called by ~ContextManager
void finalize(void);
//! Check for memory leaks
void check(void);
};
///////////////////////////////////////////////////////////////////////////////
// //
// Class: ContextObjChain //
// Author: Sergey Berezin //
// Created: Wed Mar 12 11:25:22 2003 //
/*! Description: An element of a doubly linked list holding a copy of
* ContextObj in a scope. It is made separate from ContextObj to keep
* the list pointers valid in all scopes at all times, so that the
* object can be easily removed from the list when the master
* ContextObj is destroyed. */
///////////////////////////////////////////////////////////////////////////////
class ContextObjChain {
friend class Scope;
friend class ContextObj;
friend class CDFlags;
private:
//! Next link in chain
ContextObjChain* d_restoreChainNext;
/*! @brief Pointer to the pointer of the previous object which
points to us. This makes a doubly-linked list for easy element
deletion */
ContextObjChain** d_restoreChainPrev;
//! Pointer to the previous copy which belongs to the same master
ContextObjChain* d_restore;
//! Pointer to copy of master to be restored when restore() is called
ContextObj* d_data;
//! Pointer to the master object
ContextObj* d_master;
//! Private constructor (only friends can use it)
ContextObjChain(ContextObj* data, ContextObj* master,
ContextObjChain* restore)
: d_restoreChainNext(NULL), d_restoreChainPrev(NULL),
d_restore(restore), d_data(data), d_master(master)
{ }
//! Restore from d_data to d_master
ContextObjChain* restore(void);
public:
//! Destructor
~ContextObjChain() {}
void* operator new(size_t size, MemoryManager* mm) {
return mm->newData(size);
}
void operator delete(void* pMem, MemoryManager* mm) {
mm->deleteData(pMem);
}
void operator delete(void*) { }
// If you use this operator, you have to call free yourself when the memory
// is freed.
void* operator new(size_t size, bool b) {
return malloc(size);
}
void operator delete(void* pMem, bool b) {
free(pMem);
}
IF_DEBUG(std::string name() const;)
};
///////////////////////////////////////////////////////////////////////////////
// //
// Class: ContextObj //
// Author: Clark Barrett //
// Created: Thu Feb 13 00:21:13 2003 //
/*! Description: This is a generic class from which all objects that
* are context-dependent should inherit. Subclasses need to implement
* makeCopy, restoreData, and setNull.
*/
///////////////////////////////////////////////////////////////////////////////
class CVC_DLL ContextObj {
friend class Scope;
friend class ContextObjChain;
friend class CDFlags;
private:
//! Last scope in which this object was modified.
Scope* d_scope;
/*! @brief The list of values on previous scopes; our destructor
* should clean up those. */
ContextObjChain* d_restore;
IF_DEBUG(std::string d_name);
IF_DEBUG(bool d_active);
//! Update on the given scope, on the current scope if 'scope' == -1
void update(int scope = -1);
protected:
//! Copy constructor (defined mainly for debugging purposes)
ContextObj(const ContextObj& co)
: d_scope(co.d_scope), d_restore(co.d_restore) {
IF_DEBUG(d_name=co.d_name);
DebugAssert(co.d_active, "ContextObj["+co.name()+"] copy constructor");
IF_DEBUG(d_active = co.d_active);
// TRACE("context verbose", "ContextObj()[", this, "]: copy constructor");
}
//! Assignment operator (defined mainly for debugging purposes)
ContextObj& operator=(const ContextObj& co) {
DebugAssert(false, "ContextObj::operator=(): shouldn't be called");
return *this;
}
/*! @brief Make a copy of the current object so it can be restored
* to its current state */
virtual ContextObj* makeCopy(ContextMemoryManager* cmm) = 0;
//! Restore the current object from the given data
virtual void restoreData(ContextObj* data) {
FatalAssert(false,
"ContextObj::restoreData(): call in the base abstract class");
}
const ContextObj* getRestore() {
return d_restore ? d_restore->d_data : NULL;
}
//! Set the current object to be invalid
virtual void setNull(void) = 0;
//! Return our name (for debugging)
IF_DEBUG(virtual std::string name() const { return d_name; });
//! Get context memory manager
ContextMemoryManager* getCMM() { return d_scope->getCMM(); }
public:
//! Create a new ContextObj.
/*!
* The initial scope is set to the bottom scope by default, to
* reduce the work of pop() (otherwise, if the object is defined
* only on a very high scope, its scope will be moved down with each
* pop). If 'atBottomScope' == false, the scope is set to the
* current scope.
*/
ContextObj(Context* context, bool atBottomScope = true);
virtual ~ContextObj();
int level() const { return (d_scope==NULL)? 0 : d_scope->level(); }
bool isCurrent(int scope = -1) const {
if(scope >= 0) return d_scope->level() == scope;
else return d_scope->isCurrent();
}
void makeCurrent(int scope = -1) { if (!isCurrent(scope)) update(scope); }
IF_DEBUG(void setName(const std::string& name) { d_name=name; });
void* operator new(size_t size, MemoryManager* mm) {
return mm->newData(size);
}
void operator delete(void* pMem, MemoryManager* mm) {
mm->deleteData(pMem);
}
// If you use this operator, you have to call free yourself when the memory
// is freed.
void* operator new(size_t size, bool b) {
return malloc(size);
}
void operator delete(void* pMem, bool b) {
free(pMem);
}
void operator delete(void*) { }
};
///////////////////////////////////////////////////////////////////////////////
// //
// Class: Context //
// Author: Clark Barrett //
// Created: Thu Feb 13 00:24:59 2003 //
/*!
* Encapsulates the general notion of stack-based saving and restoring
* of a database.
*/
///////////////////////////////////////////////////////////////////////////////
class Context {
//! Context Manager
ContextManager* d_cm;
//! Name of context
std::string d_name;
//! Context ID
int d_id;
//! Pointer to top and bottom scopes of context
Scope* d_topScope;
Scope* d_bottomScope;
//! List of objects to notify with every pop
std::vector d_notifyObjList;
//! Stack of free ContextMemoryManager's
std::vector d_cmmStack;
public:
Context(ContextManager* cm, const std::string& name, int id);
~Context();
//! Access methods
ContextManager* getCM() const { return d_cm; }
const std::string& name() const { return d_name; }
int id() const { return d_id; }
Scope* topScope() const { return d_topScope; }
Scope* bottomScope() const { return d_bottomScope; }
int level() const { return d_topScope->level(); }
void push();
void pop();
void popto(int toLevel);
void addNotifyObj(ContextNotifyObj* obj) { d_notifyObjList.push_back(obj); }
void deleteNotifyObj(ContextNotifyObj* obj);
unsigned long getMemory();
};
// Have to define after Context class
inline bool Scope::isCurrent(void) const
{ return this == d_context->topScope(); }
inline void Scope::addToChain(ContextObjChain* obj) {
if(d_restoreChain != NULL)
d_restoreChain->d_restoreChainPrev = &(obj->d_restoreChainNext);
obj->d_restoreChainNext = d_restoreChain;
obj->d_restoreChainPrev = &d_restoreChain;
d_restoreChain = obj;
}
inline Scope* Scope::topScope() const { return d_context->topScope(); }
inline void Scope::restore(void) {
// TRACE_MSG("context verbose", "Scope::restore() {");
while (d_restoreChain != NULL) d_restoreChain = d_restoreChain->restore();
// TRACE_MSG("context verbose", "Scope::restore() }");
}
// Create a new ContextObj. The initial scope is set to the bottom
// scope by default, to reduce the work of pop() (otherwise, if the
// object is defined only on a very high scope, its scope will be
// moved down with each pop). If 'atBottomScope' == false, the
// scope is set to the current scope.
inline ContextObj::ContextObj(Context* context, bool atBottomScope)
IF_DEBUG(: d_name("ContextObj"))
{
IF_DEBUG(d_active=true);
DebugAssert(context != NULL, "NULL context pointer");
if (atBottomScope) d_scope = context->bottomScope();
else d_scope = context->topScope();
d_restore = new(true) ContextObjChain(NULL, this, NULL);
d_scope->addToChain(d_restore);
// if (atBottomScope) d_scope->addSpecialObject(d_restore);
// TRACE("context verbose", "ContextObj()[", this, "]");
}
/****************************************************************************/
//! Manager for multiple contexts. Also holds current context.
/*!
* Author: Clark Barrett
*
* Created: Thu Feb 13 00:26:29 2003
*/
/****************************************************************************/
class ContextManager {
Context* d_curContext;
std::vector d_contexts;
public:
ContextManager();
~ContextManager();
void push() { d_curContext->push(); }
void pop() { d_curContext->pop(); }
void popto(int toLevel) { d_curContext->popto(toLevel); }
int scopeLevel() { return d_curContext->level(); }
Context* createContext(const std::string& name="");
Context* getCurrentContext() { return d_curContext; }
Context* switchContext(Context* context);
unsigned long getMemory();
};
/****************************************************************************/
/*! Author: Clark Barrett
*
* Created: Sat Feb 22 16:21:47 2003
*
* Lightweight version of ContextObj: objects are simply notified
* every time there's a pop. notifyPre() is called right before the
* context is restored, and notify() is called after the context is
* restored.
*/
/****************************************************************************/
class ContextNotifyObj {
friend class Context;
protected:
Context* d_context;
public:
ContextNotifyObj(Context* context): d_context(context)
{ context->addNotifyObj(this); }
virtual ~ContextNotifyObj() {
// If we are being deleted before the context, remove ourselves
// from the notify list. However, if the context is deleted
// before we are, then our d_context will be cleared from ~Context()
if(d_context!=NULL) d_context->deleteNotifyObj(this);
}
virtual void notifyPre(void) {}
virtual void notify(void) {}
};
/*@}*/ // end of group Context
}
#endif