I recently heard about the Zig programming language and wanted to see if I could make Pd externals with it, so I put together a "hello world" external, which showcases things like printing to console, working with symbols, and sending floats through an outlet.

Getting it to work required using a local copy of m_pd.h and making a small change to the t_text struct, since Zig's C-translator apparently doesn't like bit fields.:

-    unsigned int te_type:2;     /* from defs below */
+    unsigned int te_type;       /* from defs below */

Currently, the build file is catered to a Linux installation, but probably all that would need to be changed for a different platform is the system include path and the library extension.

To do a build, just run:

zig build -Doptimize=ReleaseFast

EDIT: Changed the struct to an extern struct to guarantee the ordering of its fields.

EDIT2: Functions are now inside the struct. This seems to be more idiomatic to Zig, plus it looks nicer.

EDIT3: Link with libc during the build process.
This keeps the external's size small, even when adding math functions like log or exp to it. Otherwise, there's an increase of about 115KB because those functions get baked right into the external.

EDIT4: Replace m_pd.h with custom definitions that allow many functions to be called as methods. Some examples:

adding a method to a class:

pd.class_addbang(myclass, @ptrCast(&bang)); // old way
myclass.addBang(@ptrCast(&bang)); // new way

creating a new instance of an object

// old way
const self: *Self = @ptrCast(pd.pd_new(myclass));
_ = pd.outlet_new(&self.obj, &pd.s_float);
self.sym = pd.gensym("world");

// new way
const self: *Self = @ptrCast(myclass.new());
_ = self.obj.outlet(pd.s.float);
self.sym = pd.symbol("world");

sending a float through the main outlet:

pd.outlet_float(self.obj.te_outlet, f * 2); // old way
self.obj.out.float(f * 2); // new way


repo: https://github.com/myQwil/pd-zig-external

More examples can be found on my library's Zig rewrite branch: