XPCOM array guide

  • Revision slug: XPCOM_array_guide
  • Revision title: XPCOM array guide
  • Revision id: 66869
  • Created:
  • Creator: JdeValk
  • Is current revision? No
  • Comment /* Typesafe Arrays */

Revision Content

Introduction

Array types

Mozilla has many array classes because each array is optimized for a particular usage pattern. This guide describes the available arrays as well as the enumerator classes that can be used to get to them. In this document the term Array refers to a container for multiple objects with a numeric, zero-based index.

The standard array classes are:

  • nsIArray - a scriptable container for scriptable XPCOM objects. This array is read-only, and the interface does not provide any methods that will allow adding and removing members.
  • nsIMutableArray - a scriptable container for scriptable XPCOM objects, which allows addition and removal of member objects. This interface actually derives from nsIArray.
  • nsCOMArray<T> - a C++ class which provides a typesafe, reference-counted container for pointers to a single type of COM object. This class is more or less a wrapper around nsVoidArray and thus shares most of its semantics.
  • nsVoidArray - a C++ class which provides a generic container for any objects using the generic void * type.
  • nsStringArray / nsCStringArray - a set of C++ classes for holding lists of string objects. Derived from nsVoidArray
  • nsAutoVoidArray - a version of nsVoidArray which includes 8 entries of internal storage for the array data.
  • nsSmallVoidArray - a replacement for nsVoidArray which is optimized to hold zero or one element.

This handy chart may make it easier to understand the different arrays:

         <td>Any</td>
         <td>No
         </td><td>No</td>
         <td>Yes*</td>
         <td>No</td>
         <td>Weak / None</td>
<tr class="even"><td>nsStringArray
nsCStringArray</td> <td>nsString
nsCString</td> <td>No </td><td>Yes</td> <td>Yes*</td> <td>No</td> <td>Private (copies strings)</td> </tr> <tr class="odd"><td>nsAutoVoidArray</td> <td>Any</td> <td>No </td><td>No</td> <td>Yes*</td> <td>Yes</td> <td>Weak / None</td> </tr> <tr class="even"><td>nsSmallVoidArray</td> <td>Any</td> <td>No </td><td>No</td> <td>Yes*</td> <td>No</td> <td>Weak / None</td> </tr> <tr class="odd"><td>nsISupportsArray</td> <td>XPCOM Object</td> <td>Yes </td><td>No</td> <td>Yes*</td> <td>No</td> <td>Reference Counted, Strong</td> </tr> </table> Note: Concrete C++ arrays can be made read-only by declaring them const. For example: // HandleList cannot modify the array because of const void HandleList(const nsVoidArray&);

In-place enumeration

Most of the arrays presented here provide callback-style means to enumerate members of an array. Instead of incrementally accessing each element of the array by its index, the arrays provide a way to pass in a callback function that will be called for each element in the array.

For most concreted C++ classes like nsVoidArray and nsCOMArray<T>, indexing should be just as fast as the callback-style enumeration, because accessing an indexed member of such an array is usually very fast. In the case of scriptable arrays like nsIArray however, the enumeration mechanism is often preferred because it avoids the AddRef / Release overhead that comes from accessing each object.

The only functional drawback to in-place enumeration is that you cannot manipulate the array itself during the enumeration. For example, you should not delete elements of an array during the enumeration as this will often confuse the loop which is enumerating the array.

Enumerators

Most arrays provide access to an object which is used to enumerate members of the array. These Enumerators maintain state about the current position in the array. Enumerators are used to access the elements in an ordered way, without relying on the underlying array type. These enumerators include:

  • nsISimpleEnumerator - an enumerator for COM objects.
  • nsIStringEnumerator / nsIUTF8StringEnumerator - enumerators for strings

Obsolete arrays / enumerators

There are some deprecated classes which should not be used by new code.

  • nsISupportsArray - obsoleted by nsIArray, use that instead.
  • nsIEnumerator - obsoleted by nsISimpleEnumerator, use that instead.
  • nsIBidirectionalEnumerator - obsoleted by nsISimpleEnumerator, use that instead.

Array Guidelines

Here are a few simple rules which will keep your code clean and your developers happy:

  • Use typesafe arrays like nsCOMArray<T> wherever possible.
  • Use enumerators for external access to internal arrays.
  • Avoid all obsolete arrays and enumerators.
  • Avoid creating temporary arrays.

Scriptable Arrays

nsIArray / nsIMutableArray

Usage

nsIArray is useful if you need to pass arrays of COM objects through interfaces or require a scriptable array. It can hold strong or weak references to its container objects. This basic interface only allows querying of existing elements in the array. The methods that modify the array have been broken out into nsIMutableArray.

An nsIArray implementation can be created from C++ with the function NS_NewArray(nsIMutableArray**);. The created array implements nsIMutableArray and nsIArray. Since nsIArray derives from nsIMutableArray, the resulting array can be cast to a read-only array.

void GetList(nsIArray** aResult) {
  nsCOMPtr<nsIMutableArray> array;
  NS_NewArray(getter_AddRefs(array));
  
  // append some elements
  ...
  
  // return it to the caller
  *aResult = array;
  NS_ADDREF(*aResult);
}

Access to elements

Since nsIArray is a regular XPCOM object, its interfaces follows the standard conventions of ownership. Access to specific elements is through QueryElementAt, which is similar to QueryInterface, but it takes a specific index.

void NotifyObservers(nsIArray* aArray) {
  PRUint32 length;
  aArray->GetLength(&length);
  for (PRUint32 i=0; i<length; ++i) {
    nsCOMPtr<nsIMyObserver> element;
    aArray->QueryElementAt(i, NS_GET_IID(nsIElement),
                                getter_AddRefs(element));
    element->Observe();
  }
}

A simpler option is to use the helper do_QueryElementAt which is typesafe.

void NotifyObservers(nsIArray* aArray) {
  PRUint32 length;
  aArray->GetLength(&length);
  for (PRUint32 i=0; i<length; ++i) {
    nsCOMPtr<nsIMyObserver> element =
    do_QueryElementAt(aArray, i);
     element->Observe();
  }
}

Passing as a parameter

Since nsIArray is an XPCOM object, it should be passed as a pointer. To distinguish between read-only arrays and writable arrays, you should make sure to pass a nsIArray or nsIMutableArray as appropriate.

When the array can or should be modified, then use nsIMutableArray:

// array is read-only because it uses nsIArray
void PrintSize(nsIArray* elements) {
  PRUint32 count;
  elements->GetLength(&count);
  printf("There are %d elements.\n", count);
}

// using nsIMutableArray, so callee may modify
void TweakArray(nsIMutableArray* elements) {
  elements->RemoveElementAt(0);
  elements->AppendElement(newElement, PR_FALSE);
}

While it is usually possible to call QueryInterface on an nsIArray to get access to the nsIMutableArray interface, this is against convention and it should be avoided.

// no need for the double-pointer, and this violates XPCOM rules
// which expect acess to a new object
void TweakArray(nsIMutableArray** elements) {
  // ugh, extra indirection!
  *elements->RemoveElementAt(0);
  *elements->AppendElement(newElement, PR_FALSE);
}

In-place enumeration

When accessing all members of an nsIArray, in-place enumeration is preferred over indexed access. However, I seem to have forgotten to implement that. Good thing the interface is under review. Sorry!

Enumerators

Creating an enumerator from an nsIArray is easy. The method Enumerate() returns a nsISimpleEnumerator which accesses all the elements in the array. Often, simply accessing an array by index, using QueryElementAt is faster. See the section on Enumerators to learn when to properly use enumerators.

For example, if you need to iterate an array returned from another object, you might use Enumerate().

...
// get the array
nsCOMPtr<nsIArray> array;
foo->GetElements(getter_AddRefs(array));

// make an enumerator
nsCOMPtr<nsISimpleEnumerator> enumerator;
array->Enumerate(getter_AddRefs(enumerator));

// now enumerate the elements
...

Typesafe Arrays

nsCOMArray<T>

nsCOMArray<T> is a typesafe wrapper around nsVoidArray, so it has a similar API. It enforces both typesafety and XPCOM reference counting by keeping an owning reference to each element in the array.

C++ Arrays

nsVoidArray

nsStringArray

nsAutoVoidArray

nsSmallVoidArray

Enumerators

nsISimpleEnumerator

nsIStringEnumerator

Obsolete Arrays and Enumerators

nsISupportsArray

nsIEnumerator (includes nsIBidirectionalEnumerator)

Which Array should I use?

ClassData TypeScriptable?Typesafe?Can be modified?Built in buffer?Ownership
nsIArray XPCOM object Yes No No No Reference Counted, Weak/Strong
nsIMutableArray XPCOM object Yes No Yes No Reference Counted, Weak/Strong
nsCOMArray<T> XPCOM object No Yes Yes* No Reference Counted, Strong
nsVoidArray</code>

Revision Source

<h2 name="Introduction"> Introduction </h2>
<h4 name="Array_types">Array types</h4>
<p>Mozilla has many array classes because each array is optimized for a particular usage pattern. This guide describes the available arrays as well as the enumerator classes that can be used to get to them. In this document the term Array refers to a container for multiple objects with a numeric, zero-based index.
</p><p>The standard array classes are:
</p>
<ul><li> <code><a href="#nsIArray_.2F_nsIMutableArray">nsIArray</a></code> - a scriptable container for scriptable XPCOM objects. This array is read-only, and the interface does not provide any methods that will allow adding and removing members.
</li><li> <code>nsIMutableArray</code> - a scriptable container for scriptable XPCOM objects, which allows addition and removal of member objects. This interface actually derives from nsIArray.
</li><li> <code>nsCOMArray&lt;T&gt;</code> - a C++ class which provides a typesafe, reference-counted container for pointers to a single type of COM object. This class is more or less a wrapper around nsVoidArray and thus shares most of its semantics.
</li><li> <code>nsVoidArray</code> - a C++ class which provides a generic container for any objects using the generic void * type.
</li><li> <code>nsStringArray</code> / <code>nsCStringArray</code> - a set of C++ classes for holding lists of string objects. Derived from nsVoidArray
</li><li> <code>nsAutoVoidArray</code> - a version of nsVoidArray which includes 8 entries of internal storage for the array data.
</li><li> <code>nsSmallVoidArray</code> - a replacement for nsVoidArray which is optimized to hold zero or one element. 
</li></ul>
<p>This handy chart may make it easier to understand the different arrays:
</p>
<pre class="eval">         &lt;td&gt;Any&lt;/td&gt;
         &lt;td&gt;No
         &lt;/td&gt;&lt;td&gt;No&lt;/td&gt;
</pre><pre class="eval">         &lt;td&gt;Yes*&lt;/td&gt;
         &lt;td&gt;No&lt;/td&gt;
         &lt;td&gt;Weak / None&lt;/td&gt;
</pre>
        &lt;tr class="even"&gt;&lt;td&gt;<code>nsStringArray</code><br><code>nsCStringArray</code>&lt;/td&gt;
          &lt;td&gt;<code>nsString</code><br><code>nsCString</code>&lt;/td&gt;

          &lt;td&gt;No
          &lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;
          &lt;td&gt;Yes*&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;Private (copies strings)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr class="odd"&gt;&lt;td&gt;<code>nsAutoVoidArray</code>&lt;/td&gt;

          &lt;td&gt;Any&lt;/td&gt;
          &lt;td&gt;No
          &lt;/td&gt;&lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;Yes*&lt;/td&gt;
          &lt;td&gt;Yes&lt;/td&gt;
          &lt;td&gt;Weak / None&lt;/td&gt;
        &lt;/tr&gt;

        &lt;tr class="even"&gt;&lt;td&gt;<code>nsSmallVoidArray</code>&lt;/td&gt;
          &lt;td&gt;Any&lt;/td&gt;
          &lt;td&gt;No
          &lt;/td&gt;&lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;Yes*&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;Weak / None&lt;/td&gt;

        &lt;/tr&gt;
        &lt;tr class="odd"&gt;&lt;td&gt;<code>nsISupportsArray</code>&lt;/td&gt;
          &lt;td&gt;XPCOM Object&lt;/td&gt;
          &lt;td&gt;Yes
          &lt;/td&gt;&lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;Yes*&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;

          &lt;td&gt;Reference Counted, Strong&lt;/td&gt;
        &lt;/tr&gt;
&lt;/table&gt;

Note: Concrete C++ arrays can be made read-only by declaring them const. For example:
 
 // HandleList cannot modify the array because of const
 void HandleList(const nsVoidArray&amp;);

<h4 name="In-place_enumeration"> In-place enumeration </h4><p>Most of the arrays presented here provide callback-style means to enumerate members of an array. Instead of incrementally accessing each element of the array by its index, the arrays provide a way to pass in a callback function that will be called for each element in the array.
</p><p>For most concreted C++ classes like <code>nsVoidArray</code> and <code>nsCOMArray&lt;T&gt;</code>, indexing should be just as fast as the callback-style enumeration, because accessing an indexed member of such an array is usually very fast. In the case of scriptable arrays like nsIArray however, the enumeration mechanism is often preferred because it avoids the AddRef / Release overhead that comes from accessing each object.
</p><p>The only functional drawback to in-place enumeration is that you cannot manipulate the array itself during the enumeration. For example, you should not delete elements of an array during the enumeration as this will often confuse the loop which is enumerating the array.
</p><h4 name="Enumerators"> Enumerators </h4><p>Most arrays provide access to an object which is used to enumerate members of the array. These Enumerators maintain state about the current position in the array. Enumerators are used to access the elements in an ordered way, without relying on the underlying array type.
These enumerators include:
</p><ul><li> <code>nsISimpleEnumerator</code> - an enumerator for COM objects.
</li><li> <code>nsIStringEnumerator</code> / <code>nsIUTF8StringEnumerator</code> - enumerators for strings
</li></ul><h4 name="Obsolete_arrays_.2F_enumerators"> Obsolete arrays / enumerators </h4><p>There are some deprecated classes which should not be used by new code.
</p><ul><li> <code>nsISupportsArray</code> - obsoleted by <code><a href="#nsIArray_.2F_nsIMutableArray">nsIArray</a></code>, use that instead.
</li><li> <code>nsIEnumerator</code> - obsoleted by <code>nsISimpleEnumerator</code>, use that instead.
</li><li> <code>nsIBidirectionalEnumerator</code> - obsoleted by <code>nsISimpleEnumerator</code>, use that instead.
</li></ul><h2 name="Array_Guidelines"> Array Guidelines </h2><p>Here are a few simple rules which will keep your code clean and your developers happy:
</p><ul><li> Use typesafe arrays like <code>nsCOMArray&lt;T&gt;</code> wherever possible.
</li><li> Use enumerators for external access to internal arrays.
</li><li> Avoid all obsolete arrays and enumerators.
</li><li> Avoid creating temporary arrays.
</li></ul><h2 name="Scriptable_Arrays"> Scriptable Arrays </h2><h3 name="nsIArray_.2F_nsIMutableArray"> nsIArray / nsIMutableArray </h3><h4 name="Usage"> Usage </h4><p><code>nsIArray</code> is useful if you need to pass arrays of COM objects through interfaces or require a scriptable array. It can hold strong or weak references to its container objects. This basic interface only allows querying of existing elements in the array. The methods that modify the array have been broken out into <code>nsIMutableArray</code>.
</p><p>An <code>nsIArray</code> implementation can be created from C++ with the function <code>NS_NewArray(nsIMutableArray**);</code>. The created array implements <code>nsIMutableArray</code> and <code>nsIArray</code>. Since <code>nsIArray</code> derives from <code>nsIMutableArray</code>, the resulting array can be cast to a read-only array. 
</p><pre class="eval">void GetList(nsIArray** aResult) {
  nsCOMPtr&lt;nsIMutableArray&gt; array;
  NS_NewArray(getter_AddRefs(array));
  
  // append some elements
  ...
  
  // return it to the caller
  *aResult = array;
  NS_ADDREF(*aResult);
}
</pre><h4 name="Access_to_elements"> Access to elements </h4><p>Since <code>nsIArray</code> is a regular XPCOM object, its interfaces follows the standard conventions of ownership. Access to specific elements is through <code>QueryElementAt</code>, which is similar to <code>QueryInterface</code>, but it takes a specific index.
</p><pre class="eval">void NotifyObservers(nsIArray* aArray) {
  PRUint32 length;
  aArray-&gt;GetLength(&amp;length);
  for (PRUint32 i=0; i&lt;length; ++i) {
    nsCOMPtr&lt;nsIMyObserver&gt; element;
    aArray-&gt;QueryElementAt(i, NS_GET_IID(nsIElement),
                                getter_AddRefs(element));
    element-&gt;Observe();
  }
}
</pre><p>A simpler option is to use the helper <code>do_QueryElementAt</code> which is typesafe.
</p><pre class="eval">void NotifyObservers(nsIArray* aArray) {
  PRUint32 length;
  aArray-&gt;GetLength(&amp;length);
  for (PRUint32 i=0; i&lt;length; ++i) {
    nsCOMPtr&lt;nsIMyObserver&gt; element =
    do_QueryElementAt(aArray, i);
     element-&gt;Observe();
  }
}
</pre><h4 name="Passing_as_a_parameter"> Passing as a parameter </h4><p>Since <code>nsIArray</code> is an XPCOM object, it should be passed as a pointer. To distinguish between read-only arrays and writable arrays, you should make sure to pass a nsIArray or <code>nsIMutableArray</code> as appropriate.
</p><p>When the array can or should be modified, then use nsIMutableArray:
</p><pre class="eval">// array is read-only because it uses nsIArray
void PrintSize(nsIArray* elements) {
  PRUint32 count;
  elements-&gt;GetLength(&amp;count);
  printf("There are %d elements.\n", count);
}

// using nsIMutableArray, so callee may modify
void TweakArray(nsIMutableArray* elements) {
  elements-&gt;RemoveElementAt(0);
  elements-&gt;AppendElement(newElement, PR_FALSE);
}
</pre><p>While it is usually possible to call <code>QueryInterface</code> on an <code>nsIArray</code> to get access to the <code>nsIMutableArray</code> interface, this is against convention and it should be avoided.
</p><pre class="eval">// no need for the double-pointer, and this violates XPCOM rules
// which expect acess to a new object
void TweakArray(nsIMutableArray** elements) {
  // ugh, extra indirection!
  *elements-&gt;RemoveElementAt(0);
  *elements-&gt;AppendElement(newElement, PR_FALSE);
}
</pre><h4 name="In-place_enumeration_2"> In-place enumeration </h4><p>When accessing all members of an <code>nsIArray</code>, in-place enumeration is preferred over indexed access. However, I seem to have forgotten to implement that. Good thing the interface is under review. Sorry! 
</p><h4 name="Enumerators_2"> Enumerators </h4><p>Creating an enumerator from an <code>nsIArray</code> is easy. The method <code>Enumerate()</code> returns a <code>nsISimpleEnumerator</code> which accesses all the elements in the array. Often, simply accessing an array by index, using <code>QueryElementAt</code> is faster. See the section on Enumerators to learn when to properly use enumerators.
</p><p>For example, if you need to iterate an array returned from another object, you might use <code>Enumerate()</code>. 
</p><pre class="eval">...
// get the array
nsCOMPtr&lt;nsIArray&gt; array;
foo-&gt;GetElements(getter_AddRefs(array));

// make an enumerator
nsCOMPtr&lt;nsISimpleEnumerator&gt; enumerator;
array-&gt;Enumerate(getter_AddRefs(enumerator));

// now enumerate the elements
...
</pre><h2 name="Typesafe_Arrays"> Typesafe Arrays </h2><h3 name="nsCOMArray.3CT.3E"> nsCOMArray&lt;T&gt; </h3><p><code>nsCOMArray&lt;T&gt;</code> is a typesafe wrapper around <code>nsVoidArray</code>, so it has a similar API. It enforces both typesafety and XPCOM reference counting by keeping an owning reference to each element in the array.
</p><h2 name="C.2B.2B_Arrays"> C++ Arrays </h2><h3 name="nsVoidArray"> nsVoidArray </h3><h3 name="nsStringArray"> nsStringArray </h3><h3 name="nsAutoVoidArray"> nsAutoVoidArray </h3><h3 name="nsSmallVoidArray"> nsSmallVoidArray </h3><h2 name="Enumerators_3"> Enumerators </h2><h3 name="nsISimpleEnumerator"> nsISimpleEnumerator </h3><h3 name="nsIStringEnumerator"> nsIStringEnumerator </h3><h2 name="Obsolete_Arrays_and_Enumerators"> Obsolete Arrays and Enumerators </h2><h3 name="nsISupportsArray"> nsISupportsArray </h3><h3 name="nsIEnumerator_.28includes_nsIBidirectionalEnumerator.29"> nsIEnumerator (includes nsIBidirectionalEnumerator) </h3><h2 name="Which_Array_should_I_use.3F"> Which Array should I use? </h2><table class="alternate data">
        <tbody><tr><th>Class</th><th>Data
            Type</th><th>Scriptable?</th><th>Typesafe?</th><th>Can be
            modified?</th><th>Built in buffer?</th><th>Ownership</th></tr>
        <tr class="even"><td><code><a href="#nsIArray_.2F_nsIMutableArray">nsIArray</a></code></td>
          <td>XPCOM object</td>
          <td>Yes
          </td><td>No</td>
          <td>No</td>
          <td>No</td>
          <td>Reference Counted, Weak/Strong</td>
        </tr>
        <tr class="odd"><td><code>nsIMutableArray</code></td>
          <td>XPCOM object</td>
          <td>Yes
          </td><td>No</td>
          <td>Yes</td>
          <td>No</td>
          <td>Reference Counted, Weak/Strong</td>
        </tr>
        <tr class="even"><td><code>nsCOMArray&lt;T&gt;</code></td>
          <td>XPCOM object</td>
          <td>No
          </td><td>Yes</td>
          <td>Yes*</td>
          <td>No</td>
          <td>Reference Counted, Strong</td>
        </tr>
        <tr class="odd"><td>nsVoidArray&lt;/code&gt;</td>


        </tr>













































</tbody></table>
Revert to this revision