Metaprogramming
Banjo provides compile-time features through the meta
system. Meta expressions and statements are evaluated at
compile-time, generating runtime code. These constructs can only access compile-time values like const
values or type
information.
The size of a type can be accessed using meta(T).size
.
println(meta(i32).size); # 4
println(meta(f64).size); # 8
println(meta(Array[i8]).size); # 24 on 64-bit targets
Type Reflection
Type reflection is possible using type expressions.
struct Circle {
var x: f32;
var y: f32;
var radius: f32;
var color: u32;
}
func main() {
println(meta(Circle).name); # "Circle"
println(meta(Circle).fields); # ["x", "y", "radius", "color"]
var circle = Circle {
x: 10.0,
y: 5.0,
radius: 3.5,
color: 0xFF0000FF
};
println(meta(circle).field("radius")); # 3.5
meta(circle).field("y") = 25.0;
println(circle.y); # 25
}
meta if
Conditions can be evaluated at compile-time using meta if
statements.
use std.config;
meta if config.OS == config.ANDROID {
use android;
func load_asset(name: *u8) -> (*u8, usize) {
return android.asset_manager.read(name);
}
}
meta for
Repetitive code can be generated at compile-time using meta for
statements.
struct Point {
var x: i32;
var y: i32;
}
func main() {
var map = [
"x": 10,
"y": 20
];
var point: Point;
meta for field_name in meta(Point).fields {
meta(point).field(field_name) = map[field_name];
}
println(point.x); # 10
println(point.y); # 20
}
std.config
The standard library features a built-in module called std.module
that provides information about the current build.
These constants are currently available:
Name |
Description |
Values |
---|---|---|
BUILD_CONFIG |
Build configuration |
DEBUG, RELEASE |
ARCH |
Target architecture |
X86_64, AARCH64 |
OS |
Target operating system |
WINDOWS, LINUX, MACOS, ANDROID |