An alternative to __if_exists in ATL
__if_not_exists keywords in the Active Template Library (ATL) allow a user to test at compile time whether an identifier exists. If the identifier exists, the associated statement block is executed.
__if_not_exists can be applied to the names of variables, functions, typedefs, as well as other identifiers. You can read more about the specifics of their use and potential pitfalls on MSDN, and in Raymond Chen’s blogpost about the keywords.
Why you shouldn’t use
Although these keywords seem like they’d be useful, they’ve been problematic for years. Also,
__if_not_exists are also incompatible with the recommended compiler
/permissive- switch, which specifies standards-conforming compiler behavior, thus they also can’t be used with the new C++20 modules feature.
Starting with Visual Studio 2022 17.2 Preview 3, because MSVC with
/permissive- (required by modules!) doesn’t support
__if_exists, ATL has a new macro—
_ATL_MODULES—which will replace code that looks like:
with code that looks like:
By default, you can still use
__if_not_exists, and you can use the old ATL code, but if you wish to use the modern
/permissive- compiler mode, you’ll need to switch. Notice that is only supported from c++ 17 and forward.
Changes in ATL Macros
Some ATL Macros were affected by the removal of
__if_exists. You need to be aware of the following changes:
- There are now two versions of
*_WITHOUT_ATL_PROP_NOTIFY_EVENT_CLASS_TYPEDEFshould be used in classes where there is no
*_WITH_ATL_PROP_NOTIFY_EVENT_CLASSversions should be used in classes where there is an
DECLARE_REGISTRY_RESOURCE_ID– These macros have new versions.
WITH_MODULEshould be used where there is a global variable named
_Modulethat is of a subtype of
WITHOUT_MODULEversion should be used when there is no such global variable available.
- V2 versions: In order for the new macros to emulate the original
__if_existsbehavior we need to know the name of the containing class. The previous versions of the macros do so using a non-Standard MSVC-specific extension. The new versions require the user to pass the name of the containing class to the macro. Whenever possible users should use the V2 versions.
BEGIN_ATTRCONNECTION_POINT_MAP– These macros define the typedef
_atl_conn_classtypeif it wasn’t already defined. Because we cannot detect if it already exists, there are now two versions of these macros:
*_WITHOUT_ATL_CONN_CLASSTYPE_TYPEDEFshould be used if there is no
*_WITH_ATL_CONN_CLASSTYPE_TYPEDEFshould be used in classes where there is an
_atl_conn_classtypetypedef already existing
Why you should consume the ATL with the
One of the most important consequences of these changes is that ATL can now be used with header units! Read more about the benefits of Modules, including improved throughput, here (https://learn.microsoft.com/cpp/build/walkthrough-header-units)
Special thanks to Miya Natsuhara, who made all of these changes on the ATL codebase.