AlexJReid/zigxll
Write Excel custom functions in zig
A Zig package for creating Excel custom functions.
There's a standalone repo here which can be used as a template.
This is all predicated on the possibility that someone would want to write functions for Excel in Zig. So that makes it very niche.
It exists because I wanted to see if it was possible to use Zig's C interop and comptime to make the Excel C SDK nicer to work with. I think it works quite nicely already, but I'd be glad of your feedback. I'm @alexjreid on X.
This came about as I'm working on xllify in C++ and Luau. I have no complaints, other than curiosity over how this would look in Zig. One day maybe xllify will be Zig - it's too soon to tell as I'm still learning the language (it's a moving target.)
Claude helped a lot with the comptime stuff and the demos. Thanks, Claude.
Anyway, we end up with:
xlAutoOpen, xlAutoClose, etc. - the framework handles it allExcelFunction() and reference your function[]u8 strings, framework handles UTF-16 conversion#VALUE! in ExcelSee how it works for more details.
See example for a simple working example.
Add ZigXLL as a dependency in your build.zig.zon:
.dependencies = .{
.xll = .{
.url = "https://github.com/alexjreid/zigxll/archive/refs/tags/v0.2.0.tar.gz",
.hash = "...",
},
},
Create your build.zig:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.resolveTargetQuery(.{
.cpu_arch = .x86_64,
.os_tag = .windows,
.abi = .msvc,
});
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSmall });
// Create a module for your functions
const user_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.strip = true,
});
// Build the XLL using the framework helper
const xll_build = @import("xll");
const xll = xll_build.buildXll(b, .{
.name = "my_functions",
.user_module = user_module,
.target = target,
.optimize = optimize,
});
const install_xll = b.addInstallFile(xll.getEmittedBin(), "lib/my_functions.xll");
b.getInstallStep().dependOn(&install_xll.step);
}
src/main.zig lists your function modules:
pub const function_modules = .{
@import("my_functions.zig"),
};
src/my_functions.zig defines your Excel functions:
const std = @import("std");
const xll = @import("xll");
const ExcelFunction = xll.ExcelFunction;
const ParamMeta = xll.ParamMeta;
pub const add = ExcelFunction(.{
.name = "add",
.description = "Add two numbers",
.category = "Zig Math",
.params = &[_]ParamMeta{
.{ .name = "a", .description = "First number" },
.{ .name = "b", .description = "Second number" },
},
.func = addImpl,
});
fn addImpl(a: f64, b: f64) !f64 {
return a + b;
}
// You can namespace functions using dots in the name
pub const bs_call = ExcelFunction(.{
.name = "MyFunctions.BSCall",
.description = "Black-Scholes European Call Option Price",
.category = "Finance",
.params = &[_]ParamMeta{
.{ .name = "S", .description = "Current stock price" },
.{ .name = "K", .description = "Strike price" },
.{ .name = "T", .description = "Time to maturity (years)" },
.{ .name = "r", .description = "Risk-free rate" },
.{ .name = "sigma", .description = "Volatility" },
},
.func = blackScholesCall,
});
fn blackScholesCall(S: f64, K: f64, T: f64, r: f64, sigma: f64) !f64 {
if (T <= 0) return error.InvalidMaturity;
// ... implementation
return call_price;
}
Build:
zig build
The XLL lands in zig-out/lib/my_functions.xll. Double click to load in Excel.
Parameters:
f64 - Numbers[]const u8 - Strings (UTF-8)*XLOPER12 - Raw Excel values (advanced)Return types:
f64 - Numbers[]const u8 / []u8 - Strings (automatically freed by Excel)*XLOPER12 - Raw Excel values (advanced)Functions return !T - errors become #VALUE! in Excel. Support for ranges is next.
ExcelFunctionpub const myFunc = ExcelFunction(.{
.name = "myFunc",
.description = "My function",
.category = "MyCategory",
.params = &[_]ParamMeta{
.{ .name = "x", .description = "Parameter help text" },
.{ .description = "Name is optional" },
},
.func = myFuncImpl,
.thread_safe = true, // Default is true
});
This library uses the Microsoft Excel 2013 XLL SDK headers and libraries. These are included in the excel/ directory and are required to build Excel add-ins.
xlcall.h, FRAMEWRK.H, xlcall32.lib, frmwrk32.libBy using this software you agree to the EULA specified by Microsoft in the above download.
You can also clone this repo to improve the framework directly:
src/user_functions.zig or create new moduleszig buildzig-out/lib/output.xll