These are the functions that must be either specified or considered in any new instance of a taBase class:
void Initialize()
Parent::Initialize()
, as this is a constructor
function and the parent's will be called for you by C++ as the object is
constructed.
SetBaseType()
).
taBase::InitPointer(ptr)
on every taBase object pointer in
class, or just set all pointers to NULL.
void Destroy()
CutLinks()
, if defined, to sever links with other objects.
void InitLinks()
Parent::InitLinks()
, since this is not a constructor
function and the parent's links will not otherwise be set.
void CutLinks()
Destroy
function when an object is actually deleted, or
explicitly by the user when the object is a member of another object.
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!).
CutLinks()
on any owned members, especially groups!
taBase::DelPointer()
on any pointers.
CutLinks()
on it.
CutLinks()
on those groups.
void Copy_(const T& cp), Copy(const T& cp)
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
.
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); }
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.
taBase::SetPointer(&ptr, cp.ptr)
for copying
pointers.
TA_BASEFUNS(T);
GetTypeDef()
etc. for taBase classes. These default
constructors and destructors call the other functions like
Initialize()
and Destroy()
.
TA_CONST_BASEFUNS(T);
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);
y
is the template
class, T
is the template class parameter.
void UpdateAfterEdit()
Check and add/remove/change initialization, copying, of this member in:
Initialize()
Copy_()
Copy()
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.
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);
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)
Initialize()
taBase::SetPointer(TAPtr* ptr, TAPtr new_val)
taBase::OwnPointer(TAPtr* ptr, TAPtr new_val, TAPtr ownr)
taBase::DelPointer(ptr)
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
.
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.
Con_Group* recv_gp; // the current group int g; FOR_ITR_GP(Con_Group, recv_gp, u->recv., g) recv_gp->UpdateWeights();
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:
Connection* con; // the current leaf taLeafItr i; // the iterator data FOR_ITR_EL(Connection, con, u->recv., i) con->UpdateWeights();
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();