nsCOMPtr
sUse nsCOMPtr
s when you need to refcount - both for local
variables and for member variables. See the nsCOMPtr
User's Manual for more details.
QueryInterface
, AddRef
, and Release
manuallyDon't bother writing the standard forms of
QueryInterface
, AddRef
, and
Release
manually. It just introduces possibility of error
and makes it harder for us to switch to table-driven
QueryInterface
if we eventually decide to do so (or, more
likely, your class will just miss out). There are macros in nsISupportsUtils.h
that implement these functions for you. To implement these three
functions for an object nsBlah
implementing three XPCOM
interfaces, nsIFoo
, nsIBar
, and
nsIBaz
, where a QueryInterface
to
nsISupports
should give the pointer to the
nsIFoo
, use:
NS_IMPL_ISUPPORTS3(nsBlah, nsIFoo, nsIBar, nsIBaz)If you want some of these but not the others, this can be split into:
NS_IMPL_QUERYINTERFACE3(nsBlah, nsIFoo, nsIBar, nsIBaz) NS_IMPL_ADDREF(nsBlah) NS_IMPL_RELEASE(nsBlah)and the
QueryInterface
can be split even further into
NS_INTERFACE_MAP_BEGIN(nsBlah) NS_INTERFACE_MAP_ENTRY(nsIFoo) NS_INTERFACE_MAP_ENTRY(nsIBar) NS_INTERFACE_MAP_ENTRY(nsIBaz) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFoo) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(nsBlah) NS_IMPL_RELEASE(nsBlah)
There are also similar macros for an inherited implementation (where
the AddRef
, Release
, and the tail of the
QueryInterface
methods forward to a base class. These are
needed when the derived class implements additional interfaces that the
base class does not. These macros are (for small n
)
NS_IMPL_ISUPPORTS_INHERITEDn
, which can be split up into
NS_IMPL_QUERY_INTERFACE_INHERITEDn
,
NS_IMPL_ADDREF_INHERITED
, and
NS_IMPL_RELEASE_INHERITED
.
There are also similar macros (insert _THREADSAFE
after
NS_IMPL
) for implementing these three functions in a
threadsafe manner. This means that reference counting is done in a
threadsafe manner and that the QueryInterface
is hacked in
DEBUG builds to allow the NS_VERIFY_THREADSAFE_INTERFACE
macro to work. (There aren't macros for a threadsafe inherited
implemenation since the normal inherited implementation macros are
sufficient.)
Note: these macros may not work well when writing XPCOM components that do not link against XPCOM and/or are intended to be used with multiple versions of XPCOM, i.e., components that are not part of the regular Mozilla build.
IIDs introduce a huge potential for type safety violations, since
they require the programmer to specify the same thing twice (in the type
of a variable, and in an IID) without having the compiler check that the
two are the same. However, some neat tricks in function templates in
XPCOM exist so that you should almost never need to manually deal with
IIDs in your code. (That is, you should never have to type
NS_GET_IID
or NS_DEFINE_IID
, and the presence
of these macros is a sign that code is not typesafe.)
Rather than manually calling functions such as
QueryInterface
, QueryReferent
,
GetInterface
, CreateInstance
, and
GetService
, you should use one of two typesafe
alternatives:
nsCOMPtr
s, which you should be in
most cases, use the helpers do_QueryInterface
,
do_QueryReferent
, do_GetInterface
,
do_CreateInstance
, do_GetService
that
determine the correct IID from the type of the
nsCOMPtr
. For example:
nsCOMPtr<nsIDOMDocument> domDoc( do_QueryInterface(mDocument) );
nsCOMPtr<nsIDOMElement> elt( do_QueryInterface(aNode, &rv) );
nsCOMPtr<nsIPrefService> pref( do_GetService(NS_PREF_CONTRACTID, &rv) );
nsCOMPtr
s, which is reasonable
for things like out parameters or static variables, use the
template functions CallQueryInterface
,
CallQueryReferent
, CallGetInterface
,
CallCreateInstance
, or CallGetService
,
which determine the correct IID from the type of the last
parameter. For example:
return CallQueryInterface(node, aResult);
rv = CallCreateInstance(kStyleSetCID, aResult);
CallGetService("@mozilla.org/js/xpc/RuntimeService;1", &gJSRuntimeService);
Pay attention to where you use owning pointers as member variables. It's very easy to create ownership cycles that cause all of the objects in the cycle and everything else they own to leak. If you think you can break the cycles, think again: are you breaking the cycles from a destructor? from something called from a destructor? Most hard-to-fix leaks come from ownership cycles that programmers thought they could break reliably.
See scc's COM Ownership Guidelines for more information.
nsAString / \ nsASingleFragmentString `-------------------------. / \ \ nsAFlatString \ nsAPromiseString / \ \ \ nsSingleFragmentDepende- / \ / \ \ `------. ntSubstring / \ / \ `-------. \ nsDependentConcatenation nsDependentSubstring / `-. \ `------------------------. / \ \ \ nsSharableString nsString nsDependentString nsPromiseFlatString | | \ nsXPIDLString nsAutoString NS_LITERAL_STRING. NS_NAMED_LITERAL_STRINGalthough in the future they will probably look something more like this:
nsAString / \ / `---------------------------------. nsASingleFragmentString \ \ / \ nsDependentConcatenation nsDependentSubstring / `---------------. nsAFlatString \ | \ \ nsSingleFragmentDependentSubstring | \ `-------. nsSharableString `-----. \ / \ \ nsPromiseFlatString / nsString nsDependentString / | \ nsXPIDLString nsAutoString NS_LITERAL_STRING, NS_NAMED_LITERAL_STRING
nsAString
represents a sequence of characters that may be stored in multiple
fragments and need not be null-terminated.
nsAFlatString
is an abstract type for "flat"
strings, i.e., strings that are a single fragment and are
null-terminated. All implementations of
nsAFlatString
have a get
method to
return a const PRUnichar*
.
nsASingleFragmentString
is an abstract type
describing a string that is a single fragment but is not
necessarily null-terminated. The result of calling
Substring
on any single fragment string is also a
single fragment string.
nsString
is a general concrete string class. It
allocates storage on the heap. In the future, it will use
copy-on-write reference counted buffers. The current
implementation is obsolete (and has a number of serious
problems, including excessive indirection in non-inlined function
calls significant enough to seriously hurt performance), but there
will eventually be a new implementation by the same name (although
probably with some of the member functions removed) in the new
string hierarchy.
nsAutoString
is like nsString
, except it
has a 64 character buffer internal to the object so that it can
avoid allocation when the string remains less than 64 characters.
It is meant to be used on the stack, where the extra size is not a
serious bloat problem and where the lack of allocation can reduce
the required number of allocations (which are expensive due to
locking) from 1 to 0 rather than from 2 to 1.
nsSharableString
is the base sharable string class,
for copy-on-write buffers. Its current implementation is
incomplete and a number of methods do not yet work. It may
eventually be renamed to nsString
.
nsXPIDLString
(in the new world -- there was once an
obsolete implementation of it) is like an
nsSharableString
except that it has a mechanism for
assigning to it through assignment to a PRUnichar**
parameter to a function.
getter_Copies(my_XPIDLString)
gives a
PRUnichar**
that can be filled in with a buffer.
nsDependentString
wraps an existing character buffer
as an nsAFlatString
, without copying. The life of
the nsDependentString
should never extend past the
life of the buffer on which it is dependent.
wchar_t
is not (or
cannot via a compiler option) a 2-byte unsigned character, this is
equivalent to the more expensive
NS_ConvertACSIItoUCS2
.)
nsDependentConcatenation
is a multi-fragment string
class that is the result of using operator+
on two
nsAString
s. Thus building a string
operator+
is more efficient than building up a string
using Append
since it does not copy, and even if a
copy is required it can be more efficient to use
operator+
since it will prevent the need for
reallocation.
nsDependentSubstring
is the result of the
Substring
function. Using Substring
is
more efficient than using nsAString::Mid
,
nsAString::Left
, or nsAString::Right
since it does not copy. The signatures of the latter three
functions may at some point in the future be changed to encourage
more efficient style.
nsPromiseFlatString
is the result of a call to
PromiseFlatString(nsAString&)
. It provides access to
a flat string (i.e., one that is single-fragment and
null-terminated) whether or not the underlying string is flat. If
the underlying string is flat, the underlying buffer is simply
returned, but if it is not flat, a new buffer is created and the
string is copied into that buffer, which is destroyed once the
nsPromiseFlatString
object goes away.
For functions that take strings as parameters (including those that
modify strings), use an appropriately abstract parameter type.
Generally this means using const nsAString&
or
nsAString&
, but in some cases it may be preferable to use
one of the other abstract types for performance reasons. (There are
currently some member functions of nsString that don't have good
replacements. Needing these functions provides a legitimate reason to
use const nsString&
and nsString&
parameters
for the time being.)
When using iterator variables, declare the types as the
iterator
or const_iterator
type of the string type that the iterator is iterating
over. For example, when iterating over a string that is statically
typed as an nsAFlatString
, use
nsAFlatString::const_iterator
.
To loop over the characters in a string, it is inefficient to simply
use the pattern for ( ...; iter != iter_end; ++iter) { ...
}
since the operator++
on iterators must check every
time to see whether the current position is the last one in a fragment.
Instead, it is better to use two nested loops, where the outer loop
checks iter.size_forward()
and the inner loop iterates over
the number of characters returned and then the outer loop advances by
that number of characters. It is also possible to accomplish the same
thing in a cleaner way with a character sink and the |copy_string|
function.
If you want a string object that always contains the same text, don't
use ns[C]AutoString
, use NS_LITERAL_[C]STRING
,
which doesn't copy (or convert), except on platforms where wchar_t is
not 2-byte unsigned (most Unix platforms, but not newer gcc's).
If you want a string object that represents a substring of a string,
don't copy it into another string, use Substring
. There
are two variants of Substring
: Substring(
string,
start-position,
length)
and Substring(
begin-iterator,
end-iterator)
. (As always, with a range of
characters, the begin iterator points to the first character that is
part of the substring and the end iterator points to the first character
after the end of the substring.)
operator+
to Append
Rather than assigning to a string with multiple calls to
Append
, use a single call to Assign
and
operator+
. Since the result of operator+
on
strings is a temporary promise substring, this pattern allows the final
buffer to be given the correct size in a single allocation and it avoids
the copying that must happen when growing the buffer.
Be aware that (at least for now) the functions
NS_ConvertASCIItoUCS2
, NS_ConvertUCS2toUTF8
,
NS_ConvertUTF8toUCS2
, and
NS_LossyConvertUCS2toASCII
are really objects derived from
ns[C]AutoString
and thus copy the buffer into a temporary
buffer. If this buffer is then assigned to an
ns[C][Auto]String
, it will be copied again (at least until
copy-on-write sharing works).
PromiseFlat[C]String
with careSince PromiseFlatString
may (if the string it is
given is not flat) need to create a temporary buffer that will go
away when the promise object goes away,
one must never use the string returned from
PromiseFlatString
after the promise string has been
destroyed.
Bad example:
const PRUnichar *foo = PromiseFlatString(myString).get(); do_Something(foo); // ERROR: foo could point to freed memory
do_Something(PromiseFlatString(myString).get());
Now that gcc 2.7.2.3 is no longer supported, it is also possible to bind the temporary to a reference (which ensures that the lifetime of the temporary is as long as the lifetime of the reference, rather than just the lifetime of the statement) and do something like:
const nsAFlatString &foo = PromiseFlatString(myString); do_Something(foo.get()); do_Something_Else(foo.get());
[ Added 2002-05-14. ]
However, when you bind temporaries to references, you must be careful
not to have any intermediate temporaries that are not bound to
references that are needed by the bound temporary, because their
lifetime is not affected by the binding of the temporary to the
reference. This is unlikely to matter in the case above, with PromiseFlatString
, as
long as you don't use PromiseFlatString
on a string that's
already known to be flat, since PromiseFlatString flattens any non-flat
string immediately, and you're unlikely to see a string of unknown
flatness that's a temporary. However, it does matter when using
operator+
, since it creates a
temporary for each call. Thus the following will fail:
const nsAFlatCString &fullPath = dirString + NS_LITERAL_CSTRING("/") + fileString; do_Something(fullPath); // ERROR: The temporary created by the // concatenation of dirString and // NS_LITERAL_CSTRING("/") has already // been destroyed, but fullPath depends // on it (and on fileString).
(Back to Mozilla Stuff, David Baron)
LDB, dbaron@dbaron.org, 2001-07-07, 2001-09-29, 2001-10-08, 2002-05-14