A macro for making C structs in common lisp a little less painful

I’m still learning common lisp, but I made a macro that I think is pretty useful.

(defmacro build-struct (struct-name struct &rest rest)
  (unless (find-symbol (string struct-name))
    (error (format nil "No struct ~a found" struct-name)))
  (labels ((make-struct-symbol (l)
             (let* ((field (car l))
                    (value (cadr l))
                    (symbol (intern (format nil "~a.~a" (string struct-name) (string field)))
))
               `(setf (,symbol ,struct) ,value))))
    (let ((forms (mapcar #'make-struct-symbol rest)))
      `(progn ,@forms))))

It’s very small and can turn this

(setf (wgpu-device-descriptor.next-in-chain desc) (cffi:null-pointer))
(setf (wgpu-device-descriptor.label desc) (cffi:lisp-string-to-foreign "device" label 6))
(setf (wgpu-device-descriptor.required-feature-count desc) 0)
(setf (wgpu-device-descriptor.required-limits desc) (cffi:null-pointer))

into this

(build-struct wgpu-device-descriptor desc
    (next-in-chain (cffi:null-pointer))
    (label (cffi:lisp-string-to-foreign "desc"))
    (required-feature-count 0)
    (required-limits (cffi:null-pointer)))

which is much more ergonomic imo

It was pointed out to me, by Vendethiel on the common lisp dicord, that I can actually write a kind of `with-c-struct` macro that would change the behavior of `setf` within its block to allow a syntax that’d more like

(with-c-struct structname
  (setf field1 'foo
        field2 'bar))

which would be even better!

So I guess that’s where I’m going next.

You’ve landed on my blog!

Welcome!

I’ve been a programmer for over 19 years. In that time I’ve worked on games, large frontend projects, enterprise backend products, fun little tools, and generative artwork.

I use this space to talk about projects I’m currently working, show off workshops I intend to give, and rant about life and technology.


By [benbot], 2024-09-22