Template Class Tab

3DS Max Plug-In SDK

Template Class Tab

See Also: Class BitArray.

template <class T> class Tab

Description:

This is a generic table class. This is a type-safe variable length array which also supports list-like operations of insertion, appending and deleting. Two instance variables are maintained: nalloc is the number elements allocated in the array; count is the number actual used. (count<=nalloc). Allocation is performed automatically when Insert or Append operations are performed. It can also be done manually by calling Resize() or Shrink().

Note: Delete does not resize the storage: to do this call Shrink(). If you are going to do a sequence of Appends, its more efficient to first call Resize() to make room for them. Beware of using the Addr() function: it returns a pointer which may be invalid after subsequent Insert(), Append(), Delete(), Resize(), or Shrink() operations.

Also note: In 3ds max 1.x, the method SetCount(n) will set the count to n, but will not assure that only n items are allocated. To do that you should call Resize(n). This sets the number allocated. It will also make sure that count<=numAlloc. To make sure that exactly n are allocated and that count = n, call both Resize(n) and SetCount(n). In 3ds max 2.x and later using SetCount() will also effectively call Resize().

The implementation minimizes the storage of empty Tables: they are represented by a single NULL pointer. Also, the major part of the code is generic, shared by different Tabs for different types of elements.

Tabs may be used on the stack, i.e. they may be declared as a local variable of a function or method. You can set the number of elements in the table, work with them, and then when the function returns, the destructor of the Tab is called, and the memory will be deallocated.

Tabs are only appropriate for use with classes that don't allocate memory. For example, Tab<float> is fine while Tab<TSTR> is problematic (TSTR is the class used for strings in 3ds max). In this case, the TSTR class itself allocates memory for the string. It relies on its constructor or destructor to allocate and free the memory. The problem is the Tab class will not call the constructors and destructors for all the items in the table, nor will it call the copy operator. As an example of this, when you assign a string to another string, the TSTR class does not just copy the pointer to the string buffer (which would result in two items pointing to the same block of memory). Rather it will allocate new memory and copy the contents of the source buffer. In this way you have two individual pointers pointing at two individual buffers. When each of the TSTR destructors is called it will free each piece of memory. So, the problem with using a Tab<TSTR> is that when you assign a Tab to another Tab, the Tab copy constructor will copy all the elements in the table, but it will not call the copy operator on the individual elements. Thus, if you had a Tab<TSTR> and you assigned it to another Tab<TSTR>, you'd have two TSTRs pointing to the same memory. Then when the second one gets deleted it will be trying to double free that memory.

So again, you should only put things in a Tab that don't allocate and deallocate memory in their destructors. Thus, this class should not be used with classes that implement an assignment operator and or destructor because neither are guaranteed to be called. The way around this is to use a table of pointers to the items. For example, instead of Tab<TSTR> use Tab <TSTR *>. As another example, Tab<int> is OK, while Tab<BitArray> would be no good. In the BitArray case one should use class pointers, i.e. Tab<BitArray *>.

All methods of this class are implemented by the system except the compare function used in sorting (see Sort()).

Methods:

Prototype:

Tab(const Tab& tb)

Remarks:

Copy constructor.

Parameters:

const Tab& tb

The table to copy.

Prototype:

~Tab()

Remarks:

Destructor. The memory associated with the table is freed.

Prototype:

void Init();

Remarks:

This method is available in release 4.0 and later only.

Implemented by the System.

Provides a way of initializing a Tab<> instance outside of its constructor. Can be used to init Tab<> instances in malloc'd memory.

Prototype:

int Count() const

Remarks:

Returns the number of entries being used.

Prototype:

void ZeroCount()

Remarks:

Set the number of elements in the array that are actually used to zero.

Prototype:

void SetCount(int n)

Remarks:

Set the number of elements in the array that are actually used to n.

Parameters:

int n

The number of elements in the array that are actually used.

Prototype:

T* Addr(const int i) const

Remarks:

Returns the address of the i-th element. Beware of using this method as it returns a pointer which may be invalid after subsequent Insert, Append, Delete, Resize, or Shrink operations.

Parameters:

const int i

Specifies the element to return the address of.

Return Value:

Pointer to the i-th element.

Prototype:

int Insert(int at, int num, T *el)

Remarks:

Insert "num" elements at position "at".

Parameters:

int at

Array index where to insert the elements.

int num

Number of elements to insert.

T *el

Array of elements to insert.

Return Value:

Returns at.

Prototype:

int Append(int num, T *el, int allocExtra=0)

Remarks:

Append "num" elements at the end of the array. If you need to enlarge the array, allocate "allocExtra" extra slots

Parameters:

int num

The number of elements to append to the end of the array.

T *el

The elements to append.

int allocExtra=0

If you need to enlarge the array, allocate "allocExtra" extra slots.

Return Value:

Returns the number of elements in use (prior to appending).

Prototype:

int Delete(int start,int num)

Remarks:

List-type delete of "num" elements starting with "start"

Parameters:

int start

The start position for element deletion.

int num

The number of elements to delete.

Return Value:

Returns the number of items left in the table.

Prototype:

int Resize(int num)

Remarks:

Changes the number of allocated items to "num".

Parameters:

int num

The new size of the array.

Return Value:

Nonzero if the array was resized; otherwise 0.

Prototype:

void Shrink()

Remarks:

Reallocate so there is no wasted space (nalloc = count).

Prototype:

void Sort(CompareFnc cmp)

Remarks:

Sorts the array using the compare function.

Parameters:

CompareFnc cmp

Type of function to pass to Sort(). Note: Sort() just uses the C library qsort function. The developer must implement the CompareFnc function.

typedef int( __cdecl *CompareFnc) (const void *elem1, const void *elem2);

The return value of CompareFnc is show below in the relationship of elem1 to elem2:

< 0

if elem1 less than elem2

0

if elem1 identical to elem2

> 0

if elem1 greater than elem2

 

Sample Code:

static int CompTable( const void *elem1, const void *elem2 ) {

 TCHAR *a = (TCHAR *)elem1;

 TCHAR *b = (TCHAR *)elem2;

 return(_tcscmp(a,b));

}

Operators:

Prototype:

Tab& operator=(const Tab& tb)

Remarks:

Assignment operator. The Tab maintains a pointer to the table buffer. This assignment operator copies the table data from the item you are copying from into the memory buffer of the item you are copying to.

Parameters:

const Tab& tb

The table to assign.

Prototype:

T& operator[](const int i) const

Remarks:

Accesses the i-th entry.

Parameters:

const int i

The index of the element to access.

Return Value:

The i-th element.