[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

C.4.4 Container Templates Changes

csArray<> Index and Size Type Change

csArray<> has been changed to use ‘size_t’ instead of ‘int’ for array sizes and indices. Probably, the most significant semantic difference of this change is that ‘size_t’ is usually unsigned as opposed to ‘int’ which is signed.

Read this paragraph carefully. In actual code, this change may result in nothing more than simple-looking compiler warnings. However, it may have caused semantics to change subtly.

This has the greatest impact in the areas discussed below.

Iterating Over csArray<> Contents

For this purpose, usually, ‘int’ typed variables were used for keeping track of the index of the current item of an array iteration. Fixing forward iteration over a csArray<> to instead use ‘size_t’ is simple:

 
csArray<...> a;
for (int i = 0, n = a.Length(); i < n; i++)
{
  ...
}

Is changed to:

 
...
for (size_t i = 0, n = a.Length(); i < n; i++)
...

Backward iteration is a bit more problematic. As mentioned before, ‘size_t’ typically is unsigned, so you do not have negative values. This can be a problem in constructs like:

 
csArray<...> a;
for (int i = a.Length() - 1; i >= 0; i--)
{
  ...
}

If the ‘int’ is changed simply to a ‘size_t’, this for-loop misbehaves unexpectedly. When ‘i’ becomes 0, the body of the loop is executed correctly. However, decrementing ‘i’ will not cause ‘i’ to have a negative value—after all, ‘size_t’ is unsigned. Instead, decrementing will make ‘i’ wrap around from 0 to 0xffffffff (on 32-bit machines). Clearly, this is greater than 0, effectively causing an infinite loop. The same thing also happens when ‘a’ has no items (i.e. ‘a.Length() == 0’). Here the -1 in the initialization of ‘i’ causes wrap-around.

Possible solutions include:

Searching

Functions like csArray<>::FindSortedKey() and csArray<>::Find() used to return -1 to signify that an item was not found. This is still the case, but with a twist. Now, ‘(size_t)-1’ is returned. For convenience, a constant, ‘csArrayItemNotFound’, has been added, which has this exact value. It can be used in comparisons to make it clear what is actually being checked.

Apropros comparisons: Recall that ‘size_t’ is unsigned, so checking whether an index returned by ‘FindSortedKey()’ or ‘Find()’ is actually a valid index by testing with ‘>= 0’ no longer works since ‘(size_t)-1’ is always >= 0. Replace such checks with tests of equality against ‘csArrayItemNotFound’:

 
csArray<...> a;
int index = a.Find (...);
if (index >= 0)
{
  /* Item found */
  ...
}
else
{
  /* Item not found */
  ...
}

Must be replaced with:

 
csArray<...> a;
size_t index = a.Find (...);
if (index != csArrayItemNotFound)
{
  /* Item found */
  ...
}
else
{
  /* Item not found */
  ...
}

‘csutil/garray.h’ Deprecated

The ‘csutil/garray.h’ file, containing the class csDirtyAccessArray<>, has been deprecated in favor of the more sensible file name ‘csutil/dirtyaccessarray.h’. No functional changes have been made to the class.

csList<>::Iterator Normalization

The behavior of csList<>::Iterator has been normalized so that it functions identically to all other iterators in Crystal Space. In the past, a newly created csList<>::Iterator already pointed at the first element in the container. This differed from all other iterator implementations, in which the first element, like all other elements, is accessed by an invocation of Next().

Old code which accessed the elements of a csList<>::Iterator required a non-standard idiom, such as:

 
csList<sometype> list = ...;
csList<sometype>::Iterator it(list);
if (it.HasCurrent())
{
  do_something(*it);         // First value.
  while (it.HasNext())
  {
    do_something(it.Next()); // Remaining values.
  }
}

Or, the slightly less complicated, though still somewhat convoluted:

 
csList<sometype>::Iterator it(list);
while (it.HasCurrent())
{
  do_something(*it);
  it.Next();
}

Following normalization, csList<>::Iterator now works like all other iterators throughout the toolkit. In particular, for forward iteration, all elements can be accessed via the standard HasNext() / Next() idiom:

 
csList<sometype>::Iterator it(list);
while (it.HasNext())
{
  do_something(it.Next());
}

Likewise, for backward iteration, the HasPrevious() / Previous() idiom works in the obvious and expected inverse fashion.

Finally, csList<>::Iterator::Next() and Previous() now return references to contained elements, rather than pointers. This makes the interface of csList<>::Iterator internally consistent, as well as consistent with other iterators throughout the toolkit, in which these methods return references.

csSet<> Relocation and Changes

The declaration of csSet<> was moved out from ‘csutil/hash.h’ and into its own header file, ‘csutil/set.h’. The method csSet<>::GetHash() has likewise been removed since it unnecessarily exposed an underlying implementation detail of csSet<>.

However, this implementation detail still affects the use of csSet<> as for storage of pointers in a csSet<> the csPtrKey<> wrapper must be used.

csHash<> Changes

The way csHash<> handles support for arbitrary key types has been changed. Previously, a so-called “hash key handler” was passed as an optional third template parameter that computed hash keys and was able to compare keys. Now, the hash computation is handled through the new csHashComputer<> template, and key comparison through csComparator<>. To provide hash computation and comparisons for specific types used as hash keys, specializations of the templates mentioned above are needed. Alternatively the default comparator uses the ‘operator <’ and ‘operator >’ operators, so you can also implement those operators instead of a csComparator<> specialization. The default csHashComputer<> implementation assumes that a type provides a ‘GetHash()’ method that computes the hash value for an object. However, this means that integral types need a specialization. As a convenience, Crystal Space already provides specializations of csHashComputer<> for a number of commonly used intergrals. Further, csComparator<> and/or csHashComputer<> specializations are provided for the commonly used types ‘csString’, ‘const char*’ and ‘csStrKey’; and the default comparison semantics of ‘operator <’ and ‘operator >’ handle the remaining fundamental types and any composite types which provide these operators. Pointers (other than ‘void*’ for which exists a specialization) as hash keys are a special case: to use a pointer of type ‘Foo*’ as a hash key, employ ‘csPtrKey<Foo>’ as the key type in the hash declaration.

Default csHash<> (and csHashReversible<>) Table Size

The default hash table size and grow rate has been reduced from 257 and 64 to 23 and 5, respectively, to have hashes use less memory. If a hash is expected to contain a large quantity of elements, those two parameters can be tweaked upon construction of a csHash<> or csHashReversible<>. Consult the Public API Documentation of csHash<> for more information.

‘csHashMap’ and ‘csHashSet’ Removal

The long-deprecated ‘csHashMap’, ‘csHashMapReversible’, and ‘csHashSet’ classes have been removed. Instead, use the templated classes csHash<>, csHashReversible<>, and csSet<>. For string-related set functionality, you may also want to consider using ‘csStringHash’ and ‘csStringSet’.

csObjectPool<> Removal

The csObjectPool<> utility class has been removed since it managed memory poorly, and failed to invoke the destructor of contained objects at the time they were recycled via csObjectPool<>::Free(), which meant that the objects might not release their own resources in a timely fashion. (It did correctly destroy the freed objects when the pool itself was destroyed, but should have been doing so at Free()-time.)

You can use csBlockAllocator<> (see ‘csutil/blockallocator.h’) as a drop-in replacement for csObjectPool<> since the API of csBlockAllocator<> is a proper superset of the csObjectPool<> API. A global search and replace should be sufficient to account for this change.

‘csStringSetIterator’ and ‘csStringHashIterator’ Removal

‘csStringSetIterator’ and ‘csStringHashIterator’ have been replaced with the iterator classes ‘csStringSet::GlobalIterator’ and ‘csStringHash::GlobalIterator’, respectively. Rather than instantiating iterators directly, you now ask the set or hash for an iterator. This brings the ‘csStringSet’ and ‘csStringHash’ API's in line with other container classes which vend iterators. Given old code which is using ‘csStringSetIterator’:

 
csStringSet set;
// ...populate set...
csStringSetIterator iter(&set);
while (iter.HasNext())
{
  char const* s = iter.Next();
  // ...do something with `s'...
}

Convert it like this:

 
csStringSet::GlobalIterator iter = set.GetIterator();
while (iter.HasNext())
{

Code using ‘csStringHashIterator’ can be fixed using the same simple transformation.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated using texi2html 1.76.