pierremgi 4850 Posted November 29, 2018 Hi all, I spent hours trying to understand how to declare some big arrays from description.ext. I had working scripts with usual functions/ variables in sqf til I decided to manage functions in cfgFunctions (this part is OK), but I fail to understand how variables work with preprocessor: I tried to add a variable.hpp supposed to host the variables then #include variables.hpp in description.ext, before the include for functions using these variables. my variables.hpp looks like this for test: #define random_loot_array ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons")) Note that: random_loot_array = ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons")); works fine (formerly used in the function). I guess I can't define a variable referring to the bunch of commands (getText, getNumber, configClasses...) But how to declare such (permanent - non parametered) variables in a hpp? If any added value for readability or performance. Thanks 1 Share this post Link to post Share on other sites
HazJ 1289 Posted November 29, 2018 You need to use _EVAL to do this. https://community.bistudio.com/wiki/PreProcessor_Commands#EVAL Share this post Link to post Share on other sites
pierremgi 4850 Posted November 29, 2018 Tested: random_loot_array = __EVAL ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons")) even: w = __EVAL ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons")) #define random_loot_array w random_loot_array stays undefined. I'm lost with these prepro commands. Share this post Link to post Share on other sites
HazJ 1289 Posted November 29, 2018 Can't help much until this afternoon as out at the office. I will look when home and reply. Share this post Link to post Share on other sites
Dedmen 2692 Posted November 29, 2018 8 hours ago, pierremgi said: but I fail to understand how variables work with preprocessor They.. eh.. don't? 8 hours ago, pierremgi said: in description.ext, before the include for functions using these variables. What? Can you explain what you are trying to do, and why you are trying it? EVAL won't work, eval only supports a limited set of commands, and config lookups certainly aren't supported. Share this post Link to post Share on other sites
pierremgi 4850 Posted November 29, 2018 Just, on my trial to optimize some working codes, as I said, I declared some sqf into functions and this stage works. Now, I never declared any variable in a hpp except for dialogs. I wondered if it was possible to proceed in a similar way So I started by creating a: variables.hpp and wrote a ridiculous: #define random_loot_array ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons")) On my mind, this was a way to "prepare", "define", or any term you want, this variable, gaining some performance or "touch of class in coding", instead of running it in two different functions. And, I #include "variables.hpp" before #include "functions.hpp" That seemed to me logical. I'm aware I'm noob for all hpp things, On HazJ comment, I took a bunch of time just for writing __EVAL instead of _EVAL (The difference wasn't an evidence)... with no success in attempts I reported. Share this post Link to post Share on other sites
Dedmen 2692 Posted November 30, 2018 I don't know what you mean with declaring a variable in a hpp? You can't declare variables in configs. Also hpp says absolutely NOTHING about what it's supposed to be. You can #include a hpp into whereever, and it will have a different meaning everywhere. #include basically just copy-pastes things into your file. And #define defines a name that every occurrence of that name will be replaced by the text that you write in the #define. You know that right? All that #include and #define do is copy-paste text around. You are not declaring any variables anywhere. 1 Share this post Link to post Share on other sites
mrcurry 496 Posted November 30, 2018 I feel this part of the code optimization page might be relevant to the discussion: https://community.bistudio.com/wiki/Code_Optimisation#Constants_2 @Dedmen is correct though, #define DUDE 1 does not declare a variable DUDE which is equal to 1, it merely let's the preprocessor know to replace DUDE with 1 when preprocessing the current file. Writing if( DUDE == 1 ) would in runtime execute as if( 1 == 1 ) 1 Share this post Link to post Share on other sites
pierremgi 4850 Posted November 30, 2018 Excellent! Thank you so much all! I totally missed the #include as a simple insertion, and misunderstood the #define functionality. Now, let see how to make it work: #include "variables.hpp" (description.ext) then, in variables.hpp (first attempt): myVar = ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons")); 1. a simple recall of myVar fails (not defined at all) but, reading the @mrcurry link for optimization, i tend to reach my goal: 2. getMissionConfigValue "myVar" returns the chain: ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons")) but not the value... So tested (2nd attempt): myVar = __EVAL ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons")); Now, getMissionConfigValue "myVar" returns the working array, but wrapped inside a string (in debug console)... "[bin\config.bin/CfgWeapons/CUP_launch_M72A6,......]" So, the good question is, should I compile (getMissionConfigValue "myVar") ? emmh... call compile (getMissionConfigValue "myVar") doesn't return anything... tested also: parseSimpleArray (getMissionConfigValue "myVar"), returns [<nul>,<nul>,<nul>,......] >> And how could this improve performance as described here?... EDITED Share this post Link to post Share on other sites
mrcurry 496 Posted November 30, 2018 1 hour ago, pierremgi said: So tested (2nd attempt): myVar = __EVAL ("getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons")); Now, getMissionConfigValue "myVar" returns the working array, but wrapped inside a string (in debug console)... The output of your _EVAL is interpreted as a string, try myVar[] = _EVAL(... Note the brackets Share this post Link to post Share on other sites
pierremgi 4850 Posted November 30, 2018 myVar[] = __EVAL ("getText (_x >>..... throws an error _ encountered instead of { Share this post Link to post Share on other sites
mrcurry 496 Posted December 1, 2018 Silly me, myVar[] = is a array declaration and requires { } around the array not [ ], the array declaration also seems to be processed before the __EVAL since the error thrown is for _ not [ or ". We also need to remember that configs only support simple data types like scalar, string and nD-arrays consisting of scalar(s) and/or string(s). If you really need to return an array of CONFIG types see the "if all else fails" at the bottom. So I finally popped on my main PC and did some tests of my own. Array as config entry This is the best I've managed to come up with so far that is along your original line of thought. Keep in mind we have to convert the configs into simple data types if you want to store it in description.ext or any other config file: //description.ext myVar = __EVAL ( "getText (_x >> 'DLC') == 'CUP_Weapons' && getnumber (_x >> 'scope') >= 2 && (getNumber (_x >> 'type') in [1,2,4])" configClasses (configfile >> "CfgWeapons") apply compile "configName _x" ); //runtime call private _classes = parseSimpleArray getMissionConfig "myVar"; //or private _classes = call compile getMissionConfig "myVar"; Yes that "apply compile" is required, __EVAL does not take kindly to the } bracket and makes the preprocessor interpret it as eof for some reason. Now I haven't tested if the "call compile getMissionConfig" is faster than the just straight up calling the configClasses and apply during runtime but it does achieve the "array as config constant" criteria. Pros: Mod version independent, pretty much no upkeep. Cons: Requires a call to parseSimpleArray, a runtime compile or similiar action. For big arrays this might be a big no no. Optimisation A possible optimisation, that might be impossible, is to somehow convert the value from the above __EVAL to an array-type config entry similiar to this: myVar[] = {"class1", "class2", ... , "classN"}; The problem is I have yet to figure out a way to do this using the preprocessor. I suppose treating the returned array like a string and doing a find & replace might work but that's a big maybe. You still have to get around the fact that myVar[] = expects to be followed by a {} pair. The easy way Another option is to just straight up define the array with classnames like below, this method does make mission upkeep a bit more involved since every update to the mod or vanilla might mean you need to update the list. The runtime call should be fast though which is nice. //1. Get the array using configClasses apply {configName _x} in the debug console. //2. Copy the result to your favorite text editor. //3. Replace the first [ and last ] with { and } //4. Paste the into the config like so: //description.ext cup_weaponClasses[] = {"class1", "class2", ... "classN"}; //runtime call getMissionConfig "cup_weaponClasses" //Returns an array of classnames If all else fails A global variable computed during pre-Init has never hurt anyone right? ^^ The goal is to get the array-assembly out of runtime code so it doesn't have to run multiple times to regenerate what is essentially a static data set, correct? If so setting up a pre-init function to compute the array contents is probably the best bet. You'll have full access to all SQF data types and don't have to pull your hair over config syntax. 2 2 Share this post Link to post Share on other sites
pierremgi 4850 Posted December 1, 2018 Thank you so much! Let the included hpp and constants optimization (red dot critical!) for simple data, or "array of simple elements". All "computed arrays", like I wanted for configClasses filter, can be grouped in a function. I will test a pre-init one as you mentioned. But I guess that, if I declare it or include it first in description.ext, the engine will read it, and the other functions (using the variables) will not return any error. In other words, is pre-init mandatory? Share this post Link to post Share on other sites
mrcurry 496 Posted December 1, 2018 8 minutes ago, pierremgi said: In other words, is pre-init mandatory? Really depends on when you expect the data to be used. I just chose pre-init cause it guarantees execution before most if not all other scopes. 1 Share this post Link to post Share on other sites
pierremgi 4850 Posted December 1, 2018 Thanks all. Special thanks to you @mrcurry Now, I'm digging deeper in the abyss of optimization. I just find a topic, with good comments for general optimization and various interesting things about scheduler and its usage. 1 Share this post Link to post Share on other sites
pierremgi 4850 Posted December 2, 2018 Well, I added my configClasses filter as variable in an sqf, then added this sqf in my hpp with all other functions. Something like: class MGI { class missionVars { file = "functions"; class serverVars { preInit = 1; }; class publicVars { preInit = 1; }; }; class basicFnc { file ="functions\basic"; class distanceGrp {}; // fn_distance.sqf class randomPos {}; class punishKill {}; class punishrelax {}; class rewarding {}; class addDamageEffect {}; class silencer {}; ... etc What I remarked: - If I don't write preInit, so the variables are not "called" (sorry for the exact term), and then not defined while in game. - if I add preInit =1, that works, variables are "read" and "called" then defined. But the weird thing is I catch an error: functions\fn_serverVars.sqf not found when I save and open a new scenario. It's weird because I wrote preInit = 1, supposed to be for the mission scope, (and not preStart = 1 supposed to be for Arma session and config.cpp addons), if I'm right. I can live with that, but if there is a work around for "calling" the function (in it's class), That could be great! Thanks. Share this post Link to post Share on other sites
Dedmen 2692 Posted December 4, 2018 On 30.11.2018 at 5:33 PM, pierremgi said: So, the good question is, should I compile No. Stringified configs are not parseable. which is why On 30.11.2018 at 5:33 PM, pierremgi said: tested also: parseSimpleArray (getMissionConfigValue "myVar"), returns [<nul>,<nul>,<nul>,......] that happens. On 1.12.2018 at 12:24 PM, mrcurry said: and 1d-arrays Nope. You can have ND arrays all you want. On 1.12.2018 at 12:24 PM, mrcurry said: Requires a runtime compile No it doesn't parseSimpleArray should work in that case. On 1.12.2018 at 12:24 PM, mrcurry said: If so setting up a pre-init function to compute the array contents is probably the best bet. That would be my recommendation too. On 1.12.2018 at 3:55 PM, pierremgi said: red dot critical! Did you even think about what you read and what you are actually trying to do there? Why do you think would this be better than just computing it once at preInit or in init.sqf? So in init.sqf, you execute the code once and store it in a variable. In description.ext, before even the mission is loaded (just trying to display the mission name in the mission selection screen has to parse the description.ext) your code is parsed, executed, and the result turned into a string. If you then choose to really start the mission you just clicked on, you will then have to take the string out and parse it back into an array. If you don't actually start that mission, you've just computed a potentially expensive and completely useless value that will be thrown away again. Performance wise the description.ext variant is simply stupid nonsense. there is 0 performance advantage. Yes, It's great if you just need something small like a random number or something, for that the solution is better than compileFinal'ing a piece of code with the value and calling that over and over again. But for things like this, it's just utterly stupid. The Code optimization wiki page assumes that you still take everything with a grain of salt and really think about what you are doing there. I'll see that I get that red dot removed, or add a big fat disclaimer to that explaining what it actually is doing. Share this post Link to post Share on other sites
pierremgi 4850 Posted December 7, 2018 @Dedmen I'd like your explanation if you didn't abuse of the term "stupid". That's probably what you think because you're so skilled for this part of scripting. Nevertheless, I hope this post will help some guys who could ask themselves similar question about optimization, without suffering your criticism. 1 1 Share this post Link to post Share on other sites