#defined
When a symbol may or may not be defined due to different compilation flags,
you can use #defined
to test whether or not it is actually defined.
#defined
looks like a procedure with a single argument, which evaluates
at compile-time to a boolean expression.
use core {println}
main :: () {
main_is_defined := #defined(main); // true
foo_is_defined := #defined(foo); // false
}
One useful feature of #defined
is that you can use it to test if a
package is defined in the program. This way, you can test for optional
extensions in your program, without relying on using the correct flags.
#if #defined(package foo) {
// We know foo is defined, we can write a procedure that uses it
uses_foo :: () {
use foo;
foo.bar();
}
}
Using with #if
#defined
is generally used with #if
to conditionally include things
depending on if something else was or was not defined.
As an example, you could have a set of procedures that can be overridden by the end-user
of your library. But if they want to use the defaults, they can be still be defined
automatically. A combination of targeted bindings, #defined
, and #if
makes this works well.
In the library, you would use #if
and #defined
to test if a certain flag was defined.
package your_library
// Use predefined procedures if user did not override them.
#if !#defined(CUSTOM_PROCEDURES) {
do_thing_one :: () { println("Default thing 1!"); }
do_thing_two :: () { println("Default thing 2!"); }
}
Then the consumer of the library can use targeted bindings to define the flag and functions if necessary.
package main
use your_library
// Override procedures with targeted binding.
your_library.CUSTOM_PROCEDURES :: true
your_library.do_thing_one :: () { println("Overridden thing 1!"); }
your_library.do_thing_two :: () { println("Overridden thing 2!"); }
main :: () {
your_library.do_thing_one(); // Overridden thing 1!
your_library.do_thing_two(); // Overridden thing 2!
}