gecko-dev/xpcom/base/nsIClassInfoImpl.h
Mike Hommey e9c25fa403 Bug 1369622 - Restore static asserts for lack of arguments on some macros. r=froydnj
Bug 1368932 removed MOZ_STATIC_ASSERT_VALID_ARG_COUNT because it didn't
actually work for large numbers of arguments. But it was kind of useful
for macros that expand to something broken when they are given no
variadic argument at all.

Now that we have a macro that doesn't require tricks to count empty
arguments lists, however, we can use straightforward static_asserts
instead of a generic macro, which has the side effect of providing more
context in the error message.

--HG--
extra : rebase_source : 223f85c2c5cc7b3fa8c584b70bb084784fb5764a
2017-06-02 15:07:58 +09:00

181 lines
8.3 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsIClassInfoImpl_h__
#define nsIClassInfoImpl_h__
#include "mozilla/Alignment.h"
#include "mozilla/Assertions.h"
#include "mozilla/MacroArgs.h"
#include "mozilla/MacroForEach.h"
#include "nsIClassInfo.h"
#include "nsISupportsImpl.h"
#include <new>
/**
* This header file provides macros which help you make your class implement
* nsIClassInfo. Implementing nsIClassInfo is particularly helpful if you have
* a C++ class which implements multiple interfaces and which you access from
* JavaScript. If that class implements nsIClassInfo, the JavaScript code
* won't have to call QueryInterface on instances of the class; all methods
* from all interfaces returned by GetInterfaces() will be available
* automagically.
*
* Here's all you need to do. Given a class
*
* class nsFooBar : public nsIFoo, public nsIBar { };
*
* you should already have the following nsISupports implementation in its cpp
* file:
*
* NS_IMPL_ISUPPORTS(nsFooBar, nsIFoo, nsIBar).
*
* Change this to
*
* NS_IMPL_CLASSINFO(nsFooBar, nullptr, 0, NS_FOOBAR_CID)
* NS_IMPL_ISUPPORTS_CI(nsFooBar, nsIFoo, nsIBar)
*
* If nsFooBar is threadsafe, change the 0 above to nsIClassInfo::THREADSAFE.
* If it's a singleton, use nsIClassInfo::SINGLETON. The full list of flags is
* in nsIClassInfo.idl.
*
* The nullptr parameter is there so you can pass a function for converting
* from an XPCOM object to a scriptable helper. Unless you're doing
* specialized JS work, you can probably leave this as nullptr.
*
* This file also defines the NS_IMPL_QUERY_INTERFACE_CI macro, which you can
* use to replace NS_IMPL_QUERY_INTERFACE, if you use that instead of
* NS_IMPL_ISUPPORTS.
*
* That's it! The rest is gory details.
*
*
* Notice that nsFooBar didn't need to inherit from nsIClassInfo in order to
* "implement" it. However, after adding these macros to nsFooBar, you you can
* QueryInterface an instance of nsFooBar to nsIClassInfo. How can this be?
*
* The answer lies in the NS_IMPL_ISUPPORTS_CI macro. It modifies nsFooBar's
* QueryInterface implementation such that, if we ask to QI to nsIClassInfo, it
* returns a singleton object associated with the class. (That singleton is
* defined by NS_IMPL_CLASSINFO.) So all nsFooBar instances will return the
* same object when QI'ed to nsIClassInfo. (You can see this in
* NS_IMPL_QUERY_CLASSINFO below.)
*
* This hack breaks XPCOM's rules, since if you take an instance of nsFooBar,
* QI it to nsIClassInfo, and then try to QI to nsIFoo, that will fail. On the
* upside, implementing nsIClassInfo doesn't add a vtable pointer to instances
* of your class.
*
* In principal, you can also implement nsIClassInfo by inheriting from the
* interface. But some code expects that when it QI's an object to
* nsIClassInfo, it gets back a singleton which isn't attached to any
* particular object. If a class were to implement nsIClassInfo through
* inheritance, that code might QI to nsIClassInfo and keep the resulting
* object alive, thinking it was only keeping alive the classinfo singleton,
* but in fact keeping a whole instance of the class alive. See, e.g., bug
* 658632.
*
* Unless you specifically need to have a different nsIClassInfo instance for
* each instance of your class, you should probably just implement nsIClassInfo
* as a singleton.
*/
class GenericClassInfo : public nsIClassInfo
{
public:
struct ClassInfoData
{
// This function pointer uses NS_CALLBACK_ because it's always set to an
// NS_IMETHOD function, which uses __stdcall on Win32.
typedef NS_CALLBACK_(nsresult, GetInterfacesProc)(uint32_t* aCountP,
nsIID*** aArray);
GetInterfacesProc getinterfaces;
// This function pointer doesn't use NS_CALLBACK_ because it's always set to
// a vanilla function.
typedef nsresult (*GetScriptableHelperProc)(nsIXPCScriptable** aHelper);
GetScriptableHelperProc getscriptablehelper;
uint32_t flags;
nsCID cid;
};
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSICLASSINFO
explicit GenericClassInfo(const ClassInfoData* aData) : mData(aData) {}
private:
const ClassInfoData* mData;
};
#define NS_CLASSINFO_NAME(_class) g##_class##_classInfoGlobal
#define NS_CI_INTERFACE_GETTER_NAME(_class) _class##_GetInterfacesHelper
#define NS_DECL_CI_INTERFACE_GETTER(_class) \
extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class) \
(uint32_t *, nsIID ***);
#define NS_IMPL_CLASSINFO(_class, _getscriptablehelper, _flags, _cid) \
NS_DECL_CI_INTERFACE_GETTER(_class) \
static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \
NS_CI_INTERFACE_GETTER_NAME(_class), \
_getscriptablehelper, \
_flags | nsIClassInfo::SINGLETON_CLASSINFO, \
_cid, \
}; \
mozilla::AlignedStorage2<GenericClassInfo> k##_class##ClassInfoDataPlace; \
nsIClassInfo* NS_CLASSINFO_NAME(_class) = nullptr;
#define NS_IMPL_QUERY_CLASSINFO(_class) \
if ( aIID.Equals(NS_GET_IID(nsIClassInfo)) ) { \
if (!NS_CLASSINFO_NAME(_class)) \
NS_CLASSINFO_NAME(_class) = new (k##_class##ClassInfoDataPlace.addr()) \
GenericClassInfo(&k##_class##ClassInfoData); \
foundInterface = NS_CLASSINFO_NAME(_class); \
} else
#define NS_CLASSINFO_HELPER_BEGIN(_class, _c) \
NS_IMETHODIMP \
NS_CI_INTERFACE_GETTER_NAME(_class)(uint32_t *count, nsIID ***array) \
{ \
*count = _c; \
*array = (nsIID **)moz_xmalloc(sizeof (nsIID *) * _c); \
uint32_t i = 0;
#define NS_CLASSINFO_HELPER_ENTRY(_interface) \
(*array)[i++] = (nsIID*)nsMemory::Clone(&NS_GET_IID(_interface), \
sizeof(nsIID));
#define NS_CLASSINFO_HELPER_END \
MOZ_ASSERT(i == *count, "Incorrent number of entries"); \
return NS_OK; \
}
#define NS_IMPL_CI_INTERFACE_GETTER(aClass, ...) \
static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0, \
"Need more arguments to NS_IMPL_CI_INTERFACE_GETTER"); \
NS_CLASSINFO_HELPER_BEGIN(aClass, MOZ_ARG_COUNT(__VA_ARGS__)) \
MOZ_FOR_EACH(NS_CLASSINFO_HELPER_ENTRY, (), (__VA_ARGS__)) \
NS_CLASSINFO_HELPER_END
#define NS_IMPL_QUERY_INTERFACE_CI(aClass, ...) \
static_assert(MOZ_ARG_COUNT(__VA_ARGS__) > 0, \
"Need more arguments to NS_IMPL_QUERY_INTERFACE_CI"); \
NS_INTERFACE_MAP_BEGIN(aClass) \
MOZ_FOR_EACH(NS_INTERFACE_MAP_ENTRY, (), (__VA_ARGS__)) \
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, MOZ_ARG_1(__VA_ARGS__)) \
NS_IMPL_QUERY_CLASSINFO(aClass) \
NS_INTERFACE_MAP_END
#define NS_IMPL_ISUPPORTS_CI(aClass, ...) \
NS_IMPL_ADDREF(aClass) \
NS_IMPL_RELEASE(aClass) \
NS_IMPL_QUERY_INTERFACE_CI(aClass, __VA_ARGS__) \
NS_IMPL_CI_INTERFACE_GETTER(aClass, __VA_ARGS__)
#endif // nsIClassInfoImpl_h__