Home   Help Search Login Register  

Author Topic: The many ways to create and use SQF functions  (Read 5087 times)

0 Members and 1 Guest are viewing this topic.

Offline ModestNovice

  • Members
  • *
The many ways to create and use SQF functions
« on: 29 Oct 2008, 02:47:31 »
Hey Spooner I was wondering what these preProcessFileLineNumbers and preProcessFile commands meant.

As on the biki it states something to do with #include statements.

Though sometimes the file it points to doesnt have any #include statements.

What do these commands actually do?
« Last Edit: 29 Oct 2008, 03:53:22 by Spooner »
"The road became empty and the people disappeared. The clouds ran away; opened up the sky, and one by one I watched every constellation die."
- Sean "Slug" Daley

Offline Spooner

  • Members
  • *
  • Mostly useless
    • Community Base Addons
The many ways to create and use SQF functions
« Reply #1 on: 29 Oct 2008, 03:52:27 »
The BIKI descriptions are extremely vague, but I updated the ones at our COMREF when people last got confused about them.

Been meaning to start a little tutorial on this topic for a while:

Concurrent (Parallel) functions
To all intents and purposes,
Code: (slower, uses slightly less memory) [Select]
// Compile and run once.
[] execVM "frog.sqf";
is the same as:
Code: (slower, uses slightly less memory) [Select]
// Run once and throw away the compiled function.
[] spawn compile preprocessFileLineNumbers "frog.sqf";
or
Code: (faster, uses slightly more memory) [Select]
// Compile once and save the function in memory.
frogFunction = compile preprocessFileLineNumbers "frog.sqf";

// later...

// Run it as many times as you like later without having to recompile it...
_result = [] spawn frogFunction;

_result = [] spawn frogFunction;
Both of these will run a script in parallel with the current script, thus immediately continuing with the current script. The new script will start running in the next frame, which would, for example, be after about 0.05s if you are getting 20fps.

Generally, try to avoid starting concurrent functions unless you really intend to run them in parallel with the current function, since they are slightly more costly to run compared functions started with run. Same thing goes if you need it to run RIGHT NOW rather than a bit later.

Immediate functions
Code: (slower, uses slightly less memory) [Select]
// Run once and throw away the compiled function.
_result = [] call compile preprocessFileLineNumbers "frog.sqf";
and
Code: (faster, uses slightly more memory) [Select]
// Compile once and save the function in memory.
frogFunction = compile preprocessFileLineNumbers "frog.sqf";

// later...

// Run it as many times as you like later without having to recompile it...
_result = [] call frogFunction;

_result = [] call frogFunction;

Will do the same sort of thing, but instead of just starting up the other script and leaving it to its own devices, it will immediately run the complete script and then the last value in that script (or last value in a exitWith block that is run) will be put into _result. Only then, will the original script continue on to the next statement.

Loading function files into the game
* loadFile - this loads the file directly into a string without doing anything to it at all (no preprocessing). If you compile this and run it using any method, then any syntax or runtime errors will be vague. It should only be used when you want a file put into a string so you can read it manually. Don't ever use it for loading functions (i.e. don't use it with compile).
* preprocessFile- this loads the file into a string, while replacing preprocessor directives (# commands like #include and #define) appropriately. If you compile this string and run it using any method, then any syntax or runtime errors will be vague. Unless you are using OFP, never use this for loading text files for use with compile.
* preprocessFileLineNumbers- this loads the file into a string, while replacing preprocessor directives (# commands) appropriately. If you compile this string and run it using any method, then any syntax or runtime errors will be detailed, like you see with execVMed files, showing both file-name and exact line the error occured on. This is the only way you should be loading files for use with compile if you are using ArmA (it isn't available in OFP).

Creating functions inside files
Functions can be created inside a function file that is being run with call/spawn/execVM too, using curly braces, {}:
Code: [Select]
myFunction =
{
    hint "hello world";
    counter = counter + 5;
};

// later...

[] call myfunction;
The error messages of functions created in this way will be of the same type as the function file they are in. That is, if the file was run with execVM or loaded with preprocessFileLineNumbers then the functions inside it will also give verbose error messages, but if it was loaded with loadFile or preprocessFile then the messages will be vague.

Dynamic functions
Dynamic functions must be created if you don't know what they will contain until you run the script. They use format to create code on the fly, which is then fed to compile and run just like any other function. These types of function always give vague error messages, regardless of what type of errors the file would normally give. This is one of the reasons they should be avoided whenever possible.

One of the most common uses of dynamic function generation is to create numbered variable names which creates a sort of pseudo-array (var1, var2, var3, etc). However, in the vast majority of cases, this is unnecessary and could be more efficiently accomplished using regularly scripted arrays that don't require code generation and thus are less likely to produce terse error messages for you which will be hard to track down.

--

Well, that is more detailed than you asked for, and a lot more detailed than I intended to write, but I hope it explains things a little clearer. Also off the top of my head and not proof-read, so do question any things that don't make sense because they could be...wrong ;P
« Last Edit: 29 Oct 2008, 04:08:34 by Spooner »
[Arma 2] CBA: Community Base Addons
[Arma 1] SPON Core (including links to my other scripts)

Offline ModestNovice

  • Members
  • *
Re: The many ways to create and use SQF functions
« Reply #2 on: 29 Oct 2008, 04:25:21 »
Wooooowww...fantastic Spooner  :good:

Thank you  :)
"The road became empty and the people disappeared. The clouds ran away; opened up the sky, and one by one I watched every constellation die."
- Sean "Slug" Daley

Offline Spooner

  • Members
  • *
  • Mostly useless
    • Community Base Addons
Re: The many ways to create and use SQF functions
« Reply #3 on: 30 Oct 2008, 17:00:55 »
Sickboy asked me about my use of the term "Function" to generally mean all SQF being run, where most people see files as "scripts" and anything compiled into a variable as a "function":

There is a difference between implementation and usage when it comes to "functions" and "procedures" and "scripts". All SQF is implemented the same as "functions" (runs code and returns a value, even if most of the time this is nil and/or is ignored by the caller) but you can consider individual ArmA "functions" to be conceptually "pure functions" (returns a value and doesn't alter external data, such as using setPos or changing a global variable) or "procedures/subroutines/etc" (may or may not return a value, but may affect external data).

As I explained earlier, execVM (and SQF files called from actions, for that matter) is equivalent to spawn compile preprocessFileLineNumbers, so all SQF is really being either called or spawned on some level. Thinking of it another way, there is really absolutely no difference between:
Code: (helloWorld.sqf) [Select]
hint "Hello World";
Code: [Select]
helloWorld = compile preprocessFileLineNumbers "helloWorld.sqf";
call helloWorld;
and
Code: [Select]
helloWorld =
{
    hint "Hello World";
};
call helloWorld;
« Last Edit: 30 Oct 2008, 17:15:15 by Spooner »
[Arma 2] CBA: Community Base Addons
[Arma 1] SPON Core (including links to my other scripts)

Offline SNKMAN

  • Members
  • *
Re: The many ways to create and use SQF functions
« Reply #4 on: 22 Jul 2010, 20:20:37 »
Really nice post Spooner. ;)

My question now is would it be better to use "[] execVM "frog.sqf";" or "[] spawn compile preprocessFileLineNumbers "frog.sqf";" or is there no noticable different?

Would be really greate to have more of such skilled knowlege sharing posts.