const std = @import("std"); const builtin = @import("builtin"); const native_arch = builtin.cpu.arch; const expect = std.testing.expect; // Functions are declared like this fn add(a: i8, b: i8) i8 { if (a == 0) { return b; } return a + b; } // The export specifier makes a function externally visible in the generated // object file, and makes it use the C ABI. export fn sub(a: i8, b: i8) i8 { return a - b; } // The extern specifier is used to declare a function that will be resolved // at link time, when linking statically, or at runtime, when linking // dynamically. The quoted identifier after the extern keyword specifies // the library that has the function. (e.g. "c" -> libc.so) // The callconv specifier changes the calling convention of the function. const WINAPI: std.builtin.CallingConvention = if (native_arch == .x86) .Stdcall else .C; extern "kernel32" fn ExitProcess(exit_code: u32) callconv(WINAPI) noreturn; extern "c" fn atan2(a: f64, b: f64) f64; // The @setCold builtin tells the optimizer that a function is rarely called. fn abort() noreturn { @setCold(true); while (true) {} } // The naked calling convention makes a function not have any function prologue or epilogue. // This can be useful when integrating with assembly. fn _start() callconv(.Naked) noreturn { abort(); } // The inline calling convention forces a function to be inlined at all call sites. // If the function cannot be inlined, it is a compile-time error. inline fn shiftLeftOne(a: u32) u32 { return a << 1; } // The pub specifier allows the function to be visible when importing. // Another file can use @import and call sub2 pub fn sub2(a: i8, b: i8) i8 { return a - b; } // Function pointers are prefixed with `*const `. const Call2Op = *const fn (a: i8, b: i8) i8; fn doOp(fnCall: Call2Op, op1: i8, op2: i8) i8 { return fnCall(op1, op2); } test "function" { try expect(doOp(add, 5, 6) == 11); try expect(doOp(sub2, 5, 6) == -1); } // test