//------------------------------------------------------------------------------
// emModel.h
//
// Copyright (C) 2005-2008,2010,2016,2021 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------
#ifndef emModel_h
#define emModel_h
#ifndef emRef_h
#include <emCore/emRef.h>
#endif
#ifndef emContext_h
#include <emCore/emContext.h>
#endif
//==============================================================================
//================================== emModel ===================================
//==============================================================================
class emModel : public emEngine {
public:
// emModel is a base class for shared data and logics.
//
// Model references
// ----------------
// Models must be referred through instances of the template class
// emRef. A model deletes itself automatically as soon as there are no
// remaining references. It is even possible to have weak references
// by making use of emCrossPtr.
//
// Model context
// -------------
// Each model exists within a context (see emContext, emRootContext and
// emView). Contexts are used for grouping and refinding models. They
// are making up a tree. Models in a root context are something like
// "global variables".
//
// Common models
// -------------
// Models can be registered within their context so that they can be
// refound. Registered models are called common models. The identity of
// a common model must be unique and consists of:
// - The final class of the model (determined through the RTTI
// function typeid).
// - The context of the model.
// - The name of the model.
// The name is a character string whose meaning is defined by derived
// model classes. It may even be generated from model specific
// arguments.
//
// Hint: a typedef does not change the result of typeid() and therefore
// it has no effect on the identity of a model. This should not be
// forgotten when doing something like:
// typedef emVarModel<int> MyIntModel;
// Best is not to typedef models. Make derivatives instead.
//
// Private models
// --------------
// Models which are not registered for refinding, are called private
// models. They do not need to have a unique identification and they are
// referring a context just for convenience.
//
// Lifetime of common models
// -------------------------
// Common models can live longer than their user references. The method
// SetMinCommonLifetime allows to set a minimum time for which the
// context should hold the model after all other references have gone.
// Thereby, models can easily be designed for being caches, resources,
// configurations and so on.
//
// Get or create a model: the Acquire function
// -------------------------------------------
// Each derived model class which is not just a base class must have a
// static method named Acquire. That function returns a reference to an
// existing or newly created model. Constructors and destructors of
// models are never to be called by users and should always be
// protected.
static emRef<emModel> Acquire(
emContext & context, const emString & name, bool common=true
);
// This is an example for the Acquire function (it does not make
// sense here because this is just a base class). The behavior
// should always be: If common==true, search for an already
// registered model of the same class, context and name, and if
// it is found, return a reference to that model. Otherwise
// create a new model, register it if common==true, and return a
// reference to it.
// Arguments:
// context - The context of the model
// name - The name of the model.
// common - true for refinding or creating a common model,
// false for creating a private model.
// Returns: A reference to the found or created model.
// The following two macros can be used to implement Acquire functions.
// The first macro is for the general case, and the second is for
// classes of models which are always common.
# define EM_IMPL_ACQUIRE(CLASS, CONTEXT, NAME, COMMON) \
CLASS * m; \
if (!(COMMON)) m=new CLASS(CONTEXT,NAME); \
else { \
m=(CLASS*)(CONTEXT).Lookup(typeid(CLASS),NAME); \
if (!m) { m=new CLASS(CONTEXT,NAME); m->Register(); } \
} \
return emRef<CLASS >(m);
# define EM_IMPL_ACQUIRE_COMMON(CLASS, CONTEXT, NAME) \
CLASS * m; \
m=(CLASS*)(CONTEXT).Lookup(typeid(CLASS),NAME); \
if (!m) { m=new CLASS(CONTEXT,NAME); m->Register(); } \
return emRef<CLASS >(m);
emRootContext & GetRootContext() const;
// Get the root context.
emContext & GetContext() const;
// Get the context of this model.
const emString & GetName() const;
// Get the name of this model.
bool IsCommon() const;
// Ask whether this is a common model.
bool IsRegistered() const;
// Just a synonym for IsCommon().
unsigned GetMinCommonLifetime() const;
void SetMinCommonLifetime(unsigned seconds);
// Minimum lifetime after the common model is no longer in use.
// Zero means this model is deleted immediately when there are
// no other references than the context. Everything greater
// UINT_MAX/2 (or (int)seconds<0) means infinite (lifetime of
// context). The default is zero. For private models, this
// parameter has no meaning. Normally, SetMinCommonLifetime
// should be called only by model constructors, not by model
// users. Or lets say user should not shorten the lifetime.
void Alloc();
void Free();
// Increment or decrement the reference counter. Free() deletes
// this model if the reference counter gets zero. These methods
// are usually only to be called by emRef and emContext.
void LinkCrossPtr(emCrossPtrPrivate & crossPtr);
// This means emCrossPtr<emModel> is possible.
protected:
emModel(emContext & context, const emString & name);
// Constructor.
// Arguments:
// context - The context for this model
// name - The name for this model.
virtual ~emModel();
// Destructor.
void Register();
void Unregister();
// Register or unregister this model in its context.
// IMPORTANT:
// - These methods must never be called from constructors or
// destructors of the model (directly or indirectly), because
// the final class is part of the registration identity, and
// typeid(this) does not return the final class while
// constructing or destructing a base class.
// - There must not be more than one registered model of the
// same class, context and name.
// - Usually, Register() should only be called by the Acquire
// functions, and Unregister() should never be called by any
// derivatives.
virtual bool Cycle();
// emModel has been derived from emEngine for convenience. This
// default implementation does nothing and returns false.
private: friend class emContext;
emContext & Context;
// The context of this model.
emString Name;
// The name of this model.
emCrossPtrList CrossPtrList;
// List of cross pointers to this model.
emAvlNode AvlNode;
// Node for this model in the AVL tree of common models of the
// context.
int AvlHashCode;
// Hash index for this model in the AVL tree of the context.
// Or zero if this is not a common model.
int RefCount;
// Number of references to this model (including the context
// when this is a common model).
unsigned MinCommonLifetime;
// If this is a common model: Number of seconds the context
// should keep this model alive when there are no other
// references. (int)MinCommonLifetime<0 means infinite.
unsigned TimeOfDeath;
// If this is a common model which is only referred by the
// context: Planed time of death (in units of
// Context.SharedTiming->SecsCounter)
};
inline emRootContext & emModel::GetRootContext() const
{
return Context.RootContext;
}
inline emContext & emModel::GetContext() const
{
return Context;
}
inline const emString & emModel::GetName() const
{
return Name;
}
inline bool emModel::IsCommon() const
{
return AvlHashCode!=0;
}
inline bool emModel::IsRegistered() const
{
return AvlHashCode!=0;
}
inline unsigned emModel::GetMinCommonLifetime() const
{
return MinCommonLifetime;
}
inline void emModel::Alloc()
{
RefCount++;
}
inline void emModel::LinkCrossPtr(emCrossPtrPrivate & crossPtr)
{
CrossPtrList.LinkCrossPtr(crossPtr);
}
inline void emModel::Register()
{
Context.RegisterModel(this);
}
inline void emModel::Unregister()
{
Context.UnregisterModel(this);
}
#endif