{"name":"PreProcessor Commands","title":"PreProcessor Commands","type":"topic","url":"https://community.bistudio.com/wiki/PreProcessor_Commands","revid":378955,"updated":"2026-09-02T17:00:00Z","aliases":["Config Parser","PreProcessor Errors"],"markdown":"The PreProcessor and the Config Parser allow to use macros in configs.\nThe purpose of macros is to re-use the same definition in scripts and configs multiple times.\nIt also gives a centralised place to correct errors in this definition.\nKnow that edge cases may arise - see the collection of test-cases linked at the end of this page.\n\n> [!NOTE]\n> **Arma 3:** In Arma 3, preprocessor commands are **case-sensitive!**\n\n### Parsing\n\nSee #Config Parser below:\n- **Config.cpp** - parsed when PBO is binarised.\n  - localize cannot be used in macros, as it would hardcode string of current language instead of creating reference.\n- **Description.ext** - parsed when mission is loaded or previewed in missions list.\n- **SQF script** - parsed when preprocessFile, preprocessFileLineNumbers, compileScript or execVM is used.\n\n### Macros\n\n#### Strings\n\nIn both Configs and SQF scripts, there are two ways to define strings: either using single quotes (`'text'`) or double quotes (`\"text\"`).\nWhile they are both identical, they are **treated differently** by the preprocessor.\nThe **single quote** string contents are **parsed** by the preprocessor, while the **double quote** ones are **ignored**.\n\n```sqf\n#define ARG world\n\nsystemChat 'hello ARG'; // prints: hello world (i.e. ARG was replaced by the preprocessor with world)\nsystemChat \"hello ARG\"; // prints: hello ARG\n```\n\n> [!WARNING]\n> While it is possible in config to define a string without quotes (the preprocessor will act as if the value were wrapped with single quotes), this practice is heavily discouraged.\n>\n> ```cpp\n> title = \"$STR_TranslationKey\";    // good\n> title = $STR_TranslationKey;    // tolerated but incorrect\n> ```\n\n#### Comments\n\nA 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.\nThe preprocessor removes all comments from the file before it is processed. Therefore, comments are never actually \"seen\" by the game engine.\n\n> [!WARNING]\n> Sometimes a problematic string can fail to purge these comment outs during preprocessing!\n>\n> ```sqf\n> private _a = '\"';\n> // this is just a comment, but failed to remove upon the preprocess so will return an error. Caused by that string\n> ```\n\n```cpp\n// this is a single-line comment\n\n/*\n    this is a\n    multi-line\n    comment\n*/\n\nmyValue = something; // only this part of the line is commented out\n\nmyArray = { \"apple\"/*, \"banana\"*/, \"pear\" }; // a portion in the middle of this line is commented out\n```\n\n#### #define\n\nUsing the `1. define` instruction, it is possible to define a keyword and assign a definition to it.\nThe 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_]*`).\nAs an example:\n\n```cpp\n#define true 1\n```\n\nThe above means that whenever *true* is used in a config, the parser will replace this with the value *1*.\n\nThe 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)\n\n```cpp\n#define MACRO test\nMACRO // preprocesses to test (without any spaces)\n\n#define MACRO    test // There's a tab between MACRO and test\nMACRO // preprocesses to \"    test\" (without quotes - they are only used to show that the tab character didn't get removed)\n```\n\nThe 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):\n\n```cpp\n#define MACRO#test\nMACRO // preprocesses to \"test\"\n```\n\n##### Arguments\n\nArguments 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.\n\n```cpp\n#define CAR(NAME) displayName = NAME;\n```\n\nIf *CAR(\"Mini\")* is used, it will be replaced with *displayName = \"Mini\";*. Multiple arguments can also be used:\n\n```cpp\n#define BLASTOFF(UNIT,RATE) UNIT setVelocity [0,0,RATE];\n```\n\nMacro arguments may be composed of any characters, as long as they do not contain a comma (because commas are used as argument-delimiters).\nIf quotes are being used, they have to be balanced.\nThe 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).\nNote however that although the macro gets resolved properly, the comma gets removed from the String (probably a bug).\n\n```cpp\n#define MACRO(arg) arg\nMACRO(\"Some, content\") // preprocesses to \"Some content\" (note the missing comma)\n```\n\nQuote escaping is also not supported in this context (neither with double- nor with single-quotes)\n\n```cpp\n#define MACRO(arg) arg\nMACRO(\"Some \"\"content\"\"\") // preprocesses to \"Some \"\"content\"\"\"\n```\n\nPassing arrays with more than one element `[el1,el2,...]` as arguments into macros as well as any argument containing commas (e.g `\"some, sentence\"`) will need a small workaround:\n\n```cpp\n#define HINTARG(ARG) hint (\"Passed argument: \" + str ARG)\n```\n\nIncorrect usage:\n\n```cpp\nHINTARG([1,2,3,4,5,6,7,8,9,0]); // ERROR, won't even compile\n```\n\nCorrect usage:\n\n```cpp\n#define array1 [1,2,3,4,5,6,7,8,9,0]\nHINTARG(array1); // SUCCESS\n```\n\nThe 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:\n\n```cpp\n#define ONE foo\n#define TWO(ONE) ONE\nTWO(bar) // will preprocess to bar\n```\n\n##### Replacing parts of words\n\nBy default, only whole words can be replaced by arguments. If only a part of the word should be replaced, use the `1.` instruction.\nThis is necessary when either the start or the end of the argument connects to another character that is not a `****` (semi-colon) or  (space).\n\n```cpp\nclass NAME##_Button_Slider: RscText \\\n{ \\\n    model = \\OFP2\\Structures\\Various\\##FOLDER##\\##FOLDER; \\\n```\n\nIt is also possible to use the single *#* to convert an argument to a string.\n\n```cpp\nstatement = (this animate [#SEL, 0]); \\\n```\n\n##### Multi-line\n\nFor 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:\n\n```cpp\n#define DRAWBUTTON(NAME)\\\n    __EXEC(idcNav = idcNav + 4) \\\n    // ...\n```\n\n> [!NOTE]\n> 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.\n\n##### Variadic Arguments (Arma 3 v2.24)\n\nReference https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html\n\nVariadic arguments allows to provide a variable number of arguments to a macro, instead of a fixed number.\n\nAnd there are several \"functions\" to work with these variable-length argument lists.\n\nA basic variadic macro looks like this\n\n```cpp\n#define MACRO(...) __VA_ARGS__\n```\n\nThere are a few special keywords and \"functions\" that are only valid when used inside a variadic macro\n- `__VA_ARGS__`\n- `__VA_OPT__(X)`\n- `__VA_OPT_INV__(X)`\n- `__VA_APPLY__(FUNC(,SEP))`\n- `__VA_SELECT__(index)`\n\n###### __VA_ARGS__\n\nis a special keyword that will be replaced by all the macro arguments, separated by \",\". For example:\n\n```cpp\n#define MACRO(...) __VA_ARGS__\n\nMACRO() // \"\", Zero is also a valid number of arguments\nMACRO(a,b,c) // \"a,b,c\", the arguments are \"a\", \"b\" and \"c\"\nMACRO({str [1,2,3]}) // \"{str [1,2,3]}\", the arguments are \"{str [1\", \"2\" and \"3]}\"\n```\n\nThis simple variant can replace helper macros like CBA mod's ARR_1, ARR_2, ARR_3, with just one ARR(...) macro.\n\nIt can also be used to wrap code blocks, which was difficult to do without it because they can contain commas and its not easy to tell the number of commas, like in the example above.\n\n###### __VA_OPT__(x) and __VA_OPT_INV__(x)\n\nare simple functions to handle the special case of there being zero variadic arguments.\n`__VA_OPT__(x)` is only active if arguments are present. And the INV variant is only active if no arguments are present\nFor example:\n\n```cpp\n#define HAS_ARGS __VA_OPT__(Yes I have Arguments!)__VA_OPT_INV__(Nothing!)\n\nHAS_ARGS() // \"Nothing!\"\nHAS_ARGS(a) // \"Yes I have Arguments!\"\nHAS_ARGS(a,b,c) // \"Yes I have Arguments!\", no matter how many arguments, if there is at least one, the optional data is inserted\n```\n\nThis is useful when the arguments are forwarded to something like:\n\n```cpp\n#define LOG(message, ...) diag_log __VA_OPT__([) message __VA_OPT__(,) __VA_ARGS__ __VA_OPT__(])\n\nLOG(\"My Message\") // 'diag_log  \"My Message\"   '\nLOG(\"My Message\",1,2,3) // 'diag_log [ \"My Message\" , 1,2,3 ]  '\n```\n\n###### __VA_APPLY__(FUNC, SEPARATOR)\n\nis similar to apply in that it applies a function(macro) on every argument. The second \"SEPARATOR\" argument is optional, it can be used to replace the usual \",\" separator with something different.\n\nThe macro passed to apply must have 0, 1 or 2 arguments.\n\nIf 0 arguments, every value will be replaced by the macro contents, for example:\n\n```cpp\n#define FUNC A\n#define TRANSFORM(...) __VA_APPLY__(FUNC)\nTRANSFORM(1,2,3) // 'A,A,A'\n```\n\nIf 1 argument, it will be the value of the argument, for example:\n\n```cpp\n#define QUOTE(x) #x\n#define TRANSFORM(...) __VA_APPLY__(QUOTE)\nTRANSFORM(1,2,3) // '\"1\",\"2\",\"3\"'\n```\n\nIf 2, the second argument MUST be named `__VA_INDEX__`, it will be filled with the index of the argument, for example:\n\n```cpp\n#define FUNC(x, __VA_INDEX__) __VA_INDEX__: #x\n#define TRANSFORM(...) __VA_APPLY__(FUNC)\nTRANSFORM(1,2,3) // '0: \"1\",1: \"2\",2: \"3\"'\n```\n\nBy default, just like `__VA_ARGS__` when the values are written to the output, they will be separated by \",\".\n\nThis can be changed by providing a separator argument, for example:\nFor example:\n\n```cpp\n#define VERBATIM(x) x\n#define SEPARATOR_NONE\n#define TRANSFORM_CONCAT(...) __VA_APPLY__(QUOTE, SEPARATOR_NONE)\nTRANSFORM_CONCAT(1,2,3) // '123'\n\n#define GET_VARIABLE(x) private _##x = player getVariable #x\n#define SEPARATOR_SEMI ;\n#define GET_VARIABLES(...) __VA_APPLY__(GET_VARIABLE, SEPARATOR_SEMI)\n\nGET_VARIABLES() // ''\nGET_VARIABLES(MyFnc) // 'private _MyFnc = player getVariable \"MyFnc\"'\nGET_VARIABLES(MyFnc,MyOtherFnc) // 'private _MyFnc = player getVariable \"MyFnc\";private _MyOtherFnc = player getVariable \"MyOtherFnc\"'\n```\n\nThe results of `__VA_APPLY__` can also be passed to another variadic macro, for example:\n\n```cpp\n#define QUOTE(x) #x\n#define WRITE_LOG(...) diag_log [__VA_ARGS__]\n#define MAKE_ARRAY(...) [__VA_ARGS__]\n\n#define DO_THINGS(...) WRITE_LOG(__VA_APPLY__(QUOTE)); _array = MAKE_ARRAY(__VA_ARGS__);\n\nDO_THINGS(a,b,c) // diag_log [\"a\",\"b\",\"c\"]; _array = [a,b,c];\n```\n\n###### __VA_SELECT__(index)\n\nwill select the specified element out of the `__VA_ARGS__` list. index must be a number.\nFor example:\n\n```cpp\n#define SELECT_ARG(index, ...) __VA_SELECT__(index)\n#define FORMAT_ARG(index) SELECT_ARG(index, \"A=%2\", \"B=%3\", \"C=%4\", \"D=%5\", \"E=%6\", \"F=%7\")\n#define FORMAT_GET_ARGSTR(arg, __VA_INDEX__) FORMAT_ARG(__VA_INDEX__)\n#define FORMAT(MESSAGE, ...) format [[__VA_OPT_INV__('%1') __VA_OPT__('%1:',) __VA_APPLY__(FORMAT_GET_ARGSTR)] joinString ' ', MESSAGE __VA_OPT__(,) __VA_ARGS__]\n\nFORMAT(\"Message\") // `format [['%1'  ] joinString ' ', \"Message\"  ]`\nFORMAT(\"Message\", value1, value2) // `format [[ '%1:',  \"A=%2\", \"B=%3\"] joinString ' ', \"Message\" ,  value1, value2]` which ends up as `format [\"%1: A=%2 B=%3\", \"Message\", value1, value2]`\n```\n\n#### #undef\n\nUndefine (delete) a macro previously set by the use of #define.\n\n```cpp\n#undef NAME\n```\n\n#### #if\n\n**Description:** Checks condition and processes content if condition returns 1. Skips content if it returns 0\n\n```cpp\n#if __GAME_VER_MAJ__ != 2\n// Do something...\n#endif\n```\n\n##### Comparison Operators\n\nThe following operations are supported:\n- <\n- >\n- <=\n- >=\n- ==\n- !=\n**Example:**\n\n```cpp\n#if __GAME_VER_MAJ__ != 2\n// Do something...\n#endif\n```\n\n#### #ifdef\n\nA simple if-then construction to check whether a certain set of definitions has already been made:\n\n```cpp\n#ifdef NAME\n    // config that will be used if NAME is defined\n#endif\n```\n\nIFDEFs *cannot* be nested. The preprocessor will generate errors for all inner definitions if the outer definition doesn't exist.\n\n#### #ifndef\n\nSame as #ifdef, but checks for absence of definition instead.\n\n```cpp\n#ifndef NAME\n    // config that will be used if NAME is -not- defined\n#endif\n```\n\n#### #else\n\nProvides alternative code to `1. if`, `1. ifdef`, `1. ifndef` checks.\n\n```cpp\n#ifndef NAME\n    // config that will be used if NAME is -not- defined\n#else\n    // config that will be used if NAME -is- defined\n#endif\n```\n\n#### #endif\n\nThis ends a conditional block as shown in the descriptions of #ifdef and #ifndef above.\n\n#### #include\n\nCopies the code from a target file and pastes it where #include directive is.\n\n```cpp\n#include \"file.hpp\"\n#include <file.txt> // Brackets are equivalent to quotation marks and may be used in their place.\n```\n\nSource directory is:\n- For any file without starting the include path with \\ - the file's current directory\n- When starting with \\ - the internal filesystem root (see Addon Prefix) or the Game's working directory (only with -filePatching enabled)\n\nA path beginning can be defined as follow:\n- drive (only with -filePatching enabled): \n\n```cpp\n#include \"D:\\file.txt\"\n```\n\n- PBO with PBOPREFIX: \n\n```cpp\n #include \"\\myMod\\myAddon\\file.txt\"\n```\n\n- 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):\n\n```cpp\n#include\"\\myMod\\myAddon\\file.txt\" // Arma 3\\@myMod\\addons\\myAddon.pbo\\file.txt;\n```\n\nTo move to parent directory use '..' (two dots) (Supported since Arma 3 v1.50):\n\n```cpp\n#include \"..\\file.sqf\"\n```\n\nPreprocessor does not support the use of macros for pre-defined file names.\n\n```cpp\n#define path \"codestrip.txt\"\n#include path // this will cause an error\n```\n\n#### #\n\n'#' (single hash) operator wraps the text with quotation marks.\n\n```cpp\n#define STRINGIFY(s) #s\n#define FOO 123\ntest1 = STRINGIFY(123); //test1 = \"123\";\ntest2 = STRINGIFY(FOO); //test2 = \"123\";\n```\n\nThis operator does only work on keywords following the rules for macro-names (see `1. define`-section).\nIf 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).\n\n```cpp\n#define MACRO Test #123\nMACRO // preprocesses to Test 123 - note that there are not any quotes inserted\n```\n\n#### ##\n\n'##' (double hash) operator concatenates what's before the ## with what's after it.\n\n```cpp\n#define GLUE(g1,g2) g1##g2\n#define FOO 123\n#define BAR 456\ntest1 = GLUE(123,456); //test1 = 123456;\ntest2 = GLUE(FOO,BAR); //test2 = 123456;\n```\n\n#### __LINE__\n\nThis 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.\n\n#### __FILE__\n\nThis keyword gets replaced with the CURRENT file being processed.\ne.g\n \"userconfig\\file1.sqf\"\n\n#### __FILE_NAME__ (Arma 3 v2.18)\n\nThis keyword gets replaced with the **name** of the current file being processed.\ne.g\n \"file1.sqf\"\n\n#### __FILE_SHORT__ (Arma 3 v2.18)\n\nThis keyword gets replaced with the **name** of the current file being processed, without extension.\ne.g\n \"file1\"\n\n#### __has_include (Arma 3 v2.02)\n\n**Description:** Checks if given file exists. Returns 1 if it does, 0 if it does not exist.\n\n> [!IMPORTANT]\n> - The file path must start with the backslash `\\` otherwise it would fail silently (returns `0` so does nothing unless you put something in `1. else`)!\n> - 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*!\n\n> [!NOTE]\n> To validate that the file exists in your game, you can use fileExists command to check.\n>\n> ```sqf\n> fileExists \"\\a3\\data_f\\config.bin\"; // returns true\n> fileExists \"\\z\\ace\\addons\\main\\script_component.hpp\"; // returns true if ACE is loaded, false otherwise\n> ```\n\n```cpp\n#if __has_include(\"\\z\\ace\\addons\\main\\script_component.hpp\")\n// file exists! Do something with it\n#else\n// file does not exist\n#endif\n```\n\n#### __DATE_ARR__ (Arma 3 v2.02)\n\n**Description:** Is replaced for current date in format array.\n\n```cpp\n__DATE_ARR__ // 2020,10,28,15,17,42\n```\n\n#### __DATE_STR__ (Arma 3 v2.02)\n\n**Description:** Is replaced for current date in format string.\n\n```cpp\n__DATE_STR__ // \"2020/10/28, 15:17:42\"\n```\n\n#### __DATE_STR_ISO8601__ (Arma 3 v2.02)\n\n**Description:** Is replaced for current date in format string. The date is presented in ISO8601 standard.\n\n```cpp\n__DATE_STR_ISO8601__ // \"2020-10-28T14:17:42Z\"\n```\n\n#### __TIME__ (Arma 3 v2.02)\n\n**Description:** Is replaced for current time.\n\n```cpp\n__TIME__ // 15:17:42\n```\n\n#### __TIME_UTC__ (Arma 3 v2.02)\n\n**Description:** Is replaced for UTC time.\n\n```cpp\n__TIME_UTC__ // 14:17:42\n```\n\n#### __DAY__ (Arma 3 v2.16)\n\n**Description:** Is replaced for UTC day.\n\n```cpp\n__DAY__ // 20\n```\n\n#### __MONTH__ (Arma 3 v2.16)\n\n**Description:** Is replaced for UTC month.\n\n```cpp\n__MONTH__ // 11\n```\n\n#### __YEAR__ (Arma 3 v2.16)\n\n**Description:** Is replaced for UTC year.\n\n```cpp\n__YEAR__ // 2023\n```\n\n#### __COUNTER__ (Arma 3 v2.02)\n\n**Description:** Counts 1 up everytime it is defined.\n\n```cpp\n__COUNTER__ // 0\n__COUNTER__ // 1\n__COUNTER__ // 2\n__COUNTER__ // 3\n__COUNTER_RESET__\n__COUNTER__ // 0\n__COUNTER__ // 1\n__COUNTER__ // 2\n```\n\n#### __TIMESTAMP_UTC__ (Arma 3 v2.06)\n\n**Description:** Is replaced to the current timestamp in UNIX timestamp.\n\n```cpp\n__TIMESTAMP_UTC__ // 1622639059\n```\n\n#### __COUNTER_RESET__ (Arma 3 v2.02)\n\n**Description:** Resets counter. See example above.\n\n#### __RAND_INT`N`__ (Arma 3 v2.02)\n\n**Description:** Gets replaced by a random `N` bit integer. `N` can be 8, 16, 32 or 64, e.g:\n\n```cpp\n__RAND_INT8__\n__RAND_INT16__\n__RAND_INT32__\n__RAND_INT64__\n```\n\n```cpp\n__RAND_INT8__ // e.g -102\n```\n\n#### __RAND_UINT`N`__ (Arma 3 v2.02)\n\n**Description:** Gets replaced by a random *unsigned* `N` bit integer. `N` can be 8, 16, 32 or 64, e.g:\n\n```cpp\n__RAND_UINT8__\n__RAND_UINT16__\n__RAND_UINT32__\n__RAND_UINT64__\n```\n\n```cpp\n__RAND_UINT8__ // 108\n```\n\n#### __GAME_VER__ (Arma 3 v2.02)\n\n**Description:** Gets replaced by the game version.\n\n```cpp\n__GAME_VER__ // 02.00.146790\n```\n\n#### __GAME_VER_MAJ__ (Arma 3 v2.02)\n\n**Description:** Gets replaced by the *major* game version.\n\n```cpp\n__GAME_VER_MAJ__ // 02\n```\n\n#### __GAME_VER_MIN__ (Arma 3 v2.02)\n\n**Description:** Gets replaced by the *minor* game version.\n\n```cpp\n__GAME_VER_MIN__ // 00\n```\n\n#### __GAME_BUILD__ (Arma 3 v2.02)\n\n**Description:** Gets replaced by the build number.\n\n```cpp\n__GAME_BUILD__ // 146790\n```\n\n#### __A3_DIAG__ (Arma 3 v2.12)\n\n**Description:** Gets replaced by 1 on diag, undefined otherwise.\n\n```cpp\n__A3_DIAG__// 1\n```\n\n#### __ARMA__ (Arma 3 v2.02)\n\n**Description:**\n\n```cpp\n__ARMA__ // 1\n```\n\n#### __ARMA3__ (Arma 3 v2.02)\n\n**Description:**\n\n```cpp\n__ARMA3__ // 1\n```\n\n#### __A3_DEBUG__ (Arma 3 v2.02)\n\n**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.\n\n```cpp\n__A3_DEBUG__ // 1 (if -debug was enabled)\n```\n\n#### __A3_EXPERIMENTAL__ (Arma 3 v2.15)\n\n**Description:** This macro is only set in experimental game builds (Development branch or Profiling branch)\n\n```cpp\n__A3_EXPERIMENTAL__ // 1 (if on dev or profiling branch)\n```\n\ncan 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)\n\n```cpp\n#if __GAME_VER_MIN__ >= 16 //#TODO remove this workaround when 2.16 is released\n#define BUG_FIXED\n#endif\n\n#if __GAME_BUILD__ > 151035 // Builds newer than this\n#if __A3_EXPERIMENTAL__ // But on experimental only\n#define BUG_FIXED\n#endif\n#endif\n\n#ifdef BUG_FIXED\nuse the thing\n#else\ndon't use the thing and have some workaround\n#endif\n```\n\n#### __A3_PROFILING__ (Arma 3 v2.15)\n\n**Description:** This macro is only set when Profiling commands are available (Development branch diag binary or in Profiling branch profiling binary)\n\n```cpp\n#if __A3_PROFILING__\ndiag_captureFrame 1;\n#endif\n```\n\n### Config Parser\n\n> [!IMPORTANT]\n> - `__EXEC` and `__EVAL` **cannot** be used in preprocessor #Macros, e.g **##if**!\n\n> [!NOTE]\n> - `__EXEC` and `__EVAL` can be used in configs (such as config.cpp, Description.ext, RVMat etc) but are not suitable for SQF or SQS scripts.\n> - As they use parsingNamespace, both global and local variables set in `__EXEC` are available in `__EVAL`.\n> - parsingNamespace can then be accessed through scripting; see the __EXEC example below.\n\n#### __EXEC\n\nThis **Config Parser** macro allows to assign values to internal variables or just execute arbitrary code.\nThe 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:\n\n```cpp\n__EXEC(cat = 5 + 1;)\n__EXEC(lev = cat - 2;)\n```\n\n```sqf\n_cat = parsingNamespace getVariable \"cat\"; // 6\n_lev = parsingNamespace getVariable \"lev\"; // 4\n```\n\n> [!WARNING]\n> `__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:\n>\n> ```cpp\n> __EXEC(a = (1+2);) // ERROR\n> ```\n>\n> ```cpp\n> __EXEC(_expr = 1+2;)\n> __EXEC(a = _expr;) // OK\n> ```\n\n#### __EVAL\n\nWith this **Config Parser** macro expressions can be evaluated, including previously assigned internal variables. Unlike `__EXEC`, `__EVAL` supports multiple parentheses:\n\n```cpp\nw = __EVAL(safeZoneW - (5 * ((1 / (getResolution select 2)) * 1.25 * 4)));\n```\n\n- `__EVAL` macros **must** be assigned to a config property and the expression **must** be terminated with `****`.\n- `__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\"`).\n\n> [!NOTE]\n> if code is needed in the expression, use compile String instead. Like so:\n>\n> ```cpp\n> result = __EVAL(call compile \"123\");\n> ```\n>\n> that is because `__EVAL` is incapable of dealing with curly brackets `{`}\n\n### Errors\n\n#### Error 2\n\n**Problem: Preprocessor failed error 2.**\n\n**How to fix: Add quotation marks in the title, or the file path. (*e.g.* `1. include \"soGood.sqf\"`).**\n\n#### Error 6\n\n**Problem: Preprocessor failed on file X - error 6.**\n\n**Known reasons:**\n\"The problem is using `1. ifdef #ifdef #endif #endif`, a.k.a nested `1. ifdef`. This doesn't work in Arma. It's only possible to use `1. ifdef` and `1. endif` once and not nested.\"\n`1. endif` without preceding `1. ifdef` or `1. ifndef`\n\n#### Error 7\n\n**Problem: Preprocessor failed on file X - error 7.**\n\n**Known reasons: The preprocessor encountered an unknown directive. Read, as a typo most has likely found a way into the file (something like `1. in*lc*ude` or `1. defi*en*`). Double-check all preprocessor directives in that file.**\n\n### External links\n\n- [Operation Flashpoint - Preprocessor Guide (Wayback Machine)](https://ofp-faguss.com/files/ofp_preprocessor_explained.pdf)\n- [Collection of preprocessor test-cases](https://github.com/Krzmbrzl/ArmaPreprocessorTestCases)","sections":[{"heading":null,"level":1,"markdown":"The PreProcessor and the Config Parser allow to use macros in configs.\nThe purpose of macros is to re-use the same definition in scripts and configs multiple times.\nIt also gives a centralised place to correct errors in this definition.\nKnow that edge cases may arise - see the collection of test-cases linked at the end of this page.\n\n> [!NOTE]\n> **Arma 3:** In Arma 3, preprocessor commands are **case-sensitive!**"},{"heading":"Parsing","level":3,"markdown":"See #Config Parser below:\n- **Config.cpp** - parsed when PBO is binarised.\n  - localize cannot be used in macros, as it would hardcode string of current language instead of creating reference.\n- **Description.ext** - parsed when mission is loaded or previewed in missions list.\n- **SQF script** - parsed when preprocessFile, preprocessFileLineNumbers, compileScript or execVM is used."},{"heading":"Macros","level":3,"markdown":""},{"heading":"Strings","level":4,"markdown":"In both Configs and SQF scripts, there are two ways to define strings: either using single quotes (`'text'`) or double quotes (`\"text\"`).\nWhile they are both identical, they are **treated differently** by the preprocessor.\nThe **single quote** string contents are **parsed** by the preprocessor, while the **double quote** ones are **ignored**.\n\n```sqf\n#define ARG world\n\nsystemChat 'hello ARG'; // prints: hello world (i.e. ARG was replaced by the preprocessor with world)\nsystemChat \"hello ARG\"; // prints: hello ARG\n```\n\n> [!WARNING]\n> While it is possible in config to define a string without quotes (the preprocessor will act as if the value were wrapped with single quotes), this practice is heavily discouraged.\n>\n> ```cpp\n> title = \"$STR_TranslationKey\";    // good\n> title = $STR_TranslationKey;    // tolerated but incorrect\n> ```"},{"heading":"Comments","level":4,"markdown":"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.\nThe preprocessor removes all comments from the file before it is processed. Therefore, comments are never actually \"seen\" by the game engine.\n\n> [!WARNING]\n> Sometimes a problematic string can fail to purge these comment outs during preprocessing!\n>\n> ```sqf\n> private _a = '\"';\n> // this is just a comment, but failed to remove upon the preprocess so will return an error. Caused by that string\n> ```\n\n```cpp\n// this is a single-line comment\n\n/*\n    this is a\n    multi-line\n    comment\n*/\n\nmyValue = something; // only this part of the line is commented out\n\nmyArray = { \"apple\"/*, \"banana\"*/, \"pear\" }; // a portion in the middle of this line is commented out\n```"},{"heading":"#define","level":4,"markdown":"Using the `1. define` instruction, it is possible to define a keyword and assign a definition to it.\nThe 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_]*`).\nAs an example:\n\n```cpp\n#define true 1\n```\n\nThe above means that whenever *true* is used in a config, the parser will replace this with the value *1*.\n\nThe 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)\n\n```cpp\n#define MACRO test\nMACRO // preprocesses to test (without any spaces)\n\n#define MACRO    test // There's a tab between MACRO and test\nMACRO // preprocesses to \"    test\" (without quotes - they are only used to show that the tab character didn't get removed)\n```\n\nThe 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):\n\n```cpp\n#define MACRO#test\nMACRO // preprocesses to \"test\"\n```"},{"heading":"Arguments","level":5,"markdown":"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.\n\n```cpp\n#define CAR(NAME) displayName = NAME;\n```\n\nIf *CAR(\"Mini\")* is used, it will be replaced with *displayName = \"Mini\";*. Multiple arguments can also be used:\n\n```cpp\n#define BLASTOFF(UNIT,RATE) UNIT setVelocity [0,0,RATE];\n```\n\nMacro arguments may be composed of any characters, as long as they do not contain a comma (because commas are used as argument-delimiters).\nIf quotes are being used, they have to be balanced.\nThe 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).\nNote however that although the macro gets resolved properly, the comma gets removed from the String (probably a bug).\n\n```cpp\n#define MACRO(arg) arg\nMACRO(\"Some, content\") // preprocesses to \"Some content\" (note the missing comma)\n```\n\nQuote escaping is also not supported in this context (neither with double- nor with single-quotes)\n\n```cpp\n#define MACRO(arg) arg\nMACRO(\"Some \"\"content\"\"\") // preprocesses to \"Some \"\"content\"\"\"\n```\n\nPassing arrays with more than one element `[el1,el2,...]` as arguments into macros as well as any argument containing commas (e.g `\"some, sentence\"`) will need a small workaround:\n\n```cpp\n#define HINTARG(ARG) hint (\"Passed argument: \" + str ARG)\n```\n\nIncorrect usage:\n\n```cpp\nHINTARG([1,2,3,4,5,6,7,8,9,0]); // ERROR, won't even compile\n```\n\nCorrect usage:\n\n```cpp\n#define array1 [1,2,3,4,5,6,7,8,9,0]\nHINTARG(array1); // SUCCESS\n```\n\nThe 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:\n\n```cpp\n#define ONE foo\n#define TWO(ONE) ONE\nTWO(bar) // will preprocess to bar\n```"},{"heading":"Replacing parts of words","level":5,"markdown":"By default, only whole words can be replaced by arguments. If only a part of the word should be replaced, use the `1.` instruction.\nThis is necessary when either the start or the end of the argument connects to another character that is not a `****` (semi-colon) or  (space).\n\n```cpp\nclass NAME##_Button_Slider: RscText \\\n{ \\\n    model = \\OFP2\\Structures\\Various\\##FOLDER##\\##FOLDER; \\\n```\n\nIt is also possible to use the single *#* to convert an argument to a string.\n\n```cpp\nstatement = (this animate [#SEL, 0]); \\\n```"},{"heading":"Multi-line","level":5,"markdown":"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:\n\n```cpp\n#define DRAWBUTTON(NAME)\\\n    __EXEC(idcNav = idcNav + 4) \\\n    // ...\n```\n\n> [!NOTE]\n> 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."},{"heading":"Variadic Arguments (Arma 3 v2.24)","level":5,"markdown":"Reference https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html\n\nVariadic arguments allows to provide a variable number of arguments to a macro, instead of a fixed number.\n\nAnd there are several \"functions\" to work with these variable-length argument lists.\n\nA basic variadic macro looks like this\n\n```cpp\n#define MACRO(...) __VA_ARGS__\n```\n\nThere are a few special keywords and \"functions\" that are only valid when used inside a variadic macro\n- `__VA_ARGS__`\n- `__VA_OPT__(X)`\n- `__VA_OPT_INV__(X)`\n- `__VA_APPLY__(FUNC(,SEP))`\n- `__VA_SELECT__(index)`"},{"heading":"__VA_ARGS__","level":6,"markdown":"is a special keyword that will be replaced by all the macro arguments, separated by \",\". For example:\n\n```cpp\n#define MACRO(...) __VA_ARGS__\n\nMACRO() // \"\", Zero is also a valid number of arguments\nMACRO(a,b,c) // \"a,b,c\", the arguments are \"a\", \"b\" and \"c\"\nMACRO({str [1,2,3]}) // \"{str [1,2,3]}\", the arguments are \"{str [1\", \"2\" and \"3]}\"\n```\n\nThis simple variant can replace helper macros like CBA mod's ARR_1, ARR_2, ARR_3, with just one ARR(...) macro.\n\nIt can also be used to wrap code blocks, which was difficult to do without it because they can contain commas and its not easy to tell the number of commas, like in the example above."},{"heading":"__VA_OPT__(x) and __VA_OPT_INV__(x)","level":6,"markdown":"are simple functions to handle the special case of there being zero variadic arguments.\n`__VA_OPT__(x)` is only active if arguments are present. And the INV variant is only active if no arguments are present\nFor example:\n\n```cpp\n#define HAS_ARGS __VA_OPT__(Yes I have Arguments!)__VA_OPT_INV__(Nothing!)\n\nHAS_ARGS() // \"Nothing!\"\nHAS_ARGS(a) // \"Yes I have Arguments!\"\nHAS_ARGS(a,b,c) // \"Yes I have Arguments!\", no matter how many arguments, if there is at least one, the optional data is inserted\n```\n\nThis is useful when the arguments are forwarded to something like:\n\n```cpp\n#define LOG(message, ...) diag_log __VA_OPT__([) message __VA_OPT__(,) __VA_ARGS__ __VA_OPT__(])\n\nLOG(\"My Message\") // 'diag_log  \"My Message\"   '\nLOG(\"My Message\",1,2,3) // 'diag_log [ \"My Message\" , 1,2,3 ]  '\n```"},{"heading":"__VA_APPLY__(FUNC, SEPARATOR)","level":6,"markdown":"is similar to apply in that it applies a function(macro) on every argument. The second \"SEPARATOR\" argument is optional, it can be used to replace the usual \",\" separator with something different.\n\nThe macro passed to apply must have 0, 1 or 2 arguments.\n\nIf 0 arguments, every value will be replaced by the macro contents, for example:\n\n```cpp\n#define FUNC A\n#define TRANSFORM(...) __VA_APPLY__(FUNC)\nTRANSFORM(1,2,3) // 'A,A,A'\n```\n\nIf 1 argument, it will be the value of the argument, for example:\n\n```cpp\n#define QUOTE(x) #x\n#define TRANSFORM(...) __VA_APPLY__(QUOTE)\nTRANSFORM(1,2,3) // '\"1\",\"2\",\"3\"'\n```\n\nIf 2, the second argument MUST be named `__VA_INDEX__`, it will be filled with the index of the argument, for example:\n\n```cpp\n#define FUNC(x, __VA_INDEX__) __VA_INDEX__: #x\n#define TRANSFORM(...) __VA_APPLY__(FUNC)\nTRANSFORM(1,2,3) // '0: \"1\",1: \"2\",2: \"3\"'\n```\n\nBy default, just like `__VA_ARGS__` when the values are written to the output, they will be separated by \",\".\n\nThis can be changed by providing a separator argument, for example:\nFor example:\n\n```cpp\n#define VERBATIM(x) x\n#define SEPARATOR_NONE\n#define TRANSFORM_CONCAT(...) __VA_APPLY__(QUOTE, SEPARATOR_NONE)\nTRANSFORM_CONCAT(1,2,3) // '123'\n\n#define GET_VARIABLE(x) private _##x = player getVariable #x\n#define SEPARATOR_SEMI ;\n#define GET_VARIABLES(...) __VA_APPLY__(GET_VARIABLE, SEPARATOR_SEMI)\n\nGET_VARIABLES() // ''\nGET_VARIABLES(MyFnc) // 'private _MyFnc = player getVariable \"MyFnc\"'\nGET_VARIABLES(MyFnc,MyOtherFnc) // 'private _MyFnc = player getVariable \"MyFnc\";private _MyOtherFnc = player getVariable \"MyOtherFnc\"'\n```\n\nThe results of `__VA_APPLY__` can also be passed to another variadic macro, for example:\n\n```cpp\n#define QUOTE(x) #x\n#define WRITE_LOG(...) diag_log [__VA_ARGS__]\n#define MAKE_ARRAY(...) [__VA_ARGS__]\n\n#define DO_THINGS(...) WRITE_LOG(__VA_APPLY__(QUOTE)); _array = MAKE_ARRAY(__VA_ARGS__);\n\nDO_THINGS(a,b,c) // diag_log [\"a\",\"b\",\"c\"]; _array = [a,b,c];\n```"},{"heading":"__VA_SELECT__(index)","level":6,"markdown":"will select the specified element out of the `__VA_ARGS__` list. index must be a number.\nFor example:\n\n```cpp\n#define SELECT_ARG(index, ...) __VA_SELECT__(index)\n#define FORMAT_ARG(index) SELECT_ARG(index, \"A=%2\", \"B=%3\", \"C=%4\", \"D=%5\", \"E=%6\", \"F=%7\")\n#define FORMAT_GET_ARGSTR(arg, __VA_INDEX__) FORMAT_ARG(__VA_INDEX__)\n#define FORMAT(MESSAGE, ...) format [[__VA_OPT_INV__('%1') __VA_OPT__('%1:',) __VA_APPLY__(FORMAT_GET_ARGSTR)] joinString ' ', MESSAGE __VA_OPT__(,) __VA_ARGS__]\n\nFORMAT(\"Message\") // `format [['%1'  ] joinString ' ', \"Message\"  ]`\nFORMAT(\"Message\", value1, value2) // `format [[ '%1:',  \"A=%2\", \"B=%3\"] joinString ' ', \"Message\" ,  value1, value2]` which ends up as `format [\"%1: A=%2 B=%3\", \"Message\", value1, value2]`\n```"},{"heading":"#undef","level":4,"markdown":"Undefine (delete) a macro previously set by the use of #define.\n\n```cpp\n#undef NAME\n```"},{"heading":"#if","level":4,"markdown":"**Description:** Checks condition and processes content if condition returns 1. Skips content if it returns 0\n\n```cpp\n#if __GAME_VER_MAJ__ != 2\n// Do something...\n#endif\n```"},{"heading":"Comparison Operators","level":5,"markdown":"The following operations are supported:\n- <\n- >\n- <=\n- >=\n- ==\n- !=\n**Example:**\n\n```cpp\n#if __GAME_VER_MAJ__ != 2\n// Do something...\n#endif\n```"},{"heading":"#ifdef","level":4,"markdown":"A simple if-then construction to check whether a certain set of definitions has already been made:\n\n```cpp\n#ifdef NAME\n    // config that will be used if NAME is defined\n#endif\n```\n\nIFDEFs *cannot* be nested. The preprocessor will generate errors for all inner definitions if the outer definition doesn't exist."},{"heading":"#ifndef","level":4,"markdown":"Same as #ifdef, but checks for absence of definition instead.\n\n```cpp\n#ifndef NAME\n    // config that will be used if NAME is -not- defined\n#endif\n```"},{"heading":"#else","level":4,"markdown":"Provides alternative code to `1. if`, `1. ifdef`, `1. ifndef` checks.\n\n```cpp\n#ifndef NAME\n    // config that will be used if NAME is -not- defined\n#else\n    // config that will be used if NAME -is- defined\n#endif\n```"},{"heading":"#endif","level":4,"markdown":"This ends a conditional block as shown in the descriptions of #ifdef and #ifndef above."},{"heading":"#include","level":4,"markdown":"Copies the code from a target file and pastes it where #include directive is.\n\n```cpp\n#include \"file.hpp\"\n#include <file.txt> // Brackets are equivalent to quotation marks and may be used in their place.\n```\n\nSource directory is:\n- For any file without starting the include path with \\ - the file's current directory\n- When starting with \\ - the internal filesystem root (see Addon Prefix) or the Game's working directory (only with -filePatching enabled)\n\nA path beginning can be defined as follow:\n- drive (only with -filePatching enabled): \n\n```cpp\n#include \"D:\\file.txt\"\n```\n\n- PBO with PBOPREFIX: \n\n```cpp\n #include \"\\myMod\\myAddon\\file.txt\"\n```\n\n- 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):\n\n```cpp\n#include\"\\myMod\\myAddon\\file.txt\" // Arma 3\\@myMod\\addons\\myAddon.pbo\\file.txt;\n```\n\nTo move to parent directory use '..' (two dots) (Supported since Arma 3 v1.50):\n\n```cpp\n#include \"..\\file.sqf\"\n```\n\nPreprocessor does not support the use of macros for pre-defined file names.\n\n```cpp\n#define path \"codestrip.txt\"\n#include path // this will cause an error\n```"},{"heading":"#","level":4,"markdown":"'#' (single hash) operator wraps the text with quotation marks.\n\n```cpp\n#define STRINGIFY(s) #s\n#define FOO 123\ntest1 = STRINGIFY(123); //test1 = \"123\";\ntest2 = STRINGIFY(FOO); //test2 = \"123\";\n```\n\nThis operator does only work on keywords following the rules for macro-names (see `1. define`-section).\nIf 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).\n\n```cpp\n#define MACRO Test #123\nMACRO // preprocesses to Test 123 - note that there are not any quotes inserted\n```"},{"heading":"##","level":4,"markdown":"'##' (double hash) operator concatenates what's before the ## with what's after it.\n\n```cpp\n#define GLUE(g1,g2) g1##g2\n#define FOO 123\n#define BAR 456\ntest1 = GLUE(123,456); //test1 = 123456;\ntest2 = GLUE(FOO,BAR); //test2 = 123456;\n```"},{"heading":"__LINE__","level":4,"markdown":"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."},{"heading":"__FILE__","level":4,"markdown":"This keyword gets replaced with the CURRENT file being processed.\ne.g\n \"userconfig\\file1.sqf\""},{"heading":"__FILE_NAME__ (Arma 3 v2.18)","level":4,"markdown":"This keyword gets replaced with the **name** of the current file being processed.\ne.g\n \"file1.sqf\""},{"heading":"__FILE_SHORT__ (Arma 3 v2.18)","level":4,"markdown":"This keyword gets replaced with the **name** of the current file being processed, without extension.\ne.g\n \"file1\""},{"heading":"__has_include (Arma 3 v2.02)","level":4,"markdown":"**Description:** Checks if given file exists. Returns 1 if it does, 0 if it does not exist.\n\n> [!IMPORTANT]\n> - The file path must start with the backslash `\\` otherwise it would fail silently (returns `0` so does nothing unless you put something in `1. else`)!\n> - 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*!\n\n> [!NOTE]\n> To validate that the file exists in your game, you can use fileExists command to check.\n>\n> ```sqf\n> fileExists \"\\a3\\data_f\\config.bin\"; // returns true\n> fileExists \"\\z\\ace\\addons\\main\\script_component.hpp\"; // returns true if ACE is loaded, false otherwise\n> ```\n\n```cpp\n#if __has_include(\"\\z\\ace\\addons\\main\\script_component.hpp\")\n// file exists! Do something with it\n#else\n// file does not exist\n#endif\n```"},{"heading":"__DATE_ARR__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Is replaced for current date in format array.\n\n```cpp\n__DATE_ARR__ // 2020,10,28,15,17,42\n```"},{"heading":"__DATE_STR__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Is replaced for current date in format string.\n\n```cpp\n__DATE_STR__ // \"2020/10/28, 15:17:42\"\n```"},{"heading":"__DATE_STR_ISO8601__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Is replaced for current date in format string. The date is presented in ISO8601 standard.\n\n```cpp\n__DATE_STR_ISO8601__ // \"2020-10-28T14:17:42Z\"\n```"},{"heading":"__TIME__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Is replaced for current time.\n\n```cpp\n__TIME__ // 15:17:42\n```"},{"heading":"__TIME_UTC__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Is replaced for UTC time.\n\n```cpp\n__TIME_UTC__ // 14:17:42\n```"},{"heading":"__DAY__ (Arma 3 v2.16)","level":4,"markdown":"**Description:** Is replaced for UTC day.\n\n```cpp\n__DAY__ // 20\n```"},{"heading":"__MONTH__ (Arma 3 v2.16)","level":4,"markdown":"**Description:** Is replaced for UTC month.\n\n```cpp\n__MONTH__ // 11\n```"},{"heading":"__YEAR__ (Arma 3 v2.16)","level":4,"markdown":"**Description:** Is replaced for UTC year.\n\n```cpp\n__YEAR__ // 2023\n```"},{"heading":"__COUNTER__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Counts 1 up everytime it is defined.\n\n```cpp\n__COUNTER__ // 0\n__COUNTER__ // 1\n__COUNTER__ // 2\n__COUNTER__ // 3\n__COUNTER_RESET__\n__COUNTER__ // 0\n__COUNTER__ // 1\n__COUNTER__ // 2\n```"},{"heading":"__TIMESTAMP_UTC__ (Arma 3 v2.06)","level":4,"markdown":"**Description:** Is replaced to the current timestamp in UNIX timestamp.\n\n```cpp\n__TIMESTAMP_UTC__ // 1622639059\n```"},{"heading":"__COUNTER_RESET__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Resets counter. See example above."},{"heading":"__RAND_INT`N`__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Gets replaced by a random `N` bit integer. `N` can be 8, 16, 32 or 64, e.g:\n\n```cpp\n__RAND_INT8__\n__RAND_INT16__\n__RAND_INT32__\n__RAND_INT64__\n```\n\n```cpp\n__RAND_INT8__ // e.g -102\n```"},{"heading":"__RAND_UINT`N`__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Gets replaced by a random *unsigned* `N` bit integer. `N` can be 8, 16, 32 or 64, e.g:\n\n```cpp\n__RAND_UINT8__\n__RAND_UINT16__\n__RAND_UINT32__\n__RAND_UINT64__\n```\n\n```cpp\n__RAND_UINT8__ // 108\n```"},{"heading":"__GAME_VER__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Gets replaced by the game version.\n\n```cpp\n__GAME_VER__ // 02.00.146790\n```"},{"heading":"__GAME_VER_MAJ__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Gets replaced by the *major* game version.\n\n```cpp\n__GAME_VER_MAJ__ // 02\n```"},{"heading":"__GAME_VER_MIN__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Gets replaced by the *minor* game version.\n\n```cpp\n__GAME_VER_MIN__ // 00\n```"},{"heading":"__GAME_BUILD__ (Arma 3 v2.02)","level":4,"markdown":"**Description:** Gets replaced by the build number.\n\n```cpp\n__GAME_BUILD__ // 146790\n```"},{"heading":"__A3_DIAG__ (Arma 3 v2.12)","level":4,"markdown":"**Description:** Gets replaced by 1 on diag, undefined otherwise.\n\n```cpp\n__A3_DIAG__// 1\n```"},{"heading":"__ARMA__ (Arma 3 v2.02)","level":4,"markdown":"**Description:**\n\n```cpp\n__ARMA__ // 1\n```"},{"heading":"__ARMA3__ (Arma 3 v2.02)","level":4,"markdown":"**Description:**\n\n```cpp\n__ARMA3__ // 1\n```"},{"heading":"__A3_DEBUG__ (Arma 3 v2.02)","level":4,"markdown":"**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.\n\n```cpp\n__A3_DEBUG__ // 1 (if -debug was enabled)\n```"},{"heading":"__A3_EXPERIMENTAL__ (Arma 3 v2.15)","level":4,"markdown":"**Description:** This macro is only set in experimental game builds (Development branch or Profiling branch)\n\n```cpp\n__A3_EXPERIMENTAL__ // 1 (if on dev or profiling branch)\n```\n\ncan 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)\n\n```cpp\n#if __GAME_VER_MIN__ >= 16 //#TODO remove this workaround when 2.16 is released\n#define BUG_FIXED\n#endif\n\n#if __GAME_BUILD__ > 151035 // Builds newer than this\n#if __A3_EXPERIMENTAL__ // But on experimental only\n#define BUG_FIXED\n#endif\n#endif\n\n#ifdef BUG_FIXED\nuse the thing\n#else\ndon't use the thing and have some workaround\n#endif\n```"},{"heading":"__A3_PROFILING__ (Arma 3 v2.15)","level":4,"markdown":"**Description:** This macro is only set when Profiling commands are available (Development branch diag binary or in Profiling branch profiling binary)\n\n```cpp\n#if __A3_PROFILING__\ndiag_captureFrame 1;\n#endif\n```"},{"heading":"Config Parser","level":3,"markdown":"> [!IMPORTANT]\n> - `__EXEC` and `__EVAL` **cannot** be used in preprocessor #Macros, e.g **##if**!\n\n> [!NOTE]\n> - `__EXEC` and `__EVAL` can be used in configs (such as config.cpp, Description.ext, RVMat etc) but are not suitable for SQF or SQS scripts.\n> - As they use parsingNamespace, both global and local variables set in `__EXEC` are available in `__EVAL`.\n> - parsingNamespace can then be accessed through scripting; see the __EXEC example below."},{"heading":"__EXEC","level":4,"markdown":"This **Config Parser** macro allows to assign values to internal variables or just execute arbitrary code.\nThe 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:\n\n```cpp\n__EXEC(cat = 5 + 1;)\n__EXEC(lev = cat - 2;)\n```\n\n```sqf\n_cat = parsingNamespace getVariable \"cat\"; // 6\n_lev = parsingNamespace getVariable \"lev\"; // 4\n```\n\n> [!WARNING]\n> `__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:\n>\n> ```cpp\n> __EXEC(a = (1+2);) // ERROR\n> ```\n>\n> ```cpp\n> __EXEC(_expr = 1+2;)\n> __EXEC(a = _expr;) // OK\n> ```"},{"heading":"__EVAL","level":4,"markdown":"With this **Config Parser** macro expressions can be evaluated, including previously assigned internal variables. Unlike `__EXEC`, `__EVAL` supports multiple parentheses:\n\n```cpp\nw = __EVAL(safeZoneW - (5 * ((1 / (getResolution select 2)) * 1.25 * 4)));\n```\n\n- `__EVAL` macros **must** be assigned to a config property and the expression **must** be terminated with `****`.\n- `__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\"`).\n\n> [!NOTE]\n> if code is needed in the expression, use compile String instead. Like so:\n>\n> ```cpp\n> result = __EVAL(call compile \"123\");\n> ```\n>\n> that is because `__EVAL` is incapable of dealing with curly brackets `{`}"},{"heading":"Errors","level":3,"markdown":""},{"heading":"Error 2","level":4,"markdown":"**Problem: Preprocessor failed error 2.**\n\n**How to fix: Add quotation marks in the title, or the file path. (*e.g.* `1. include \"soGood.sqf\"`).**"},{"heading":"Error 6","level":4,"markdown":"**Problem: Preprocessor failed on file X - error 6.**\n\n**Known reasons:**\n\"The problem is using `1. ifdef #ifdef #endif #endif`, a.k.a nested `1. ifdef`. This doesn't work in Arma. It's only possible to use `1. ifdef` and `1. endif` once and not nested.\"\n`1. endif` without preceding `1. ifdef` or `1. ifndef`"},{"heading":"Error 7","level":4,"markdown":"**Problem: Preprocessor failed on file X - error 7.**\n\n**Known reasons: The preprocessor encountered an unknown directive. Read, as a typo most has likely found a way into the file (something like `1. in*lc*ude` or `1. defi*en*`). Double-check all preprocessor directives in that file.**"},{"heading":"External links","level":3,"markdown":"- [Operation Flashpoint - Preprocessor Guide (Wayback Machine)](https://ofp-faguss.com/files/ofp_preprocessor_explained.pdf)\n- [Collection of preprocessor test-cases](https://github.com/Krzmbrzl/ArmaPreprocessorTestCases)"}],"categories":["Scripting Topics","Stubs"],"groups":["Scripting"]}