Macros¶
Macros provide compile-time code generation via #param# text substitution. They expand at compile time, replacing parameter names with the caller's literal arguments.
Syntax¶
macro name(#param1#, #param2#, ...)
// macro body with #param# substitution
end macroBasic Macros¶
macro util.swap(#a#, #b#)
util.swap := #a#
#a# := #b#
#b# := util.swap
end macroCalling util.swap(x, y) expands to:
util.swap := x
x := y
y := util.swapParameter Substitution¶
Parameters are purely textual -- #param# is replaced by whatever text was passed. Parameters concatenate with surrounding text to form identifiers:
macro type._Property(#type#, #field#)
property #type#.#field#
function get(pointer) -> result
result := #type#.pool[pointer * #type#.BLOCK + #type#.field.#field#]
end function
function set(pointer, value)
#type#.pool[pointer * #type#.BLOCK + #type#.field.#field#] := value
end function
end property
end macroCalling type._Property(Voice, note) generates a Voice.note property with getter and setter accessing Voice.field.note.
Parameters in strings are substituted too:
macro cluster.Validate(#cluster#, #member#)
if not (#cluster#.validate.#member#(#cluster#[self].#member#))
message('ArgFailure (#cluster#): #cluster#[self].#member# invalid: ' & #cluster#[self].#member#)
self_invalid := TRUE
end if
end macroNested Macro Calls¶
Macros can call other macros:
macro ivls.wait_ms(#ms#)
ivls.wait_us((#ms#) * 1000)
end macro
macro ivls.wait_us(#micros#)
ivls.wait_us.unsafe(#micros#)
if not check_ref(Voice, self)
NodeEnv[nenv].info.path_cancelled := TRUE
__RUN_CB__(PathCancellation)
end if
end macro
macro ivls.wait_us.unsafe(#micros#)
if #micros# > 0
tcm.wait(#micros#)
end if
end macroivls.wait_ms(100) -> ivls.wait_us(100000) -> ivls.wait_us.unsafe(100000) + safety checks.
Macros Generating Code Constructs¶
Generating Functions¶
macro type.Functions(#type#)
function #type#.new() -> result
call #type#.new.internal()
declare global #type#.new.result
result := #type#.new.result
end function
function #type#.delete(ref)
declare global #type#.delete.ref := ref
call #type#.delete.internal()
end function
function #type#.copy(src) -> result
declare global #type#.copy.src := src
call #type#.copy.internal()
declare global #type#.copy.result
result := #type#.copy.result
end function
end macrotype.Functions(Voice) generates Voice.new(), Voice.delete(), Voice.copy(), etc.
Generating Properties¶
macro kontakt._add_mod_par(#prop#, engine_par)
property group.mods.#prop#
function get(group, mods) -> result
result := get_engine_par(engine_par, group, get_mod_idx(group, mods), -1)
end function
function set(group, mods, value)
set_engine_par(engine_par, value, group, get_mod_idx(group, mods), -1)
end function
end property
end macroConstructor/Destructor Patterns¶
macro Voice.Constructor(#ref#)
declare ref := #ref#
__RUN_CB__(VoiceConstructor)
end macro
macro Voice.Destructor(#ref#)
declare ref := #ref#
__RUN_CB__(VoiceDestructor)
end macro
macro Voice.CopyConstructor(#ref#)
declare ref := #ref#
__RUN_CB__(VoiceCopyConstructor)
ivls._refurbish_voice(ref)
end macroType System Macros¶
macro type.Create(#type#)
type.CreateCustomPool(#type#, 524288)
end macro
macro type.CreateCustomPool(#type#, #pool#)
const #type#.field
literate_post_macro(#l#) on #type#.MEMBERS
end const
declare const #type#.BLOCK := #type#.field.SIZE
declare const #type#.POOL_SIZE := #pool# - (#pool# mod #type#.BLOCK)
declare #type#.pool[#type#.POOL_SIZE] := (-1)
declare #type#.is_alloc[#type#.POOL_SIZE / #type#.BLOCK]
declare #type#.count := 0
literate_post_macro(type._Property(#type#, #l#)) on #type#.MEMBERS
#type#.def()
end macroUtility Macros¶
macro util.array.fill(#arr#, #value#)
for util.fill_i := 0 to num_elements(#arr#) - 1
#arr#[util.fill_i] := #value#
end for
end macro
macro Voice.WriteInitList(#array#)
for Voice.i := 0 to num_elements(#array#) - 1
Voice.INIT[Voice.write_idx] := #array#[Voice.i]
inc(Voice.write_idx)
end for
end macro
macro util.log(#text#)
USE_CODE_IF(DEBUG)
message(#text#)
END_USE_CODE
end macroScope and Expansion¶
- Macros expand at compile time, not runtime
- Variables declared inside a macro follow normal scoping after expansion
- Use dotted names for macro namespacing:
util.array.fill,cluster.Validate,ivls.play - Use unique prefixed temp variables (
util.fill_i) to avoid conflicts across expansions - Parameters are evaluated at each occurrence in the expanded code
See Also¶
- Defines - Simple inline substitutions
- Functions - Runtime callable code (inlined)
- Types - Type system macros
- Code Generation - Literate macros and metaprogramming