PreProcessor Commands: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Text replacement - "{{Stub}}" to "{{Wiki|stub}}")
m (Text replacement - "{{Feature|Warning|" to "{{Feature|warning|")
 
(46 intermediate revisions by 8 users not shown)
Line 1: Line 1:
{{TOC|side|0.9}}
{{TOC|side|0.9}}
The parser allows you to use macros in configs. The purpose of macros is to re-use the same definition in scripts and configs multiple times. It also gives you a centralized place to correct errors in this definition.
The PreProcessor and the Config Parser allow to use macros in configs. The purpose of macros is to re-use the same definition in scripts and configs multiple times.
If you really want to dig into the depths of the preprocessor you'll need to confront it with a bunch of edge cases and see how it behaves. If you are interested in this kind of thing, you might want to have a look at the collection of test-cases linked at the end of this page.<br>
It also gives a centralized place to correct errors in this definition.
{{Feature|arma3 | In {{arma3}}, preprocessor commands are <b>case-sensitive!</b>}}
Know that edge cases may arise - see the collection of test-cases linked at the end of this page.
{{Feature|arma3|In {{arma3}}, preprocessor commands are '''case-sensitive!'''}}
 


== Parsing ==
== Parsing ==
See {{Link|#Config Parser}} below:
* '''[[Config.cpp]]''' - parsed when PBO is binarized.
* '''[[Config.cpp]]''' - parsed when PBO is binarized.
** [[localize]] cannot be used in macros, as it would hardcode string of current language instead of creating reference.
** [[localize]] cannot be used in macros, as it would hardcode string of current language instead of creating reference.
* '''[[Description.ext]]''' - parsed when mission is loaded or previewed in missions list.
* '''[[Description.ext]]''' - parsed when mission is loaded or previewed in missions list.
* '''[[SQF]] script''' - parsed when [[preprocessFile]], [[preprocessFileLineNumbers]] or [[execVM]] is used.
* '''[[SQF Syntax|SQF]] script''' - parsed when [[preprocessFile]], [[preprocessFileLineNumbers]], [[compileScript]] or [[execVM]] is used.
 


== Macros ==
== Macros ==
=== Comments ===
=== Comments ===
A comment is a line within code that is not actually processed by the game engine. They are used to make code more readable or to add notes for future reference. The preprocessor removes all comments from the file before it is processed. Therefore, comments are never actually "seen" by the game engine.


{{cc|this is a single-line comment}}
A comment is a line within code that is not actually processed by the game engine. They are used to make code more readable or to add notes for future reference.
The preprocessor removes all comments from the file before it is processed. Therefore, comments are never actually "seen" by the game engine.
{{Feature|warning|Sometimes a problematic string can fail to purge these comment outs during preprocessing!
<sqf>private _a = '"';
// this is just a comment, but failed to remove upon the preprocess so will return an error. Caused by that string</sqf>}}
<syntaxhighlight lang="cpp">
// this is a single-line comment
   
   
{{codecomment|/* this is a
/*
multi-line
this is a
comment */}}
multi-line
comment
*/
   
   
mycode = something; {{cc|only this part of the line is commented out}}
myValue = something; // only this part of the line is commented out
 
myArray = ["apple"{{codecomment|/*,"banana*/}},"pear"]; {{cc|// a portion in the middle of this line is commented out}}
myArray = { "apple"/*, "banana"*/, "pear" }; // a portion in the middle of this line is commented out
</syntaxhighlight>


=== #define ===
=== #define ===


Using the ''#define'' instruction, you can define a keyword and assign a definition to it. The keyword may contain any letter, digit or underscore in arbitrary order, as long as it doesn't start with a digit (RegEx: <tt>[a-zA-Z_][0-9a-zA-Z_]*</tt>). As an example:
Using the {{hl|#define}} instruction, it is possible to define a keyword and assign a definition to it.
The keyword may contain any letter, digit or underscore in arbitrary order, as long as it does not start with a digit (RegEx: {{hl|[a-zA-Z_][0-9a-zA-Z_]*}}).
As an example:
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#define true 1
#define true 1
Line 49: Line 65:


==== Arguments ====
==== Arguments ====
You can add arguments to more complex macros, by including them between brackets after the keyword. For the name of the arguments the same rule as for the macro-keyword (see above) apply.
Arguments can be added to more complex macros, by including them between brackets after the keyword. For the name of the arguments the same rule as for the macro-keyword (see above) apply.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#define CAR(NAME) displayName = NAME;
#define CAR(NAME) displayName = NAME;
</syntaxhighlight>
</syntaxhighlight>


If you now use ''CAR("Mini")'', this will be replaced with ''displayName = "Mini";''. Multiple arguments can also be used:
If ''CAR("Mini")'' is used, it will be replaced with ''displayName = "Mini";''. Multiple arguments can also be used:
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#define BLASTOFF(UNIT,RATE) UNIT setVelocity [0,0,RATE];
#define BLASTOFF(UNIT,RATE) UNIT setVelocity [0,0,RATE];
</syntaxhighlight>
</syntaxhighlight>


Macro arguments may be composed of any characters, as long as they do not contain a comma (because commas are used as argument-delimiters). If quotes are being used, they have to be balanced. The same applies to single-quotes This is because String detection is working in macro arguments - Therefore you can even pass in commas as a macro argument as long as they are part of a String (This only works with Strings wrapped in double-quotes though). Note however that although the macro gets resolved properly, the comma gets removed from the String (probably a bug).
Macro arguments may be composed of any characters, as long as they do not contain a comma (because commas are used as argument-delimiters).
If quotes are being used, they have to be balanced.
The same applies to single-quotes; this is because String detection is working in macro arguments - therefore commas can even get passed in as a macro argument as long as they are part of a String (This only works with Strings wrapped in double-quotes though).
Note however that although the macro gets resolved properly, the comma gets removed from the String (probably a bug).
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#define MACRO(arg) arg
#define MACRO(arg) arg
Line 71: Line 90:
</syntaxhighlight>
</syntaxhighlight>


Passing arrays with more than one element <tt>[el1,el2,...]</tt> as arguments into macros as well as any argument containing comas <tt>"some, sentence"</tt>, will need a small workaround:
Passing arrays with more than one element {{hl|[el1,el2,...]}} as arguments into macros as well as any argument containing comas {{hl|"some, sentence"}}, will need a small workaround:
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#define HINTARG(ARG) hint ("Passed argument: " + str ARG)
#define HINTARG(ARG) hint ("Passed argument: " + str ARG)
Line 95: Line 114:


==== Replacing parts of words ====
==== Replacing parts of words ====
By default, you can only replace whole words by arguments. If you need to replace only part of a word, you can use the ''##'' instruction. This is necessary when either the start or the end of the argument connects to another character that is not a ''';''' (semi-colon) or &nbsp; (space).
By default, only whole words can be replaced by arguments. If only a part of the word should be replaced, use the {{hl|##}} instruction.
This is necessary when either the start or the end of the argument connects to another character that is not a {{hl|;}} (semi-colon) or {{hl|&nbsp;}} (space).
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
class NAME##_Button_Slider: RscText \
class NAME##_Button_Slider: RscText \
Line 102: Line 122:
</syntaxhighlight>
</syntaxhighlight>


You can also use the single ''#'' to convert an argument to a string.
It is also possible to use the single ''#'' to convert an argument to a string.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
statement = (this animate [#SEL, 0]); \
statement = (this animate [#SEL, 0]); \
Line 108: Line 128:


==== Multi-line ====
==== Multi-line ====
For longer definitions, you can stretch the macro across multiple lines. To create a multi-line definition, each line except the last one should end with a ''\'' character:
For longer definitions, one can stretch the macro across multiple lines. To create a multi-line definition, each line except the last one should end with a ''\'' character:
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#define DRAWBUTTON(NAME)\
#define DRAWBUTTON(NAME)\
__EXEC(idcNav = idcNav + 4) \
__EXEC(idcNav = idcNav + 4) \
...
// ...
</syntaxhighlight>
</syntaxhighlight>


{{Informative | The backslash must be the last character in a line when defining a multi-line macro. Any character (including spaces) after the backslash will cause issues.}}
{{Feature|informative|The backslash must be the last character in a line when defining a multi-line macro. Any character (including spaces) after the backslash will cause issues.}}


=== #undef ===
=== #undef ===
Undefine (delete) a macro previously set by the use of #define.
Undefine (delete) a macro previously set by the use of #define.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#undef NAME
#undef NAME
</syntaxhighlight>
=== #if  ===
'''Description:''' Checks condition and processes content if condition returns 1. Skips content if it returns 0
<syntaxhighlight lang="cpp">
#if __A3_DEBUG__ // Check if game is started in debug mode
#include "debug.hpp" // Include some file if debug mode is enabled
#endif
</syntaxhighlight>
==== Comparison Operators ====
The following operations are supported:
* <
* >
* <=
* >=
* ==
* !=
'''Example:'''
<syntaxhighlight lang="cpp">
#if __GAME_VER_MAJ__ != 2
// Do something...
#endif
</syntaxhighlight>
</syntaxhighlight>


=== #ifdef ===
=== #ifdef ===
You can use a simple if-then construction to check whether a certain set of definitions has already been made:
 
A simple if-then construction to check whether a certain set of definitions has already been made:
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#ifdef NAME
#ifdef NAME
...text that will be used if NAME is defined...
// config that will be used if NAME is defined
#endif
#endif
</syntaxhighlight>
</syntaxhighlight>
Line 134: Line 180:


=== #ifndef ===
=== #ifndef ===
Same as #ifdef, but checks for absence of definition instead.
Same as #ifdef, but checks for absence of definition instead.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#ifndef NAME
#ifndef NAME
...text that will be used if NAME ''isn't'' defined...
// config that will be used if NAME is -not- defined
#endif
#endif
</syntaxhighlight>
</syntaxhighlight>


=== #else ===
=== #else ===
Provides alternative code to {{hl|#if}}, {{hl|#ifdef}}, {{hl|#ifndef}} checks.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#ifndef NAME
#ifndef NAME
...text that will be used if NAME is -not- defined...
// config that will be used if NAME is -not- defined
#else
#else
...text that will be used if NAME -is- defined...
// config that will be used if NAME -is- defined
#endif
#endif
</syntaxhighlight>
</syntaxhighlight>


=== #endif ===
=== #endif ===
This ends a conditional block as shown in the descriptions of #ifdef and #ifndef above.
This ends a conditional block as shown in the descriptions of #ifdef and #ifndef above.


=== #include ===
=== #include ===
Copies the code from a target file and pastes it where #include directive is.
Copies the code from a target file and pastes it where #include directive is.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
Line 162: Line 213:
Source directory is:
Source directory is:
* For any file without starting the include path with \ - the file's current directory
* For any file without starting the include path with \ - the file's current directory
* When starting with \ - the internal filesystem root (see [[CMA:DevelopmentSetup#Addon_development|Addon Development]]) or the Game's working directory (only with [[Arma 3 Startup Parameters|-filePatching]] enabled)
* When starting with \ - the internal filesystem root (see [[CMA:DevelopmentSetup#Addon_development|Addon Development]]) or the Game's working directory (only with [[Arma 3: Startup Parameters|-filePatching]] enabled)


You can define a path beginning with:
A path beginning can be defined as follow:
* drive (only with [[Arma 3 Startup Parameters|-filePatching]] enabled):<!--
* drive (only with [[Arma 3 Startup Parameters|-filePatching]] enabled): <syntaxhighlight lang="cpp">#include "D:\file.txt"</syntaxhighlight>
--><syntaxhighlight lang="cpp">#include "D:\file.txt"</syntaxhighlight><!--
* PBO with [[PBOPREFIX]]: <syntaxhighlight lang="cpp"> #include "\myMod\myAddon\file.txt"</syntaxhighlight>
-->
* PBO with [[PBOPREFIX]]:<!--
--><syntaxhighlight lang="cpp"> #include "\myMod\myAddon\file.txt"</syntaxhighlight>
* PBO (keep in mind that in this case, if the PBO's file name will be changed, all '#include' referencing it will need to be updated):<!--
* PBO (keep in mind that in this case, if the PBO's file name will be changed, all '#include' referencing it will need to be updated):<!--
--><syntaxhighlight lang="cpp">#include"\myMod\myAddon\file.txt" // Arma 3\@myMod\addons\myAddon.pbo\file.txt;</syntaxhighlight>
--><syntaxhighlight lang="cpp">#include"\myMod\myAddon\file.txt" // Arma 3\@myMod\addons\myAddon.pbo\file.txt;</syntaxhighlight>


To move to parent directory use '..' (two dots) (Supported in Arma 3 since v1.49.131707):
To move to parent directory use '..' (two dots) (Supported since {{GVI|arma3|1.50}}):
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#include "..\file.sqf"
#include "..\file.sqf"
Line 185: Line 233:


=== # ===
=== # ===
'#' (single hash) operator wraps the text with quotation marks.
'#' (single hash) operator wraps the text with quotation marks.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#define STRINGIFY(s) #s;
#define STRINGIFY(s) #s
#define FOO 123
#define FOO 123
test1 = STRINGIFY(123); //test1 = "123";
test1 = STRINGIFY(123); //test1 = "123";
Line 193: Line 242:
</syntaxhighlight>
</syntaxhighlight>


This operator does only work on keywords following the rules for macro-names (see <tt>#define</tt>-section). If one wants to stringify anything else (like e.g. a number), one has to use a stringify-macro that takes an argument and stringifies that (as in the example above).
This operator does only work on keywords following the rules for macro-names (see {{hl|#define}}-section).
If one wants to stringify anything else (like e.g. a number), one has to use a stringify-macro that takes an argument and stringifies that (as in the example above).
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#define MACRO Test #123
#define MACRO Test #123
Line 200: Line 250:


=== ## ===
=== ## ===
'##' (double hash) operator concatenates what's before the ## with what's after it.
'##' (double hash) operator concatenates what's before the ## with what's after it.
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
Line 209: Line 260:
</syntaxhighlight>
</syntaxhighlight>


=== __EXEC ===
=== __LINE__ ===
This '''config parser''' macro allows you to assign values to internal variables or just execute arbitrary code. The code inside <tt>__EXEC</tt> macros runs in [[parsingNamespace]] and variables defined in it will also be created in [[parsingNamespace]]. The variables can then be used to create more complex macros:
 
This keyword gets replaced with the line number in the file where it is found. For example, if {{hl|__LINE__}} is found on the 10th line of a file, the word {{hl|__LINE__}} will be replaced with the number 10.
 
=== __FILE__ ===
 
This keyword gets replaced with the CURRENT file being processed.


__EXEC(cat = 5 + 1;)
{{ArgTitle|3|__has_include|{{GVI|arma3|2.02}}}}
__EXEC(lev = cat - 2;)
_cat = [[parsingNamespace]] [[getVariable]] "cat"; {{cc|6}}
_lev = [[parsingNamespace]] [[getVariable]] "lev"; {{cc|4}}


{{Warning | <tt>__EXEC</tt> doesn't like round brackets <tt>()</tt> inside expressions. If you need to have grouping, perhaps you could calculate values inside the brackets separately and assign to local variables:
'''Description:''' Checks if given file exists. Returns 1 if it does, 0 if it does not exist.
<code>__EXEC(a {{=}} (1+2);) {{cc|ERROR}}</code>
{{Feature|important|
<div><code>__EXEC(_expr {{=}} 1+2;)
* The file path must start with the backslash {{hl|\}} otherwise it would fail silently (returns {{hl|0}} so does nothing unless you put something in {{hl|#else}})!
__EXEC(a {{=}} _expr;) {{cc|OK}}</code></div>
* If you wanted to make an addon that can change its config dynamically depending on mods that loaded along, '''do not''' binarize the ''config.cpp''!
}}
}}
{{Feature|informative|
To validate that the file exists in your game, you can use [[fileExists]] command to check.
<sqf>
fileExists "\a3\data_f\config.bin"; // returns true
fileExists "\z\ace\addons\main\script_component.hpp"; // returns true if ACE is loaded, false otherwise
</sqf>
}}
<syntaxhighlight lang="cpp">
#if __has_include("\z\ace\addons\main\script_component.hpp")
// file exists! Do something with it
#else
// file does not exist
#endif
</syntaxhighlight>
{{ArgTitle|3|__DATE_ARR__|{{GVI|arma3|2.02}}}}


=== __EVAL ===
'''Description:''' Is replaced for current date in format [[Array|array]].
With this '''config parser''' macro you can evaluate expressions, including previously assigned internal variables. Unlike with <tt>__EXEC</tt>, <tt>__EVAL</tt> supports multiple parentheses
<syntaxhighlight lang="cpp">
__DATE_ARR__ // 2020,10,28,15,17,42
</syntaxhighlight>
 
{{ArgTitle|3|__DATE_STR__|{{GVI|arma3|2.02}}}}


w = __EVAL([[safeZoneW]] - (5 * ((1 / ([[getResolution]] [[select]] 2)) * 1.25 * 4)));
'''Description:''' Is replaced for current date in format [[String|string]].
<syntaxhighlight lang="cpp">
__DATE_STR__  // "2020/10/28, 15:17:42"
</syntaxhighlight>


<tt>__EVAL</tt> macros '''must''' be assigned to a config property and the expression '''must''' be terminated with <tt>;</tt>. <tt>__EVAL</tt> can only return [[Number]] or [[String]]: . Any other type is represented as [[String]], even [[Boolean]] type, which will result in either <tt>"true"</tt> or <tt>"false"</tt>.
{{ArgTitle|3|__DATE_STR_ISO8601__|{{GVI|arma3|2.02}}}}


{{Warning | <tt>__EVAL</tt> doesn't like curly brackets <tt>{}</tt>, if you need to have code in your expression use [[compile]] [[String]] instead:
'''Description:''' Is replaced for current date in format [[String|string]]. The date is presented in ISO8601 standard.
<code>result <nowiki>=</nowiki> __EVAL([[call]] {123}); {{cc|ERROR}}</code>
<syntaxhighlight lang="cpp">
<code>result <nowiki>=</nowiki> __EVAL([[call]] [[compile]] "123"); {{cc|OK}}</code>}}
__DATE_STR_ISO8601__ // "2020-10-28T14:17:42Z"
</syntaxhighlight>


{{Important|<tt>__EXEC</tt> and <tt>__EVAL</tt> macros are not suitable for SQF/SQS scripts but can be used in configs, including [[description.ext]]. <br>Both global and local variables set in <tt>__EXEC</tt> are available in <tt>__EVAL</tt>}}
{{ArgTitle|3|__TIME__|{{GVI|arma3|2.02}}}}


=== __LINE__ ===
'''Description:''' Is replaced for current time.
This keyword gets replaced with the line number in the file where it is found. For example, if __LINE__ is found on the 10th line of a file, the word __LINE__ will be replaced with the number 10.
<syntaxhighlight lang="cpp">
__TIME__ // 15:17:42
</syntaxhighlight>


=== __FILE__ ===
{{ArgTitle|3|__TIME_UTC__|{{GVI|arma3|2.02}}}}
This keyword gets replaced with the CURRENT file being processed.


{{Feature|arma3| The following macros are available since {{arma3}} 2.01.}}
'''Description:''' Is replaced for UTC time.
<syntaxhighlight lang="cpp">
__TIME_UTC__ // 14:17:42
</syntaxhighlight>


=== __has_include  ===
{{ArgTitle|3|__DAY__|{{GVI|arma3|2.16}}}}
'''Description:''' Checks if given file exists.
#if __has_include("\z\ace\addons\main\script_component.hpp")
{{cc|File exists! Do something with it}}
#else
{{cc|File does not exist}}
#endif


=== __DATE_ARR__  ===
'''Description:''' Is replaced for UTC day.
'''Description:''' Is replaced for current date in format [[Array|array]].
<syntaxhighlight lang="cpp">
__DATE_ARR__ {{cc|2020,10,28,15,17,42}}
__DAY__ // 20
</syntaxhighlight>


=== __DATE_STR__ ===
{{ArgTitle|3|__MONTH__|{{GVI|arma3|2.16}}}}
'''Description:''' Is replaced for current date in format [[String|string]].
__DATE_STR__  {{cc|"2020/10/28, 15:17:42"}}


=== __DATE_STR__ ===
'''Description:''' Is replaced for UTC month.
'''Description:''' Is replaced for current date in format [[String|string]].
<syntaxhighlight lang="cpp">
__DATE_STR__ {{cc|"2020/10/28, 15:17:42"}}
__MONTH__ // 11
</syntaxhighlight>


=== __DATE_STR_ISO8601__  ===
{{ArgTitle|3|__YEAR__|{{GVI|arma3|2.16}}}}
'''Description:''' Is replaced for current date in format [[String|string]]. The date is presented in ISO8601 standard.
__DATE_STR_ISO8601__ {{cc|"2020-10-28T14:17:42Z"}}


=== __TIME__ ===
'''Description:''' Is replaced for UTC year.
'''Description:''' Is replaced for current time.
<syntaxhighlight lang="cpp">
__TIME__ {{cc|15:17:42}}
__YEAR__ // 2023
</syntaxhighlight>


=== __TIME_UTC__ ===
{{ArgTitle|3|__COUNTER__|{{GVI|arma3|2.02}}}}
'''Description:''' Is replaced for  UTC time.
__TIME_UTC__ {{cc|14:17:42}}


=== __COUNTER__ ===
'''Description:''' Counts 1 up everytime it is defined.
'''Description:''' Counts 1 up everytime it is defined.
__COUNTER__ {{cc|0}}
<syntaxhighlight lang="cpp">
__COUNTER__ {{cc|2}}
__COUNTER__ // 0
__COUNTER__ {{cc|3}}
__COUNTER__ // 1
__COUNTER_RESET__
__COUNTER__ // 2
__COUNTER__ {{cc|0}}
__COUNTER__ // 3
__COUNTER__ {{cc|1}}
__COUNTER_RESET__
__COUNTER__ {{cc|2}}
__COUNTER__ // 0
__COUNTER__ // 1
__COUNTER__ // 2
</syntaxhighlight>
 
{{ArgTitle|3|__TIMESTAMP_UTC__|{{GVI|arma3|2.06}}}}
 
'''Description:''' Is replaced to the current timestamp in UNIX timestamp.
<syntaxhighlight lang="cpp">
__TIMESTAMP_UTC__ // 1622639059
</syntaxhighlight>
 
{{ArgTitle|3|__COUNTER_RESET__|{{GVI|arma3|2.02}}}}


=== __COUNTER_RESET__===
'''Description:''' Resets counter. See example above.
'''Description:''' Resets counter. See example above.


=== __RAND_INT<tt>N</tt>===
{{ArgTitle|3|__RAND_INT{{hl|N}}__|{{GVI|arma3|2.02}}}}
'''Description:''' Gets replaced by a random <tt>N</tt> bit integer. N can be replaced by 8, 16, 32 and 64
 
__RAND_INT8__ {{cc|102}}
'''Description:''' Gets replaced by a random {{hl|N}} bit integer. {{hl|N}} can be 8, 16, 32 or 64, e.g:
<syntaxhighlight lang="cpp">
__RAND_INT8__
__RAND_INT16__
__RAND_INT32__
__RAND_INT64__
</syntaxhighlight>
 
<syntaxhighlight lang="cpp">
__RAND_INT8__ // e.g -102
</syntaxhighlight>
 
{{ArgTitle|3|__RAND_UINT{{hl|N}}__|{{GVI|arma3|2.02}}}}
 
'''Description:''' Gets replaced by a random ''unsigned'' {{hl|N}} bit integer. {{hl|N}} can be 8, 16, 32 or 64, e.g:
<syntaxhighlight lang="cpp">
__RAND_UINT8__
__RAND_UINT16__
__RAND_UINT32__
__RAND_UINT64__
</syntaxhighlight>
 
<syntaxhighlight lang="cpp">
__RAND_UINT8__ // 108
</syntaxhighlight>


=== __RAND_UINT<tt>N</tt>===
{{ArgTitle|3|__GAME_VER__|{{GVI|arma3|2.02}}}}
'''Description:''' Gets replaced by a random ''unsigned'' <tt>N</tt> bit integer. N can be replaced by 8, 16, 32 and 64
__RAND_UINT8__ {{cc|108}}


===__GAME_VER__ ===
'''Description:''' Gets replaced by the game version.
'''Description:''' Gets replaced by the game version.
__GAME_VER__ {{cc|02.00.146790}}
<syntaxhighlight lang="cpp">
__GAME_VER__ // 02.00.146790
</syntaxhighlight>
 
{{ArgTitle|3|__GAME_VER_MAJ__|{{GVI|arma3|2.02}}}}


===__GAME_VER_MAJ__ ===
'''Description:''' Gets replaced by the ''major'' game version.  
'''Description:''' Gets replaced by the ''major'' game version.  
__GAME_VER_MAJ__ {{cc|02}}
<syntaxhighlight lang="cpp">
__GAME_VER_MAJ__ // 02
</syntaxhighlight>
 
{{ArgTitle|3|__GAME_VER_MIN__|{{GVI|arma3|2.02}}}}


===__GAME_VER_MIN__  ===
'''Description:''' Gets replaced by the ''minor'' game version.
'''Description:''' Gets replaced by the ''minor'' game version.
__GAME_VER_MIN__ {{cc|00}}
<syntaxhighlight lang="cpp">
__GAME_VER_MIN__ // 00
</syntaxhighlight>
 
{{ArgTitle|3|__GAME_BUILD__|{{GVI|arma3|2.02}}}}


===__GAME_BUILD__ ===
'''Description:''' Gets replaced by the build number.
'''Description:''' Gets replaced by the build number.
__GAME_BUILD__ {{cc|146790}}
<syntaxhighlight lang="cpp">
__GAME_BUILD__ // 146790
</syntaxhighlight>
 
{{ArgTitle|3|__A3_DIAG__|{{GVI|arma3|2.12}}}}
 
'''Description:''' Gets replaced by 1 on diag, undefined otherwise.
<syntaxhighlight lang="cpp">
__GAME_BUILD__ // 1
</syntaxhighlight>
 
{{ArgTitle|3|__ARMA__|{{GVI|arma3|2.02}}}}


===__ARMA__ ===
'''Description:''' {{Wiki|stub}}
'''Description:''' {{Wiki|stub}}
__ARMA__ {{cc|1}}
<syntaxhighlight lang="cpp">
__ARMA__ // 1
</syntaxhighlight>
 
{{ArgTitle|3|__ARMA3__|{{GVI|arma3|2.02}}}}


===__ARMA3__ ===
'''Description:''' {{Wiki|stub}}
'''Description:''' {{Wiki|stub}}
__ARMA3__ {{cc|1}}
<syntaxhighlight lang="cpp">
__ARMA3__ // 1
</syntaxhighlight>


===__A3_DEBUG__ ===
{{ArgTitle|3|__A3_DEBUG__|{{GVI|arma3|2.02}}}}
'''Description:''' This macro is only set when the [[Arma 3 Startup Parameters| -debug parameter]] was provided. It can be used to switch your mods or scripts to debug mode dynamically.
 
__A3_DEBUG__ {{cc|1 (If -debug was enabled)}}
'''Description:''' This macro is only set when the [[Arma 3 Startup Parameters|-debug parameter]] was provided. It can be used to switch mods or scripts to debug mode dynamically.
<syntaxhighlight lang="cpp">
__A3_DEBUG__ // 1 (if -debug was enabled)
</syntaxhighlight>
 
{{ArgTitle|3|__A3_EXPERIMENTAL__|{{GVI|arma3|2.15}}}}
 
'''Description:''' This macro is only set in experimental game builds ([[Arma_3:_Steam_Branches|Development branch]] or [[Arma_3:_Steam_Branches|Profiling branch]])
<syntaxhighlight lang="cpp">
__A3_EXPERIMENTAL__ // 1 (if on dev or profiling branch)
</syntaxhighlight>
can be combined with game build to check for bugfixes added to profiling branch (in case a stable branch hotfix is released with a higher buildnumber but that doesn't include the fix)
<syntaxhighlight lang="cpp">
#if __GAME_VER_MIN__ >= 16 //#TODO remove this workaround when 2.16 is released
#define BUG_FIXED
#endif
 
#if __GAME_BUILD__ > 151035 // Builds newer than this
#if __A3_EXPERIMENTAL__ // But on experimental only
#define BUG_FIXED
#endif
#endif
 
#ifdef BUG_FIXED
use the thing
#else
don't use the thing and have some workaround
#endif
</syntaxhighlight>
 
{{ArgTitle|3|__A3_PROFILING__|{{GVI|arma3|2.15}}}}
 
'''Description:''' This macro is only set when Profiling commands are available ([[Arma_3:_Steam_Branches|Development branch]] diag binary or in [[Arma_3:_Steam_Branches|Profiling branch]] profiling binary)
<syntaxhighlight lang="cpp">
#if __A3_PROFILING__
diag_captureFrame 1;
#endif
</syntaxhighlight>
 
== Config Parser ==
 
{{Feature|important|
* {{hl|__EXEC}} and {{hl|__EVAL}} '''cannot''' be used in preprocessor {{Link|#Macros}}, e.g '''{{Link|##if}}'''!
}}
{{Feature|informative|
* {{hl|__EXEC}} and {{hl|__EVAL}} can be used in configs (such as [[config.cpp]], [[Description.ext]], [[Rvmat File Format|RVMat]] etc) but are not suitable for [[SQF Syntax|SQF]] or [[SQS Syntax|SQS]] scripts.
* As they use [[parsingNamespace]], both global and local variables set in {{hl|__EXEC}} are available in {{hl|__EVAL}}.
* [[parsingNamespace]] can then be accessed through scripting; see the {{Link|#EXEC|__EXEC}} example below.
}}
 
=== __EXEC ===
 
This '''Config Parser''' macro allows to assign values to internal variables or just execute arbitrary code.
The code inside {{hl|__EXEC}} macros runs in [[parsingNamespace]] and variables defined in it will also be created in [[parsingNamespace]]. The variables can then be used to create more complex macros:
 
<syntaxhighlight lang="cpp">
__EXEC(cat = 5 + 1;)
__EXEC(lev = cat - 2;)
</syntaxhighlight>
<sqf>
_cat = parsingNamespace getVariable "cat"; // 6
_lev = parsingNamespace getVariable "lev"; // 4
</sqf>
 
{{Feature|warning|
{{hl|__EXEC}} does not like round brackets {{hl|()}} inside expressions. If grouping is needed, perhaps values could be calculated inside the brackets separately and assigned to local variables:
<syntaxhighlight lang="cpp">__EXEC(a = (1+2);) // ERROR</syntaxhighlight>
<syntaxhighlight lang="cpp">
__EXEC(_expr = 1+2;)
__EXEC(a = _expr;) // OK
</syntaxhighlight>
}}
 
=== __EVAL ===
 
With this '''Config Parser''' macro expressions can be evaluated, including previously assigned internal variables. Unlike {{hl|__EXEC}}, {{hl|__EVAL}} supports multiple parentheses:
 
<syntaxhighlight lang="cpp">
w = __EVAL(safeZoneW - (5 * ((1 / (getResolution select 2)) * 1.25 * 4)));
</syntaxhighlight>
 
* {{hl|__EVAL}} macros '''must''' be assigned to a config property and the expression '''must''' be terminated with {{hl|;}}.
* {{hl|__EVAL}} can only return [[Number]] or [[String]]. Any other type is represented as [[String]], even [[Boolean]] type (which would result in either {{hl|"true"}} or {{hl|"false"}}).
 
{{Feature|warning|
{{hl|__EVAL}} does not like curly brackets {{hl|{}}}; if code is needed in the expression, use [[compile]] [[String]] instead:
<syntaxhighlight lang="cpp">result = __EVAL(call { 123 }); // ERROR</syntaxhighlight>
<syntaxhighlight lang="cpp">result = __EVAL(call compile "123"); // OK</syntaxhighlight>
}}


===__GAME_BUILD__ ===
'''Description:''' Gets replaced by the build number. <tt>146790</tt>
__GAME_BUILD__ {{cc|146790}}


== Errors ==
== Errors ==
Line 335: Line 547:
; Problem: Preprocessor failed error 2.
; Problem: Preprocessor failed error 2.


; How to fix: Add quotation marks in the title, or the file path. (''e.g.'' <tt>#include "soGood.sqf"</tt>).
; How to fix: Add quotation marks in the title, or the file path. (''e.g.'' {{hl|#include "soGood.sqf"}}).


=== Error 6 ===
=== Error 6 ===
Line 342: Line 554:


; Known reasons:
; Known reasons:
: "The problem is using <tt>#ifdef #ifdef #endif #endif</tt>, a.k.a nested <tt>#ifdef</tt>. This doesn't work in Arma. It's only possible to use <tt>#ifdef</tt> and <tt>#endif</tt> once and not nested."
: "The problem is using {{hl|#ifdef #ifdef #endif #endif}}, a.k.a nested {{hl|#ifdef}}. This doesn't work in Arma. It's only possible to use {{hl|#ifdef}} and {{hl|#endif}} once and not nested."
: <tt>#endif</tt> without preceding <tt>#ifdef</tt> or <tt>#ifndef</tt>
: {{hl|#endif}} without preceding {{hl|#ifdef}} or {{hl|#ifndef}}


=== Error 7 ===
=== Error 7 ===
Line 349: Line 561:
; Problem: Preprocessor failed on file X - error 7.
; Problem: Preprocessor failed on file X - error 7.


; Known reasons: The preprocessor encountered an unknown directive. Read, you have probably a typo in the file (something like <tt>#inlcude</tt> or <tt>#defien</tt>). Double-check all preprocessor directives in that file.
; Known reasons: The preprocessor encountered an unknown directive. Read, as a typo most has likely found a way into the file (something like {{hl|#inlcude}} or {{hl|#defien}}).<!--
--> Double-check all preprocessor directives in that file.
 


== External links ==
== External links ==


* [https://web.archive.org/web/20170319190732/http://ofp-faguss.com/files/ofp_preprocessor_explained.pdf {{ofp}} - Preprocessor Guide (Wayback Machine)]
* {{Link|https://web.archive.org/web/20170319190732/http://ofp-faguss.com/files/ofp_preprocessor_explained.pdf|{{ofp}} - Preprocessor Guide (Wayback Machine)}}
* [https://github.com/Krzmbrzl/ArmaPreprocessorTestCases Collection of preprocessor test-cases]
* {{Link|https://github.com/Krzmbrzl/ArmaPreprocessorTestCases|Collection of preprocessor test-cases}}
 


[[Category:Scripting_Topics]]
[[Category:Scripting Topics]]

Latest revision as of 01:25, 2 February 2024

The PreProcessor and the Config Parser allow to use macros in configs. The purpose of macros is to re-use the same definition in scripts and configs multiple times. It also gives a centralized place to correct errors in this definition. Know that edge cases may arise - see the collection of test-cases linked at the end of this page.

Arma 3
In Arma 3, preprocessor commands are case-sensitive!


Parsing

See Config Parser below:


Macros

Comments

A comment is a line within code that is not actually processed by the game engine. They are used to make code more readable or to add notes for future reference. The preprocessor removes all comments from the file before it is processed. Therefore, comments are never actually "seen" by the game engine.

Sometimes a problematic string can fail to purge these comment outs during preprocessing!
private _a = '"'; // this is just a comment, but failed to remove upon the preprocess so will return an error. Caused by that string
// this is a single-line comment
 
/*
	this is a
	multi-line
	comment
*/
 
myValue = something; // only this part of the line is commented out

myArray = { "apple"/*, "banana"*/, "pear" }; // a portion in the middle of this line is commented out

#define

Using the #define instruction, it is possible to define a keyword and assign a definition to it. The keyword may contain any letter, digit or underscore in arbitrary order, as long as it does not start with a digit (RegEx: [a-zA-Z_][0-9a-zA-Z_]*). As an example:

#define true 1

The above means that whenever true is used in a config, the parser will replace this with the value 1.

The define-statement does swallow all spaces in between the macro-keyword and any non-space-character in the body (Note that tabs are not spaces! They don't get removed)

#define MACRO                     test
MACRO // preprocesses to test (without any spaces)

#define MACRO	test // There's a tab between MACRO and test
MACRO // preprocesses to "	test" (without quotes - they are only used to show that the tab character didn't get removed)

The space between the macro-keyword and the body is also fully optional (though very useful to tell the preprocessor where the macro name ends and where the body begins):

#define MACRO#test
MACRO // preprocesses to "test"

Arguments

Arguments can be added to more complex macros, by including them between brackets after the keyword. For the name of the arguments the same rule as for the macro-keyword (see above) apply.

#define CAR(NAME) displayName = NAME;

If CAR("Mini") is used, it will be replaced with displayName = "Mini";. Multiple arguments can also be used:

#define BLASTOFF(UNIT,RATE) UNIT setVelocity [0,0,RATE];

Macro arguments may be composed of any characters, as long as they do not contain a comma (because commas are used as argument-delimiters). If quotes are being used, they have to be balanced. The same applies to single-quotes; this is because String detection is working in macro arguments - therefore commas can even get passed in as a macro argument as long as they are part of a String (This only works with Strings wrapped in double-quotes though). Note however that although the macro gets resolved properly, the comma gets removed from the String (probably a bug).

#define MACRO(arg) arg
MACRO("Some, content") // preprocesses to "Some content" (note the missing comma)

Quote escaping is also not supported in this context (neither with double- nor with single-quotes)

#define MACRO(arg) arg
MACRO("Some ""content""") // preprocesses to "Some ""content"""

Passing arrays with more than one element [el1,el2,...] as arguments into macros as well as any argument containing comas "some, sentence", will need a small workaround:

#define HINTARG(ARG) hint ("Passed argument: " + str ARG)

Incorrect usage:

HINTARG([1,2,3,4,5,6,7,8,9,0]); // ERROR, won't even compile

Correct usage:

#define array1 [1,2,3,4,5,6,7,8,9,0] 
HINTARG(array1); // SUCCESS

The argument replacement is performed before the expansion of the macro body. That means one doesn't have to worry about name-conflicts between argument-names of the current macro and already defined macros:

#define ONE foo
#define TWO(ONE) ONE
TWO(bar) // will preprocess to bar

Replacing parts of words

By default, only whole words can be replaced by arguments. If only a part of the word should be replaced, use the ## instruction. This is necessary when either the start or the end of the argument connects to another character that is not a ; (semi-colon) or   (space).

class NAME##_Button_Slider: RscText \
{ \
	model = \OFP2\Structures\Various\##FOLDER##\##FOLDER; \

It is also possible to use the single # to convert an argument to a string.

statement = (this animate [#SEL, 0]); \

Multi-line

For longer definitions, one can stretch the macro across multiple lines. To create a multi-line definition, each line except the last one should end with a \ character:

#define DRAWBUTTON(NAME)\
	__EXEC(idcNav = idcNav + 4) \
	// ...
The backslash must be the last character in a line when defining a multi-line macro. Any character (including spaces) after the backslash will cause issues.

#undef

Undefine (delete) a macro previously set by the use of #define.

#undef NAME

#if

Description: Checks condition and processes content if condition returns 1. Skips content if it returns 0

#if __A3_DEBUG__ // Check if game is started in debug mode
#include "debug.hpp" // Include some file if debug mode is enabled
#endif

Comparison Operators

The following operations are supported:

  • <
  • >
  • <=
  • >=
  • ==
  • !=

Example:

#if __GAME_VER_MAJ__ != 2
// Do something...
#endif

#ifdef

A simple if-then construction to check whether a certain set of definitions has already been made:

#ifdef NAME
	// config that will be used if NAME is defined
#endif

IFDEFs cannot be nested. The preprocessor will generate errors for all inner definitions if the outer definition doesn't exist.

#ifndef

Same as #ifdef, but checks for absence of definition instead.

#ifndef NAME
	// config that will be used if NAME is -not- defined
#endif

#else

Provides alternative code to #if, #ifdef, #ifndef checks.

#ifndef NAME
	// config that will be used if NAME is -not- defined
#else
	// config that will be used if NAME -is- defined
#endif

#endif

This ends a conditional block as shown in the descriptions of #ifdef and #ifndef above.

#include

Copies the code from a target file and pastes it where #include directive is.

#include "file.hpp"
#include <file.txt> // Brackets are equivalent to quotation marks and may be used in their place.

Source directory is:

  • For any file without starting the include path with \ - the file's current directory
  • When starting with \ - the internal filesystem root (see Addon Development) or the Game's working directory (only with -filePatching enabled)

A path beginning can be defined as follow:

  • drive (only with -filePatching enabled):
    #include "D:\file.txt"
    
  • PBO with PBOPREFIX:
     #include "\myMod\myAddon\file.txt"
    
  • PBO (keep in mind that in this case, if the PBO's file name will be changed, all '#include' referencing it will need to be updated):
    #include"\myMod\myAddon\file.txt" // Arma 3\@myMod\addons\myAddon.pbo\file.txt;
    

To move to parent directory use '..' (two dots) (Supported since Arma 3 logo black.png1.50):

#include "..\file.sqf"

Preprocessor does not support the use of macros for pre-defined file names.

#define path "codestrip.txt"
#include path // this will cause an error

#

'#' (single hash) operator wraps the text with quotation marks.

#define STRINGIFY(s) #s
#define FOO 123
test1 = STRINGIFY(123); //test1 = "123";
test2 = STRINGIFY(FOO); //test2 = "123";

This operator does only work on keywords following the rules for macro-names (see #define-section). If one wants to stringify anything else (like e.g. a number), one has to use a stringify-macro that takes an argument and stringifies that (as in the example above).

#define MACRO Test #123
MACRO // preprocesses to Test 123 - note that there are not any quotes inserted

##

'##' (double hash) operator concatenates what's before the ## with what's after it.

#define GLUE(g1,g2) g1##g2
#define FOO 123
#define BAR 456
test1 = GLUE(123,456); //test1 = 123456;
test2 = GLUE(FOO,BAR); //test2 = 123456;

__LINE__

This keyword gets replaced with the line number in the file where it is found. For example, if __LINE__ is found on the 10th line of a file, the word __LINE__ will be replaced with the number 10.

__FILE__

This keyword gets replaced with the CURRENT file being processed.

__has_include

Description: Checks if given file exists. Returns 1 if it does, 0 if it does not exist.

  • The file path must start with the backslash \ otherwise it would fail silently (returns 0 so does nothing unless you put something in #else)!
  • If you wanted to make an addon that can change its config dynamically depending on mods that loaded along, do not binarize the config.cpp!
To validate that the file exists in your game, you can use fileExists command to check.
fileExists "\a3\data_f\config.bin"; // returns true fileExists "\z\ace\addons\main\script_component.hpp"; // returns true if ACE is loaded, false otherwise
#if __has_include("\z\ace\addons\main\script_component.hpp")
// file exists! Do something with it
#else
// file does not exist
#endif

__DATE_ARR__

Description: Is replaced for current date in format array.

__DATE_ARR__ // 2020,10,28,15,17,42

__DATE_STR__

Description: Is replaced for current date in format string.

__DATE_STR__  // "2020/10/28, 15:17:42"

__DATE_STR_ISO8601__

Description: Is replaced for current date in format string. The date is presented in ISO8601 standard.

__DATE_STR_ISO8601__ // "2020-10-28T14:17:42Z"

__TIME__

Description: Is replaced for current time.

__TIME__ // 15:17:42

__TIME_UTC__

Description: Is replaced for UTC time.

__TIME_UTC__ // 14:17:42

__DAY__

Description: Is replaced for UTC day.

__DAY__ // 20

__MONTH__

Description: Is replaced for UTC month.

__MONTH__ // 11

__YEAR__

Description: Is replaced for UTC year.

__YEAR__ // 2023

__COUNTER__

Description: Counts 1 up everytime it is defined.

__COUNTER__ // 0
__COUNTER__ // 1
__COUNTER__ // 2
__COUNTER__ // 3
__COUNTER_RESET__
__COUNTER__ // 0
__COUNTER__ // 1
__COUNTER__ // 2

__TIMESTAMP_UTC__

Description: Is replaced to the current timestamp in UNIX timestamp.

__TIMESTAMP_UTC__ // 1622639059

__COUNTER_RESET__

Description: Resets counter. See example above.

__RAND_INTN__

Description: Gets replaced by a random N bit integer. N can be 8, 16, 32 or 64, e.g:

__RAND_INT8__
__RAND_INT16__
__RAND_INT32__
__RAND_INT64__
__RAND_INT8__ // e.g -102

__RAND_UINTN__

Description: Gets replaced by a random unsigned N bit integer. N can be 8, 16, 32 or 64, e.g:

__RAND_UINT8__
__RAND_UINT16__
__RAND_UINT32__
__RAND_UINT64__
__RAND_UINT8__ // 108

__GAME_VER__

Description: Gets replaced by the game version.

__GAME_VER__ // 02.00.146790

__GAME_VER_MAJ__

Description: Gets replaced by the major game version.

__GAME_VER_MAJ__ // 02

__GAME_VER_MIN__

Description: Gets replaced by the minor game version.

__GAME_VER_MIN__ // 00

__GAME_BUILD__

Description: Gets replaced by the build number.

__GAME_BUILD__ // 146790

__A3_DIAG__

Description: Gets replaced by 1 on diag, undefined otherwise.

__GAME_BUILD__ // 1

__ARMA__

Description:

__ARMA__ // 1

__ARMA3__

Description:

__ARMA3__ // 1

__A3_DEBUG__

Description: This macro is only set when the -debug parameter was provided. It can be used to switch mods or scripts to debug mode dynamically.

__A3_DEBUG__ // 1 (if -debug was enabled)

__A3_EXPERIMENTAL__

Description: This macro is only set in experimental game builds (Development branch or Profiling branch)

__A3_EXPERIMENTAL__ // 1 (if on dev or profiling branch)

can be combined with game build to check for bugfixes added to profiling branch (in case a stable branch hotfix is released with a higher buildnumber but that doesn't include the fix)

#if __GAME_VER_MIN__ >= 16 //#TODO remove this workaround when 2.16 is released
#define BUG_FIXED
#endif

#if __GAME_BUILD__ > 151035 // Builds newer than this
#if __A3_EXPERIMENTAL__ // But on experimental only
#define BUG_FIXED
#endif
#endif

#ifdef BUG_FIXED
use the thing
#else
don't use the thing and have some workaround
#endif

__A3_PROFILING__

Description: This macro is only set when Profiling commands are available (Development branch diag binary or in Profiling branch profiling binary)

#if __A3_PROFILING__
diag_captureFrame 1;
#endif

Config Parser

  • __EXEC and __EVAL cannot be used in preprocessor Macros, e.g #if!

__EXEC

This Config Parser macro allows to assign values to internal variables or just execute arbitrary code. The code inside __EXEC macros runs in parsingNamespace and variables defined in it will also be created in parsingNamespace. The variables can then be used to create more complex macros:

__EXEC(cat = 5 + 1;)
__EXEC(lev = cat - 2;)

_cat = parsingNamespace getVariable "cat"; // 6 _lev = parsingNamespace getVariable "lev"; // 4

__EXEC does not like round brackets () inside expressions. If grouping is needed, perhaps values could be calculated inside the brackets separately and assigned to local variables:
__EXEC(a = (1+2);) // ERROR
__EXEC(_expr = 1+2;)
__EXEC(a = _expr;) // OK

__EVAL

With this Config Parser macro expressions can be evaluated, including previously assigned internal variables. Unlike __EXEC, __EVAL supports multiple parentheses:

w = __EVAL(safeZoneW - (5 * ((1 / (getResolution select 2)) * 1.25 * 4)));
  • __EVAL macros must be assigned to a config property and the expression must be terminated with ;.
  • __EVAL can only return Number or String. Any other type is represented as String, even Boolean type (which would result in either "true" or "false").
__EVAL does not like curly brackets {}; if code is needed in the expression, use compile String instead:
result = __EVAL(call { 123 });			// ERROR
result = __EVAL(call compile "123");	// OK


Errors

Error 2

Problem
Preprocessor failed error 2.
How to fix
Add quotation marks in the title, or the file path. (e.g. #include "soGood.sqf").

Error 6

Problem
Preprocessor failed on file X - error 6.
Known reasons
"The problem is using #ifdef #ifdef #endif #endif, a.k.a nested #ifdef. This doesn't work in Arma. It's only possible to use #ifdef and #endif once and not nested."
#endif without preceding #ifdef or #ifndef

Error 7

Problem
Preprocessor failed on file X - error 7.
Known reasons
The preprocessor encountered an unknown directive. Read, as a typo most has likely found a way into the file (something like #inlcude or #defien). Double-check all preprocessor directives in that file.


External links