nsIClassInfo
OverviewnsIClassInfo
?
QueryInterface
rules?nsIClassInfo
?
nsIClassInfo
is an interface that provides information
about a specific implementation class, to wit:
To get the nsIClassInfo
object for the implementation behind
a given interface, simply QueryInterface
for the nsIClassInfo
interface:
C++:
nsCOMPtr<nsIClassInfo> info = do_QueryInterface(ifacePtr);
JavaScript:
var info = ifacePtr.QueryInterface(Components.interfaces.nsIClassInfo);
It's important to note that this will usually return a singleton
object: there often exists only one nsIClassInfo implementation for
all implementations of a given class. This is very important, because it
means that you can't QueryInterface
back to the original object,
no matter how hard you try.
QueryInterface
rules?Quite. As discussed in
bug 67699, though, it's a reasonable concession to make in order
to avoid an additional vtbl entry on every class that wants to export
nsIClassInfo
data.
The language helpers are basically hooks for alternate language bindings to use for providing language-specific wrapping and manipulation behaviour, without adding code to every wrapped object. In bug 67669, jband tells us what XPConnect does with the language helpers:
See bug 67699 for more interesting discussion about pluggable language helpers and decoupling them from specific wrapped classes.When wrapping an object xpconnect QIs the object for its classinfo (say 'foo'). If it has seen that foo classinfo pointer before then it knows that this object is a foo and it can reuse all the info is knows about foo objects. If it has never seen foo it will gather info (such as the JS helper) and remember that for future wrappings of foo objects.
Assuming that the foo helper tells xpconnect to not bother QI'ing each foo instance for a per instance helper (or if the instances don't respond to QI for the helper) then the same foo helper is used on all calls from JS relating to the foo instances.
What you may be missing is that methods on the helper (nsIXPCScriptable in the xpconnect case) *all* receive a pointer to the instance wrapper when called. I.e. the helper methods have an explicit 'self' param. This allows the helper to customize its response for each method call without requiring a helper per instance.
Why, yes, it is useful. To provide nsIClassInfo
data for your class, you need to ensure that it returns an nsIClassInfo
-implementing object when it is QueryInterface
d for
nsIClassInfo
. Simple enough, but it can be even simpler through
the use of a handful of helper macros/functions. Choose your own adventure:
nsModuleComponentInfo
.First, make sure that your class has the nsIClassInfo
helpers, by changing the NS_IMPL_ISUPPORTS
line:
NS_IMPL_ISUPPORTS2(nsSampleImpl, nsISample, nsIOther)
becomes
NS_IMPL_ISUPPORTS2_CI(nsSampleImpl, nsISample, nsIOther)
This will provide an implementation of a helper function, named
nsSampleImpl_GetInterfacesHelper
, which handles the processing
of nsIClassInfo::getInterfaces
.
Next, in your module code, use NS_DECL_CLASSINFO
to
provide the rest of the per-class infrastructure (a global pointer into
which to stash the nsIClassInfo
object, and an extern declaration
of the interfaces-helper, in case it's defined in another file):
NS_DECL_CLASSINFO(nsSampleImpl)
You'll need one of these lines for each nsIClassInfo
-supporting class represented in your nsModuleComponentInfo
array.
Lastly, fill in the appropriate members of nsModuleComponentInfo
to wire everything up:
static nsModuleComponentInfo components[] =
{
{
"Sample Component", NS_SAMPLE_CID, NS_SAMPLE_CONTRACTID,
nsSampleImplConstructor,
nsSampleRegistrationProc,
nsSampleUnregistrationProc,
nsnull /* no factory destructor */,
NS_CI_INTERFACE_GETTER_NAME(nsSampleImpl), /* interface getter */
nsnull /* no language helper */,
&NS_CLASSINFO_NAME(nsSampleImpl), /* global class-info pointer */
0 /* no class flags */
}
};
If you want to add a callback which provides language-helper
objects, replace the last nsnull
with your callback. See the
nsIClassInfo
IDL file for details on language helpers and
other esoterica.
Note: the details of these macros may change
slightly over the next week or so, as we refine a system for using table-driven
QI and sharing data between QI and the class-info calls.
You need some utility macros, don't ya? We're working
on it. (You should really use the generic factory and module, though.
See
nsUConvModule.cpp for an example of how to use nsModuleComponentInfo
and the generic modules even when you have highly-custom registration
requirements.)
Interface flattening is a way to present multiple interfaces
of the same object to a language binding, without requiring explicit
QueryInterface
calls. Consider the following interfaces and
an object obj
that implements both of them:
interface nsIFoo : nsISupports {
void fooMeth(in string message);
};
interface nsIBar : nsISupports {
void barMeth(in PRUint32 meaning);
};
You could use the following code to call both fooMeth and
barMeth without any QueryInterface:
obj.fooMeth("welcome to foo");
obj.barMeth(42);
Pretty, no? Pretty, yes.
There are also intricacies related to conflicting method
names and the difference between interface sets that are part of a contract's
promise and those which are simply artifacts of the implementation, but they're
beyond the scope of this overview.