C++ Keywords: __identifier

Syntax

__identifier ( token )

It is an error (C2760) to follow __identifier with any sequence of tokens except a left parenthesis, an arbitrary token and a right parenthesis.

If the token is not one of various acceptable keywords (see below), it must be either an identifier or a string literal. Anything else is an error (C2745). Note that any text that tokenises as an unacceptable keyword causes this error, even if the text is otherwise valid as a C++ identifier.

Depending on such things as prevailing command-line options (especially on /Ze), some of the acceptable keywords may have multiple forms as text, most notably with different numbers of leading underscores, that are all defined as keywords and which tokenise indistinguishably. Provision of such a keyword as the token causes a warning (C4932).

Behaviour

The __identifier keyword acts as an operator in the token stream. It takes as its one argument a token, which it returns re-tokenised as an identifer. The sequence of tokens from __identifier up to and including the closing parenthesis is replaced in the token stream by the one token, but now converted to an identifier.

Keywords

This conversion is especially useful for text that is valid as a C++ identifier but is recognised as special to the compiler, such that tokenisation renders it as one of the many tokens that are each dedicated to some keyword, rather than as an identifier token. A side-effect of this distinct tokenisation of keywords is that the text of a keyword is not ordinarily available as an identifier, e.g., for labelling code or data. The practical effect of the __identifier construction is to make the text usable as an identifier after all.

That said, there are several caveats.

First, the keyword is tokenised before __identifier gets to work on it. Errors and warnings that arise from this tokenisation are not avoided and may affect the behaviour, and indeed the capability, of __identifier. This applies particularly to disabled keywords. For example,

int __identifier (near);

does not even get to try converting near to an identifier, because the tokenisation of near causes a warning (C4226) that the keyword is obsolete, and the keyword is then discarded from the token stream.

Identifiers

The re-tokenisation performed by __identifier is trivial if the argument is already tokenised as an identifier.

The product documentation notes that “use of the __identifier keyword for identifiers that are not keywords is permitted, but strongly discouraged as a matter of style.” This admonition might carry more weight were Microsoft better at documenting all the circumstances in which a given identifier may or may not happen to be defined as a keyword. Consider for instance: when is bool an identifier, such that its use with __identifier is strongly discouraged style? The product documentation is clear enough that the /noBool option leaves bool as an identifier, but you will look a long time to find that /Ze does too.

String Literals

Conversion is nearly trivial if the given token is a string literal. The practical consequences, however, are arguably not trivial at all, since this mechanism allows the construction of identifiers that violate the standard restrictions on acceptable characters. For instance, the following compiles without complaint:

char * __identifier ("Function with unusual name") (void)
{
    return __FUNCDNAME__;
}

as does

#include    <stdio.h>

extern char * __identifier ("Function with unusual name") (void);

void main (void)
{
    char *p = __identifier ("Function with unusual name") ();
    printf ("Decorated name is:  %s\n", p);
}

and the two object files even link without complaint, there being evidently no problem to either the compiler or linker to work with a symbol (here, the decorated name ?Function with unusual name@@YAPADXZ) that contains spaces.

This unusual effect from allowing a string literal as the argument for __identifier may be why the support is not mentioned by the product documentation. That said, the support is no accident or oversight: it is explicitly provided for in the compiler’s code.

Indeed, this undocumented support for string literals provides a way to produce an identifier from a keyword whose bare use with __identifier causes an error or warning. For instance, imagine a programmer who is concerned that source code may be compiled with different options once it gets into the hands of others, e.g., clients or students. Alternatively, imagine that this programmer is actually required (by company policy or the specification of a client) to support compilation with /Za. Now suppose that this programmer’s particular work item is to use object code that defines a function named compl, with some such definition as

void compl (void);

provided in a header file. The use of compl to name a function is a problem because the /Za option turns compl into a keyword. The object code, supplied in a library, was written elsewhere and is unchangeable, as is the name of the compl function, but the header can be edited. A rewrite to

void __identifier (compl) (void);

though consistent with the product documentation, is no solution, because compl, as a keyword, tokenises to an operator and is not convertible to an identifier. However, the undocumented

void __identifier ("compl") (void);

fits the bill perfectly.

Note that the preceding example does not differ very much from the scenario proposed in the product documentation. There, the function that must be accessed in unchangeable code has the name of a C++ keyword because it was written in another language, not because it was written in C++ but without concern for being compiled under an option that Microsoft perhaps provides only grudgingly.

Documentation Status

The product documentation states that “the main purpose of this keyword is to allow managed classes to access and use external classes that may use a C++ keyword as an identifier” and indeed, the page that the documentation offers as details for this keyword is placed in the Managed Extensions for C++ Reference.

As actually implemented in the product, the __identifier keyword has no particular dependence on the use of managed code. The accompanying documentation’s only acknowledgement of this seems to be the absence of the footnote “Applicable to Managed Extensions for C++ only” where the keyword appears in the master list of C++ Keywords in the C++ Language Reference.