http://www.developer.com/

Back to article

Mastering Symbian OS Arrays


January 14, 2008

Foreword

Symbian OS is well known for its complexity and often confusing documentation, one might say. Well, in fact, this is a kind of red herring because, once you know where to look, you find that a lot of things you would implement on other OSes yourself are already done for you here. From the other side, right, the number of the classes in SDK is enormous. Array classes are not an exception from this rule, so this article will guide you thought the various available array classes and their functionality on Symbian OS.

Simple Case: Fixed Arrays

To start with, you still may use good old C-type arrays just as usual. Nevertheless, I reckon you thought at least once or twice that it would be nice to wrap it in some template to have, for example, boundary checks or operations. All right, there are STL, ATL, you name it, you might say. That's true, and Symbian OS provides the STL port nowadays. Nevertheless, it costs you some overhead; this is not always acceptable. Symbian OS offers, in addition to standard C-style arrays, a number of various fixed and dynamic template array classes. Fixed arrays are discussed here. You will look at dynamic arrays later in this article.

Fixed C-style array wrapper

TFixedArray is a wrapper class for regular C-style arrays that, in addition to standard operators, performs an automatic boundary check to guarantee the validity of all the array's methods. The public methods are listed below:

template <class T,Tint>
class TFixedArray
   {
   ...
public:
   inline TFixedArray();
   inline TFixedArray(const T* aList, Tint aLength);
   //
   inline void Copy(const T* aList, Tint aLength);
   inline void Reset();    // zero fill
   inline void DeleteAll();
   //
   inline Tint Count() const;
   inline Tint Length() const;
   // Accessors - debug range checking
   inline T& operator[](Tint aIndex);
   inline const T& operator[] (Tint aIndex) const;
   // Accessors - always range checking
   inline T& At(Tint aIndex);
   inline const T& At(Tint aIndex) const;
   // Provides pointers to the beginning and end of the array
   inline T* Begin();
   inline T* End();
   inline const T* Begin() const;
   inline const T* End() const;
   //
   inline TArray<T> Array() const;
   ...
   };

The TFixedArray class implementation has few points to keep in mind when you use it. First, the Length() method returns the size, in bytes, of a single item in the array; for the number of items in the array, use Count(). DeleteAll() invokes the delete operator for every item in the array, so you can use it only for CBase-derived elements. The last things I want to mention here are At() and operator[]. They both let you access any given item in the array. You should remember that the former checks for indexes in range for both debug and release build; the latter checks only for debug.

Although TFixedArray is still useful, in your real applications you will use dynamic arrays more often than not.

CArray Dynamic Arrays

You start your guided tour in the Symbian dynamic arrays land from the CArray family of classes. You will find many different flavors of them in the SDK documentation. But, no worries; just think about two things to choose the right one to use: the expected array's item size (fixed or variable) and the method of keeping the data in the array, that is, as one linear buffer or segmented, just like some kind of linked list. The buffer type is important because if you don't resize it frequently, you might prefer Flat-arrays. If the size of your array varies often, Seg-type arrays give you much better performance.

So, the naming pattern is quite simple:

CArray[Fix|Var|Ptr|Pak][Flat|Seg]

Ptr-arrays contain the CBase-derived elements, and Pak-arrays, packed ones. You can shuffle these parts to use the list of all available classes, except CArrayPakSeg, which is not implemented. A good thing is that you don't need to worry about freeing the elements in the array; it does it for you automatically. The only exception is Ptr-arrays where you have to call ResetAndDestroy() to release the memory.

R-Arrays

With the evolution of Symbian OS, you have two new classes : RArray and RPointerArray. They are siblings of CArrayFixFlat and CArrayPtrFlat respectively but have better performance. The reasons lie in improved memory management and assertions usage. Thus they are recommended for Flat-arrays.

Using those classes is quite simple. You should call Close() to release RArray data when you no longer need it or Reset() just to free the memory without destroying the array and reuse it later. RPointerArray holds the pointers and therefore may be responsible to release the memory if it owns the data. ReleaseAndDestroy() does all the job.

Another small but important detail with RArray class is that it uses 4-byte alignment for the storage. It may affect appending, insertion, and access methods in the elements of the array that have different alignment and cause exceptions on some hardware that enforces it.

Descriptor Arrays

To keep your mind busy, that's not the end of the story with the arrays. Here are a few additional array classes to work specifically with descriptors:

  • CDesC16ArrayFlat
  • CDesC8ArrayFlat
  • CDesC16ArraySeg
  • CDesC8ArraySeg
  • CPtrC16Array
  • CPtrC8Array

In fact, you have some kind of two-dimensional set of arrays. They inherit from CArrayNNN[Fix|Seg] base classes; CDesC-arrays have Flat and Seg flavors, CPtr-arrays use Flat buffer only. Descriptor arrays have all the functionality of the CArray-family, but in addition provide methods to reflect descriptor-specific aspects.

Typical Dynamic Array Operations

Now, you get to the practical stuff. This section provides several examples of the dynamic arrays usage. The SDK contains good and extensive examples for various types of arrays, dynamic and static, that show how to exercise typical operations, like adding new elements and so forth. What the SDK samples do not illustrate so much is how you sort the arrays, insert some elements in sorted order, and similar things. So, you will focus on this instead of duplicating the documentation.

Start with CArrays. You would want to store in them the T-classes like data or descriptors. CArrayFixBase and CArrayVarBase have Sort(), InsertIsqL(), Find(), and FindIsqL() member functions that require a TKeyArrayFix object as an argument to define the location and type of key within an array element. Those keys can represent descriptor, text, or numeric objects.

class TElement2Sort
   {
public :
   TElement2Sort()
      {
      }
public :
   TBuf<16> iBuffer;
   };

_LIT(KElem1,"fiRst");
_LIT(KElem2,"second");
_LIT(KElem3,"Third");
_LIT(KFmt,"%S\n");

LOCAL_C void MainL ()
   {

   CArrayFixFlat<TElement2Sort>* arr =
      new (ELeave) CArrayFixFlat<TElement2Sort>(3);

   TElement2Sort e1;
   e1.iBuffer.Copy(KElem1);
   TElement2Sort e2;
   e2.iBuffer.Copy(KElem2);
   TElement2Sort e3;
   e3.iBuffer.Copy(KElem3);

   arr->AppendL(e3);
   arr->AppendL(e1);
   arr->AppendL(e2);

   TKeyArrayFix key(_FOFF(TElement2Sort, iBuffer), ECmpFolded);
   Tint res = arr->Sort(key);

   for (int i = 0; i < 3; i++)
      {
         TElement2Sort e = arr->At(i);
         console->Printf(KFmt,&(e.iBuffer));
      }
   delete arr;
   }

You can insert the elements to the array in required order via an InsertIsqL() method that takes the TKeyArrayXXX parameter. Be aware that the array has to be already in the same order as the key parameter defines. You can modify the above sample to use InsertIsq() as follows:

LOCAL_C void MainL ()
   {

   CArrayFixFlat<TElement2Sort>* arr =
      new (ELeave) CArrayFixFlat<TElement2Sort>(3);

   TElement2Sort e1;
   e1.iBuffer.Copy(KElem1);
   TElement2Sort e2;
   e2.iBuffer.Copy(KElem2);
   TElement2Sort e3;
   e3.iBuffer.Copy(KElem3);

   TKeyArrayFix key(_FOFF(TElement2Sort, iBuffer), ECmpFolded);

   arr->InsertIsqL(e3,key);
   arr->InsertIsqL(e1,key);
   arr->InsertIsqL(e2,key);

   for (int i = 0; i < 3; i++)
      {
         TElement2Sort e = arr->At(i);
         console->Printf(KFmt,&(e.iBuffer));
      }
   delete arr;
   }

TLinearOrder class acts in similar way for R-Arrays. Let's see how we can sort the array of CPerson objects in the following sample which uses RPointerArray:

class CPerson : public CBase
   {
public:
   static CPerson* NewLC(Tint aId,
                         const TDesC& aFirstName,
                         const TDesC& aLastName );

   static Tint CompareByLastName(
                         const CPerson& person1,
                         const CPerson& person2);
   const TDesC& LastName();

protected:
   CPerson(Tint aId,
           const TDesC& aFirstName,
           const TDesC& aLastName);
private:
   Tint iId;
   TBuf<64> iFirstName;
   TBuf<64> iLastName;
   };

Tint CPerson::CompareByLastName( const CPerson& person1,
const CPerson& person2 )
    {
   return person1.LastName().CompareC(person2.LastName());
   }

LOCAL_C void MainL ()
   {

   RPointerArray<CPerson> people;
   CleanupClosePushL(people);

   CPerson* one = CPerson::NewLC(1, _L("John"), _L("TheFirst"));
   CPerson* two = CPerson::NewLC(3, _L("Thomas"), _L("McSecond"));
   CPerson* three = CPerson::NewLC(2, _L("Sean"), _L("O'Third"));

   people.AppendL(one);
   people.AppendL(two);
   people.AppendL(three);

   TLinearOrder<CPerson>
      sortByLastName(CPerson::CompareByLastName);
   people.Sort(sortByLastName);

   _LIT(KFmt,"%S\n");
   for (int i = 0; i < 3; i++)
      {
         CPerson* p = people[i];
         console->Printf(KFmt,&(p->LastName()));
      }

   CleanupStack::Pop(4);
   people.ResetAndDestroy();
   people.Close();

   }

You may exercise the rest of array's methods on your own, consulting the SDK documentation and samples where necessary. I hope this article helped you better understand the arrays on Symbian OS.

Conclusion

In this article, you have learned a few general topics to help you get started quicklyst with Symbian OS arrays and find your way among a crowd of different sorts and types of them. You should be able to choose suitable array class for your needs now. With more practice and a 'change and test' approach, you will definitely find a lot more quite soon. Enjoy!

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. After working almost a decade for an international retail software company as a team leader of the Windows Mobile R department, he has decided to dive into Symbian OS ™ Core development.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date