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) functionsTo
all intents and purposes,
// Compile and run once.
[] execVM "frog.sqf";
is the same as:
// Run once and throw away the compiled function.
[] spawn compile preprocessFileLineNumbers "frog.sqf";
or
// 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// Run once and throw away the compiled function.
_result = [] call compile preprocessFileLineNumbers "frog.sqf";
and
// 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 filesFunctions can be created inside a function file that is being run with
call/spawn/execVM too, using curly braces, {}:
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 functionsDynamic 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