6.8.1 Basic Functions

These are the functions that must be either specified or considered in any new instance of a taBase class:

void Initialize()
  • It is called in every constructor.
  • Do not call Parent::Initialize(), as this is a constructor function and the parent's will be called for you by C++ as the object is constructed.
  • Set the initial/default values of each member in the class.
  • Set the default type for groups that you own (SetBaseType()).
  • Call taBase::InitPointer(ptr) on every taBase object pointer in class, or just set all pointers to NULL.
  • EVEN IF NOTHING NEEDS INITIALIZING: use `void Initialize() { };' to avoid multiple calls to the parent's Initialize.
    void Destroy()
  • It is called in every destructor.
  • Do not call the parent, as C++ will automatically call the parent's destructor for you.
  • Free any resources you might have allocated.
  • Call CutLinks(), if defined, to sever links with other objects.
  • EVEN IF NOTHING TO DESTROY: use `void Destroy() { };' to avoid multiple calls to parents Destroy.
    void InitLinks()
  • Called when an object is linked into some kind of ownership structure.
  • Call the Parent::InitLinks(), since this is not a constructor function and the parent's links will not otherwise be set.
  • Own any classes contained as members: `taBase::Own(recv, this);'
  • Set any pointers to objects with default values (e.g., `spec->SetDefaultSpec(this));', etc.
  • Be sure to use `taBase::SetPointer(ptr, new_val);' for setting pointers.
  • Or use `taBase::OwnPointer(ptr, new_val);' for those you own.
  • If you do not need to do any of these InitLinks actions, then you do not need to define an InitLinks function.
    void CutLinks()
  • Called when an object is removed from its owner, or as part of the Destroy function when an object is actually deleted, or explicitly by the user when the object is a member of another object.
  • At end of CutLinks(), call Parent::CutLinks(), since this is not always used as a destructor function, and parent's might not be called. Note, however, that when it is called in the destructor, it will be repeatedly called, so it should be robust to this (i.e., SET ANY POINTERS YOU DELETE TO NULL SO YOU DON'T DELETE THEM AGAIN!).
  • Should sever all links to other objects, allowing them to be freed too.
  • Call CutLinks() on any owned members, especially groups!
  • Use taBase::DelPointer() on any pointers.
  • If you have a spec, call CutLinks() on it.
  • If you have group members, call CutLinks() on those groups.
    void Copy_(const T& cp), Copy(const T& cp)
  • Used to duplicate the class, Copy is the = oper and copy constructor
  • Call Parent::Copy since this will not be called otherwise.
  • Copy_(const T& cp) is an "implementation" that does the copying for just this class, and does not call the parent Copy.
  • Use COPY_FUNS(T, P); (type and parent-type) to define the default macros for doing this:
      void Copy(const T& cp)      { P::Copy(cp); Copy_(cp); }
    
  • Use SIMPLE_COPY(T); to define a Copy_ function that automatically copies the members unique to this class in a member-by-member (using TypeDef) way. This is less optimal, but easy when members are just simple floats and ints, etc.
  • Be sure to use taBase::SetPointer(&ptr, cp.ptr) for copying pointers.
    TA_BASEFUNS(T);
    This defines the actual "basic" functions like constructors, destructors, GetTypeDef() etc. for taBase classes. These default constructors and destructors call the other functions like Initialize() and Destroy().
    TA_CONST_BASEFUNS(T);
    This defines the actual "basic" functions like constructors, etc. for taBase classes which have const members defined in the class. These need to be initialized as part of the constructor, so this macro leaves out the default constructors and destructor, which should contain the following code:
      MyClass() { Register(); Initialize(); SetDefaultName(); }
      MyClass(const MyClass& cp)
       { Register(); Initialize(); Copy(cp); }
      ~MyClass() { unRegister(); Destroy(); }
    
    TA_TMPLT_BASEFUNS(y,T);
    Defines the actual "basic" functions like constructors, etc. for taBase classes which are also templates. y is the template class, T is the template class parameter.
    void UpdateAfterEdit()
  • Called after class members change via edit dialogs, loading from a file, or and assign operator in CSS.
  • Maintain consistency of member values.
  • Update links, etc.
  • 6.9 When you add/remove/change any class members:

    Check and add/remove/change initialization, copying, of this member in:

    Initialize()
    Copy_()
    Copy()

    6.10 For classes with Specs:

    A pointer to a spec is encapsulated in a SpecPtr template class, which is declared once immediately after a new class of spec types is defined as follows (this will not typically done by the user):

      SpecPtr_of(UnitSpec);  // this defines the template class 
    				    (only for base spec type)
    

    This pointer is then included in the class with the following:

      UnitSpec_SPtr  spec;  // this puts a spec pointer in the class
    

    Also, InitLinks() should have:

      spec.SetDefaultSpec(this);
    

    So that the spec pointer will set its pointer to a default instance of the correct spec type (the this pointer is because this also "owns" the spec pointer object.

    6.11 For classes with taBase members:

    All taBase members which appear as members of another class should be owned by the parent class. This increments their ref counter, so that if they are ever pointed to by something else (e.g., during loading this happens), and then unref'd, they won't then be deleted.

    InitLinks() should own the object member as follows:

      ta_Base::Own(obj_memb, this);
    

    For members that derive from taList or taGroup, Initialize() should set the default type of object that goes in the group:

      gp_obj.SetDefaultType(&TA_typename);
    

    6.12 Referring to other objects via pointers:

    If a class contains a pointer to another object, it should typically refer to that object whenever the pointer is set. The interface assumes that this is the case, and any pointer member that it sets will use the SetPointer function described below, which does the referencing of the new value and the dereferencing of the current one.

    HOWEVER, when the pointer is to a physical PARENT of the object (or just higher in the deletion hierarchy) then it should not be referenced, as this will prevent the parent from being deleted, which will then prevent the child from being deleted.

    In this case, and in general when the pointer is just for "internal use" of the class, and is not to be set by the user, the following comment directives should always be used: #READ_ONLY #NO_SAVE as this will prevent the user from overwriting the pointer, and the loading code automatically does a reference when setting a pointer, so these should not be saved. DO NOT COPY SUCH POINTERS, since they typically are set by the InitLinks based on the owner, which is usually different for different tokens.

    When managing a pointer that the user can set, there are a set of convenient functions in taBase that manage this process (note that the argument is a pointer to the pointer):

    taBase::InitPointer(TAPtr* ptr)
    initializes the pointer (or just set the ptr to NULL yourself) in Initialize()
    taBase::SetPointer(TAPtr* ptr, TAPtr new_val)
    unRef's *ptr obj if non-null, refs the new one.
    taBase::OwnPointer(TAPtr* ptr, TAPtr new_val, TAPtr ownr)
    like set, but owns the pointer too with the given owner.
    taBase::DelPointer(ptr)
    unRefDone the object pointed to, sets pointer to NULL.

    Using these functions will ensure correct refcounts on objects pointed to, etc.

    If you Own the object at the pointer, then you should either mark the member as #NO_SAVE if it is automatically created, or #OWN_POINTER if it is not. This is because saving and loading, being generic, use SetPointer unless this comment is present, in which case they use OwnPointer.

    6.13 Using Group Iterators:

    There are special iterator functions which iterate through the members of a group. One method is to iterate through those sub-groups (including the 'this' group) which contain actual terminal elements ("leaves"). This is leaf-group iteration. Then, the elements of each group can be traversed simply using the El or FastEl functions.

  • for leaf-group iteration, using macros (preferred method):
      Con_Group* recv_gp;		// the current group
      int g;
      FOR_ITR_GP(Con_Group, recv_gp, u->recv., g)
        recv_gp->UpdateWeights();
    
  • for leaf-group iteration without macros:
      Con_Group* recv_gp;		// the current group
      int g;
      for(recv_gp = (Con_Group*)u->recv.FirstGp(g); recv_gp;
          recv_gp = (Con_Group*)u->recv.NextGp(g))
        recv_gp->UpdateWeights();
    

    When all you care about are the leaf elements themselves, you can iterate over them directly using leaf-iteration:

  • for leaf-iteration, using macros (preferred method):
      Connection* con;		// the current leaf
      taLeafItr i;			// the iterator data
      FOR_ITR_EL(Connection, con, u->recv., i)
        con->UpdateWeights();
    
  • for leaf-iteration without macros:
      Connection* con;		// the current leaf
      taLeafItr i;			// the iterator data
      for(con = (Connection*)u->recv.FirstEl(i); con;
          con = (Connection*)u->recv.NextEl(i))
        con->UpdateWeights();