|
| 1 | +--- |
| 2 | +title: "/experimental:preprocessor (Enable preprocessor conformance mode)" |
| 3 | +description: "Use the /experimental:preprocessor compiler option to enable experimental compiler support for a standard conforming preprocessor." |
| 4 | +ms.date: "09/03/2019" |
| 5 | +f1_keywords: ["preprocessor", "/experimental:preprocessor"] |
| 6 | +helpviewer_keywords: ["preprocessor conformance", "/experimental:preprocessor", "Enable preprocessor conformance mode"] |
| 7 | +--- |
| 8 | +# /experimental:preprocessor (Enable preprocessor conformance mode) |
| 9 | + |
| 10 | +This option enables an experimental, token-based preprocessor that conforms to C++11 standards, including C99 preprocessor features. |
| 11 | + |
| 12 | +## Syntax |
| 13 | + |
| 14 | +> **/experimental:preprocessor**[**-**] |
| 15 | +
|
| 16 | +## Remarks |
| 17 | + |
| 18 | +Use the **/experimental:preprocessor** compiler option to enable the experimental conforming preprocessor. You can use **/experimental:preprocessor-** option to explicitly specify the traditional preprocessor. |
| 19 | + |
| 20 | +The **/experimental:preprocessor** option is available starting in Visual Studio 2017 version 15.8. |
| 21 | + |
| 22 | +You can detect which preprocessor is in use at compile time. Check the value of the predefined macro [\_MSVC\_TRADITIONAL](../../preprocessor/predefined-macros.md) to tell if the traditional preprocessor is in use. This macro is set unconditionally by versions of the compiler that support it, independent of which preprocessor is invoked. Its value is 1 for the traditional preprocessor. It's 0 for the conformant experimental preprocessor: |
| 23 | + |
| 24 | +```cpp |
| 25 | +#if defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL |
| 26 | +// Logic using the traditional preprocessor |
| 27 | +#else |
| 28 | +// Logic using cross-platform compatible preprocessor |
| 29 | +#endif |
| 30 | +``` |
| 31 | + |
| 32 | +### Behavior changes in the experimental preprocessor |
| 33 | + |
| 34 | +Here are some of the more common breaking changes found when preprocessor conformance mode is enabled: |
| 35 | + |
| 36 | +#### Macro comments |
| 37 | + |
| 38 | +The traditional preprocessor uses character buffers instead of preprocessor tokens. That allows some unusual behavior, such as this preprocessor comment trick, which doesn't work under the conforming preprocessor: |
| 39 | + |
| 40 | +```cpp |
| 41 | +#if DISAPPEAR |
| 42 | +#define DISAPPEARING_TYPE /##/ |
| 43 | +#else |
| 44 | +#define DISAPPEARING_TYPE int |
| 45 | +#endif |
| 46 | + |
| 47 | +// myVal disappears when DISAPPEARING_TYPE is turned into a comment |
| 48 | +// To make standards compliant, wrap the following line with the appropriate #if/#endif |
| 49 | +DISAPPEARING_TYPE myVal; |
| 50 | +``` |
| 51 | + |
| 52 | +#### String prefixes (L#val) |
| 53 | + |
| 54 | +The traditional preprocessor incorrectly combines a string prefix to the result of the [stringizing operator (#)](../../preprocessor/stringizing-operator-hash.md): |
| 55 | + |
| 56 | +```cpp |
| 57 | +#define DEBUG_INFO(val) L"debug prefix:" L#val |
| 58 | +// ^ |
| 59 | +// this prefix |
| 60 | + |
| 61 | +const wchar_t *info = DEBUG_INFO(hello world); |
| 62 | +``` |
| 63 | + |
| 64 | +The `L` prefix is unnecessary here, because the adjacent string literals get combined after macro expansion anyway. The backward compatible fix is to change the definition to: |
| 65 | + |
| 66 | +```cpp |
| 67 | +#define DEBUG_INFO(val) L"debug prefix:" #val |
| 68 | +// ^ |
| 69 | +// no prefix |
| 70 | +``` |
| 71 | + |
| 72 | +This issue is also found in convenience macros that 'stringize' the argument to a wide string literal: |
| 73 | + |
| 74 | +```cpp |
| 75 | +// The traditional preprocessor creates a single wide string literal token |
| 76 | +#define STRING(str) L#str |
| 77 | + |
| 78 | +// Potential fixes: |
| 79 | +// Use string concatenation of L"" and #str to add prefix |
| 80 | +// This works because adjacent string literals are combined after macro expansion |
| 81 | +#define STRING1(str) L""#str |
| 82 | + |
| 83 | +// Add the prefix after #str is stringized with additional macro expansion |
| 84 | +#define WIDE(str) L##str |
| 85 | +#define STRING2(str) WIDE(#str) |
| 86 | + |
| 87 | +// Use concatenation operator ## to combine the tokens. |
| 88 | +// The order of operations for ## and # is unspecified, although all compilers |
| 89 | +// checked perform the # operator before ## in this case. |
| 90 | +#define STRING3(str) L## #str |
| 91 | +``` |
| 92 | +
|
| 93 | +#### Warning on invalid ## |
| 94 | +
|
| 95 | +When the [token-pasting operator (##)](../../preprocessor/token-pasting-operator-hash-hash.md) doesn't result in a single, valid preprocessing token, the behavior is undefined. The traditional preprocessor silently fails to combine the tokens. The new preprocessor matches the behavior of most other compilers and emits a diagnostic. |
| 96 | +
|
| 97 | +```cpp |
| 98 | +// The ## is unnecessary and doesn't result in a single preprocessing token. |
| 99 | +#define ADD_STD(x) std::##x |
| 100 | +
|
| 101 | +// Declare a std::string |
| 102 | +ADD_STD(string) s; |
| 103 | +``` |
| 104 | + |
| 105 | +#### Comma elision in variadic macros |
| 106 | + |
| 107 | +Consider the following example: |
| 108 | + |
| 109 | +```cpp |
| 110 | +void func(int, int = 2, int = 3); |
| 111 | +// This macro replacement list has a comma followed by __VA_ARGS__ |
| 112 | +#define FUNC(a, ...) func(a, __VA_ARGS__) |
| 113 | +int main() |
| 114 | +{ |
| 115 | + // The following macro is replaced with: |
| 116 | + // func(10,20,30) |
| 117 | + FUNC(10, 20, 30); |
| 118 | + |
| 119 | + // A conforming preprocessor replaces the following macro with: |
| 120 | + // func(1, ); |
| 121 | + // which results in a syntax error. |
| 122 | + FUNC(1, ); |
| 123 | +} |
| 124 | +``` |
| 125 | +
|
| 126 | +All major compilers have a preprocessor extension that helps address this issue. The traditional MSVC preprocessor always removes commas before empty `__VA_ARGS__` replacements. The updated preprocessor more closely follows the behavior of other popular cross platform compilers. For the comma to be removed, the variadic argument must be missing, not just empty, and it must be marked with a `##` operator: |
| 127 | +
|
| 128 | +```cpp |
| 129 | +#define FUNC2(a, ...) func(a , ## __VA_ARGS__) |
| 130 | +int main() |
| 131 | +{ |
| 132 | + // The variadic argument is missing in the macro being evoked |
| 133 | + // The comma is removed and replaced with: |
| 134 | + // func(1) |
| 135 | + FUNC2(1); |
| 136 | +
|
| 137 | + // The variadic argument is empty, but not missing. (Notice the |
| 138 | + // comma in the argument list.) The comma isn't removed |
| 139 | + // when the macro is replaced: |
| 140 | + // func(1, ) |
| 141 | + FUNC2(1, ); |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +In the upcoming C++2a standard, this issue has been addressed by adding `__VA_OPT__`, which isn't implemented yet. |
| 146 | + |
| 147 | +#### Macro arguments are 'unpacked' |
| 148 | + |
| 149 | +In the traditional preprocessor, if a macro forwards one of its arguments to another dependent macro, then the argument doesn't get "unpacked" when it's substituted. Usually this optimization goes unnoticed, but it can lead to unusual behavior: |
| 150 | + |
| 151 | +```cpp |
| 152 | +// Create a string out of the first argument, and the rest of the arguments. |
| 153 | +#define TWO_STRINGS( first, ... ) #first, #__VA_ARGS__ |
| 154 | +#define A( ... ) TWO_STRINGS(__VA_ARGS__) |
| 155 | + |
| 156 | +const char* c[2] = { A(1, 2) }; |
| 157 | +// Conformant preprocessor results: |
| 158 | +// const char c[2] = { "1", "2" }; |
| 159 | +// Traditional preprocessor results, all arguments are in the first string: |
| 160 | +// const char c[2] = { "1, 2", }; |
| 161 | +``` |
| 162 | +
|
| 163 | +When expanding `A()`, the traditional preprocessor forwards all of the arguments packaged in `__VA_ARGS__` to the first argument of `TWO_STRINGS`. The variadic argument of `TWO_STRINGS` is empty, which causes the result of `#first` to be `"1, 2"` rather than just `"1"`. You may be wondering what happened to the result of `#__VA_ARGS__` in the traditional preprocessor expansion. if the variadic parameter is empty, it should result in an empty string literal "". Because of a separate issue, the empty string literal token wasn't generated. |
| 164 | +
|
| 165 | +#### Rescanning replacement list for macros |
| 166 | +
|
| 167 | +After a macro is replaced, the resulting tokens are rescanned for additional macro identifiers to replace. The rescan algorithm used by the traditional preprocessor isn't conformant, as shown in this example based on actual code: |
| 168 | +
|
| 169 | +```cpp |
| 170 | +#define CAT(a,b) a ## b |
| 171 | +#define ECHO(...) __VA_ARGS__ |
| 172 | +
|
| 173 | +// IMPL1 and IMPL2 are implementation details |
| 174 | +#define IMPL1(prefix,value) do_thing_one( prefix, value) |
| 175 | +#define IMPL2(prefix,value) do_thing_two( prefix, value) |
| 176 | +// MACRO chooses the expansion behavior based on the value passed to macro_switch |
| 177 | +#define DO_THING(macro_switch, b) CAT(IMPL, macro_switch) ECHO(( "Hello", b)) |
| 178 | +
|
| 179 | +DO_THING(1, "World"); |
| 180 | +// Traditional preprocessor: |
| 181 | +// do_thing_one( "Hello", "World"); |
| 182 | +// Conformant preprocessor: |
| 183 | +// IMPL1 ( "Hello","World"); |
| 184 | +``` |
| 185 | + |
| 186 | +To see what is going on in this example, we break down the expansion starting with `DO_THING`: |
| 187 | + |
| 188 | +`DO_THING(1, "World")` -> |
| 189 | +`CAT(IMPL, 1) ECHO(("Hello", "World"))` |
| 190 | + |
| 191 | +Second, CAT is expanded: |
| 192 | + |
| 193 | +`CAT(IMPL, 1)` -> `IMPL ## 1` -> `IMPL1` |
| 194 | + |
| 195 | +Which puts the tokens into this state: |
| 196 | + |
| 197 | +`IMPL1 ECHO(("Hello", "World"))` |
| 198 | + |
| 199 | +The preprocessor finds the function-like macro identifier `IMPL1`, but it's not followed by a "(", so it's not considered a function-like macro invocation. It moves on to the following tokens and finds the function-like macro `ECHO` invoked: |
| 200 | + |
| 201 | +`ECHO(("Hello", "World"))` -> `("Hello", "World")` |
| 202 | + |
| 203 | +`IMPL1` is never considered again for expansion, so the full result of the expansions is: |
| 204 | + |
| 205 | +`IMPL1("Hello", "World");` |
| 206 | + |
| 207 | +The macro can be modified to behave the same way under both the experimental preprocessor and the traditional preprocessor. The solution is to add another layer of indirection: |
| 208 | + |
| 209 | +```cpp |
| 210 | +#define CAT(a,b) a##b |
| 211 | +#define ECHO(...) __VA_ARGS__ |
| 212 | + |
| 213 | +// IMPL1 and IMPL2 are macros implementation details |
| 214 | +#define IMPL1(prefix,value) do_thing_one( prefix, value) |
| 215 | +#define IMPL2(prefix,value) do_thing_two( prefix, value) |
| 216 | + |
| 217 | +#define CALL(macroName, args) macroName args |
| 218 | +#define DO_THING_FIXED(a,b) CALL( CAT(IMPL, a), ECHO(( "Hello",b))) |
| 219 | + |
| 220 | +DO_THING_FIXED(1, "World"); |
| 221 | +// macro expanded to: |
| 222 | +// do_thing_one( "Hello", "World"); |
| 223 | +``` |
| 224 | +
|
| 225 | +### Conformance mode conformance |
| 226 | +
|
| 227 | +The experimental preprocessor isn't complete yet, and some preprocessor directive logic still falls back to the traditional behavior. Here is a partial list of incomplete features: |
| 228 | +
|
| 229 | +- Support for `_Pragma` |
| 230 | +- C++20 features |
| 231 | +- Additional diagnostic improvements |
| 232 | +- Switches to control the output under /E and /P |
| 233 | +- Boost blocking bug: Logical operators in preprocessor constant expressions aren't fully implemented in the new preprocessor. On some `#if` directives, the new preprocessor can fall back to the traditional preprocessor. The effect is only noticeable when macros that are incompatible with the traditional preprocessor get expanded, which can happen when building Boost preprocessor slots. |
| 234 | +
|
| 235 | +### To set this compiler option in the Visual Studio development environment |
| 236 | +
|
| 237 | +1. Open the project's **Property Pages** dialog box. For details, see [Set C++ compiler and build properties in Visual Studio](../working-with-project-properties.md). |
| 238 | +
|
| 239 | +1. Select the **Configuration Properties** > **C/C++** > **Command Line** property page. |
| 240 | +
|
| 241 | +1. Modify the **Additional Options** property to include **/experimental:preprocessor** and then choose **OK**. |
| 242 | +
|
| 243 | +## See also |
| 244 | +
|
| 245 | +[/Zc (Conformance)](zc-conformance.md) |
0 commit comments