Revised EDM Model Notes
                           25 February - 02 March 1999
                                   Rob Kennedy
I) Minimize copies in transient <=> stored representation
   -) Have insert() store ObjectIds into things passed to it
II) Revisions to Collections APIs to reduce coupling
-------------------------------------------------------------------------------
I) Minimize copies in transient <=> stored representation
	Suppose there is class Muon which contains data members of built-in
data type and a one-way association to a class Track, implemented with a smart
StorablePointer instance:
template<class TYPE>
class StorablePointer
{ TYPE*        _p_item ;   // TRANSIENT
  EventRecord* _p_record ; // TRANSIENT
  ObjectNumber _number ;
  static ObjectName name(void) { return(ObjectName("TYPE")) ; } // or equivalent
  stream_read() ;
  stream_write() ;
  setPointer(TYPE*) ;
  setPointer(RecordIterator) ; // downcast to TYPE* from StorableObject*
  setId(EventRecord*, ObjectId) ;
  // Can have _p_item active, _number active, or both
  // _p_record is NULL: object pointed to is not stored, so _number inactive
} ;
class Muon
{ StorablePointer<Track> _p_track ;
  activate(void) ;
  deactivate(EventRecord*, const ObjectId&) ;
}
class GenericStorableObjectPointer: public SmartRefCntPointer
  { StorableObject* _p_generic_object ;
    constructor(const SpecificStorablePointer<TYPE>& p_specific_object)
      { _p_generic_object = static_cast<StorableObject*>(p_specific_object) ;
      }
    operator *() ;
    operator ->() ;  // StorableObject API only... objectName(), objectNumber()
    bool is_valid(void) const ;
    // Smart RefCnt pointer machinery
  } ;
template <class TYPE>
class SpecificStorableObjectPointer: public SmartRefCntPointer
  { TYPE* _p_specific_object ;
    constructor(const GenericStorablePointer& p_generic_object)
      { _p_specific_object = dynamic_cast<TYPE*>(p_generic_object) ;
      } // TYPE must be child of StorableObject for dynamic_cast to work
    operator *() ;
    operator ->() ;  // TYPE's API available
    bool is_valid(void) const ;
    // Smart RefCnt pointer machinery
  } ;
// The EventRecord is a restricted list of GenericStorableObjectPointers.
bool EventRecord::insert(const GenericStorableObjectPointer& p_item)
  {
    _list.insert(p_item) ;
    return(true) ;
  }
class StorableObject
{
  virtual bool activate(void) = 0 ;
  virtual bool deactivate(EventRecord* p_record,
                          const ObjectId* p_objectId = 0,
                          const int       n_objectId = 0) = 0 ;
}
-------------
Track aTrack(...) ; // Track derived from StorableObject
SpecificStorableObjectPointer<Muon> p_muon = new Muon(&aTrack, ...) ;
ObjectId muonId = p_record->insert(p_muon) ; // up-cast to GenericStorObjPtr
float pt = p_muon->pt() ;
ObjectId trackId = p_record->insert(aTrack) ;
p_muon->deactivate(p_record, &trackId, 1) ;
  {
    _p_track.setId(p_record, trackId) ;
  }
ObjectId muonId = p_record->insert(p_muon) ;
// And now in a different module
RecordIterator muonIter = p_record->find_first("Muon") ;
SpecificStorableObjectPointer<Muon> p_muon(muonIter) ;
float pt = p_muon->pt() ;
p_muon->activate() ;
  {
    ObjectId trackId = p_muon->p_track().objectId() ;
    RecordIterator trackIter = p_record->find(trackId) ;
    _p_track.setPointer(trackIter) ;
  }
This setPointer() method here hides a downcast from StorableObject* to TYPE*,
just as the SpecificStorableObjectPointer constructor (GenericStorObjPtr) hides
one. This is the price to be paid to go from the generic classes RecordIterator
and GenericStorableObjectPointer that only exhibit traits related to
StorableObjects to the classes with specific object APIs, StorablePointer and
SpecificStorableObjectPointer. The naming scheme should be cleaned up to avoid
confusion, since some names are rather similar for very different classes.
---------------
Consider the following case, however (from D0 experience):
Track aTrack(...) ; // Track derived from StorableObject
Muon* pointer_to_muon = new Muon(&aTrack, ...) ;  // bare pointer
SpecificStorableObjectPointer<Muon> p_muon = pointer_to_muon ;
ObjectId muonId = p_record->insert(p_muon) ;
ObjectId trackId = p_record->insert(aTrack) ;
p_muon->deactivate(&trackId, 1) ;
ObjectId muonId = p_record->insert(p_muon) ;
delete pointer_to_muon ;
This releases the memory that something in the record points to. Jim K's has
been looking into preventing this by controlling how that the
SpecificStorableObjectPointer<> instance can be constructed. For instance, one
can use a memory allocation factory in the EventRecord class:
Track aTrack(...) ; // Track derived from StorableObject
SpecificStorableObjectPointer<Muon> p_muon = p_record->allocate<Muon>(1) ;
ObjectId muonId = p_record->insert(p_muon) ;
ObjectId trackId = p_record->insert(aTrack) ;
p_muon->deactivate(&trackId, 1) ;
ObjectId muonId = p_record->insert(p_muon) ;
// Nothing is left to be deleted erroneously
// Crude, off-the-cuff implementations... do not take seriously
SpecificStorableObjectPointer<class TYPE>
EventRecord::allocate<class TYPE>(const int n_instances)
{ size_t allocation_size = sizeof(TYPE)*n_instances ;
  void*  p_allocation = 0 ;
  if      (allocation_size <   256)
    { p_allocation = _allocate_from_small_pool(allocation_size) ;
    }
  else if (allocation_size <  4096)
    { p_allocation = _allocate_from_medium_pool(allocation_size) ;
    }
  else if (allocation_size < 65536)
    { p_allocation = _allocate_from_large_pool(allocation_size) ;
    }
    SpecificStorableObjectPointer<TYPE> temp(*this, p_allocation) ;
    return(temp) ;
}
EventRecord::deallocate<class TYPE>(SpecificStorableObjectPointer<class TYPE> p)
{
  void*  p_allocation = p.where() ;
  size_t allocation_size = sizeof(*where) ;
  if      (allocation_size <   256)
    { _deallocate_from_small_pool(p_allocation) ;
    }
  else if (allocation_size <  4096)
    { _deallocate_from_medium_pool(p_allocation) ;
    }
  else if (allocation_size < 65536)
    { _deallocate_from_large_pool(p_allocation) ;
    }
    return ;
}
The number of instances argument is useless in this crude implementation. I
need to provide array allocator/deallocators which use arrays of
SpecStorObjPtr. Does this  particular API leads to a coupling of the
StorableObjectPointer classes to the EventRecord?
-------------------------------------------------------------------------------
02-Mar-1999
Can I improve the robustness by storing the ObjectId inside Muon in insert()?
class Muon: public StorableObject
{ StorablePointer<Track> _p_track ;
  setPointer(void) ;
  setLink(void) ;
} ; // A transient Muon class
ObjectId
Edm::EventRecord::
insert(StorableObject& thing)  // non-const argument since _number will change
{
  if (thing.number() != ObjectNumber::Null)
    { // ERROR: This object already inserted!!!
      return(thing.number()) ; // avoids user reaction to this error
    }
  ObjectName   name   = thing.name() ;
  ObjectNumber number = acquireNextNumber(name) ;
  thing.setNumber(number) ;
  if (! store(thing))
    { thing.setNumber(ObjectNumber::Null) ;
      releaseNumber(name, number) ;
    }
  thing.setReadOnly() ;
  return(number) ;
}
class StorableObject
{
  friend EventRecord::insert ;
  public:
  ObjectName   name(void) const ;
  ObjectNumber number(void) const ;
  private:
  void setNumber(ObjectNumber newval) ;
  void setReadOnly(void) ;
  private: // Never touch data members directly outside of class
  ObjectNumber _number ; == ObjectNumber::Null by default
  bool         _is_readonly ;
} ;
// ModuleA
//========
Track aTrack(...) ;
SmartRCPointer<Muon> p_muon = new Muon(&aTrack, ...) ;
float pt = p_muon->pt() ;   // Pointer has Muon API via op->()
ObjectId trackId = p_record->insert(aTrack) ;
p_muon->setLink() ;         // Asks aTrack for its Id, and sets link
ObjectId muonId = p_record->insert(p_muon) ; // insert muon
// ModuleB
//========
RecordIterator iter_muon(p_record, SelectByName("Muon")) ;
ConstSmartRCPointer<Muon> p_muon(iter_muon) ;
p_muon->setPointer()
  { // code for setPointer()
    ObjectId trackId = p_muon->_p_track.objectId() ; // query link for Id
    RecordIterator trackIter(p_record, SelectById(trackId)) ;
    _p_track.setPointer(trackIter) ;
    // casts const StorableObject* in trackIter to const TYPE* in _p_track
  }
	At this pointer, both the Muon and the Track are stored in the
EventRecord, but they linked with a transient pointer (which is not stored in
the persistent representation, of course). Care must be taken to insure that the
read-only nature of the stored objects is preserved.
-------------------------------------------------------------------------------
II) Revisions to Collections APIs to reduce coupling
	After reviewing the current CdfTrackCollection implementation which is
being adopted as the StorableCollection prototype, Jim K has made several
suggestions to reduce coupling between collections, iterators, and predicates.
LAST NOTES:
for (Iterator iter = mygoldmus.begin(Muon::PtGreaterThan(4.0)) ;
     iter != mygoldmus.end() ;
     ++iter)
  {
    // treat muons with Pt > 4.0
  }
SUGGESTED:
for (Iterator iter(mygoldmus, Muon::PtGreaterThan(4.0)) ;
     iter != mygoldmus.end() ;
     ++iter)
  {
    // treat muons with Pt > 4.0
  }
Note a) The STL algorithms tend to take as arguments either (collection,
predicate) or (iter_begin, iter_end, predicate). Avoid EventRecord::begin(Pred).
Note b) The STL containers have few member function manipulators. Most
manipulators are external generic algorithms. Avoid EventRecord.find_first().
class Iterator
{
  Iterator(Iterator begin, Iterator end, Predicate selector) ;
  Iterator(Collection entire_container,  Predicate selector) ;
}
class Container
{ Iterator begin() ;
  Iterator end() ;
}
class ElementOfCollection
{ class Predicate ;
  class Comparison ;
}
for (Iterator iter1(my_muon_set, Muon::PtGreaterThan(4.0)) ;
     iter1 != my_muon_set.end() ;
     ++iter1)
  {
    for (Iterator iter2 = iter1.peek_ahead() ;
         iter2 != my_muon_set.end() ;
         ++iter2)
      {
        // treat distinct muon pairs, for muons with Pt > 4.0
      }
  }
-------------------
	Similarly for Views, we should not have a collection know how to make a
view of itself, but just the reverse. Check out the discussion of the Observer
pattern in Design Patterns by Gamma et al.
LAST NOTES:
MuonSetView myhardgoldmus = mygoldmus.createView(PtGreaterThan(4.0));
SUGGESTED:
MuonSetView myhardgoldmus(mugoldmus, PtGreaterThan(4.0), Muon::RankByPt) ;