February 28th, 2023

An alternative to __if_exists in ATL

What are _if_exists and __if_not_exists

The __if_exists and __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_exists and __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 _if_exists or __if_not_exists

Although these keywords seem like they’d be useful, they’ve been problematic for years.  Also, _if_exists and __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.

Alternatives to __if_exists and __if_not_exists

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:

__if_exists(m_nAccessors)

with code that looks like:

if constexpr(_Has_m_nAccessors<_Self>::value)

By default, you can still use __if_exists and __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 BEGIN_PROPERTY_MAP and BEGIN_PROP_MAP: *_WITHOUT_ATL_PROP_NOTIFY_EVENT_CLASS_TYPEDEF should be used in classes where there is no _ATL_PROP_NOTIFY_EVENT_CLASS typedef; *_WITH_ATL_PROP_NOTIFY_EVENT_CLASS versions should be used in classes where there is an _ATL_PROP_NOTIFY_EVENT_CLASS typedef.
  • DECLARE_REGISTRY_RESOURCE, DECLARE_REGISTRY_RESOURCE_ID – These macros have new versions.
    • WITH_MODULE should be used where there is a global variable named _Module that is of a subtype of CComModule
    • WITHOUT_MODULE version should be used when there is no such global variable available.
    • V2 versions: In order for the new macros to emulate the original __if_exists behavior 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_CONNECTION_POINT_MAP, BEGIN_ATTRCONNECTION_POINT_MAP – These macros define the typedef _atl_conn_classtype if 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_TYPEDEF should be used if there is no _atl_conn_classtype typedef
    • *_WITH_ATL_CONN_CLASSTYPE_TYPEDEF should be used ­in classes where there is an _atl_conn_classtype typedef already existing

Why you should consume the ATL with the _ATL_MODULES flag

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.

Category
C++

0 comments

Discussion are closed.