-
Notifications
You must be signed in to change notification settings - Fork 48
Using #mfunc magic
On a glance, GameMaker macros might seem pretty similar to C macros, but not exactly:
- They are token-based, not text-based.
This is mostly a good thing and saves you from a couple strange errors. - GML macros do not support arguments.
The second part is a bit of a shame - in C/C++, macros can be used for ad-hoc implementations of language features, ranging from simple shortcuts to generating entire fields and types. However, that can be fixed with a bit of preprocessing.
Given
#mfunc view_get_xview(i) camera_get_view_x(view_camera[i])
var vx = view_get_xview(0);
this would be translated to and from
//!#mfunc view_get_xview {"args":["i"],"order":[0]}
#macro view_get_xview_mf0 camera_get_view_x(view_camera[
#macro view_get_xview_mf1 ])
var vx = view_get_xview_mf0 0 view_get_xview_mf1;
Or, for an example of something you cannot do with a script,
#mfunc swap(a, b) { var __swap = a; a = b; b = __swap; }
var a = 1, b = 2;
show_debug_message(string(a) + " " + string(b));
swap(a, b);
show_debug_message(string(a) + " " + string(b));
<->
//!#mfunc swap {"args":["a"," b"],"order":[0,0,1,1]}
#macro swap_mf0 { var __swap =
#macro swap_mf1 ;
#macro swap_mf2 =
#macro swap_mf3 ;
#macro swap_mf4 = __swap; }
var a = 1, b = 2;
show_debug_message(string(a) + " " + string(b));
swap_mf0 a swap_mf1 a swap_mf2 b swap_mf3 b swap_mf4;
show_debug_message(string(a) + " " + string(b));
Pretty straightforward really,
#mfunc <name>(<...arguments>) <...code>
#mfunc <name>(<...arguments>) as "<type>" <...code>
then, each use of argument by-name inside code will be replaced by the passed in expression.
The second version allows you to specify how your macro should be highlighted - e.g. if you are essentially adding a new keyword, highlighting it accordingly is nice:
#mfunc case2(a, b) as "keyword" case a: case b
switch (get_integer("Pick a number!", 0)) {
case2(1, 2): show_message("You picked 1 or 2!"); break;
default: show_message("You picked something else!");
}
also see Finding what to style on discovering token types.
If you would like variable arguments, you can use ...
:
#mfunc log(tag, ...) show_debug_message(tag + ": " + string([...]))
log("test", "hi!", "hello!"); // -> show_debug_message("test: " + string(["hi!", "hello!"]))
Multi-line macro functions are also supported:
#mfunc view_get_yview(i) \
camera_get_view_x(view_camera[i])
show_debug_message(view_get_yview(0));
Macro functions support a number of magic variables in @@name
format:
-
@@__FILE__
: Name of the current file as a string -
@@__HERE__
: Name of the current resource, literal (can be used to self-reference) -
@@__DATE__
: Date of changing the file via GMEdit -
@@__TIME__
: Time of changing the file via GMEdit -
@@__LINE__
: Line that the macro-function was called from -
@@__LINE_STR__
: Same as above, but as a string -
@@argument
,@@argument_count
,@@argument#
: Lets you use script arguments inside your macros -
@@yourArgName
: Turns the argument "value" into a string
Examples:
#mfunc log(msg) show_debug_message("[" + @@__FILE__ + ":" + @@__LINE_STR__ + "] " + string(msg))
log("hi!"); // -> show_debug_message("[scr_test:2] hi!");
#mfunc dump(val) show_debug_message(@@val + " is " + string(val))
var hi = 1; dump(hi); // -> show_debug_message("hi is " + string(hi));
You can use pre##yourArgName
, yourArgName##post
, or pre##yourArgName##post
to form new identifiers based on arguments. This can be handy for forming temporary variables
#mfunc argument_pack(arr) for (var arr = array_create(@@argument_count), _i_##arr = 0;\
_i_##arr < @@argument_count; _i_##arr++) arr[_i_##arr] = @@argument[_i_##arr]
Which, when used like
argument_pack(args);
show_debug_message(args);
would make variables called "args" and "_i_args" accordingly.
- #mfunc does not work in GMS1 due to IDE denying to compile any "non-value" macros.
- You cannot stitch together GMEdit-specific syntax (e.g. lambdas) because #mfunc is disassembled into GML macros.
- Rearranging mfunc arguments does not currently auto-update other uses across the project (only in the active tab), so be careful with that.
- Smart auto-completion
- Types
- JSDoc tags (incl. additional ones)
- @hint tag (mostly 2.3)
- `vals: $v1 $v2` (template strings)
- #args (pre-2.3 named arguments)
- ??= (for pre-GM2022 optional arguments)
- ?? ?. ?[ (pre-GM2022 null-conditional operators)
- #lambda (pre-2.3 function literals)
- => (2.3+ function shorthands)
- #import (namespaces and aliases)
- v:Type (local variable types)
- #mfunc (macros with arguments)
- #gmcr (coroutines)