Programming thread

  • 🐕 I am attempting to get the site runnning as fast as possible. If you are experiencing slow page load times, please report it.
Hell, I can do p = Path.home() / '.my_app.d', and I'll get a valid path on any machine.
this is evil you should be using XDG paths on linux, appdata on windows, and whatever the hell mac does on mac
and i have no idea what this would do on wasm and mobile platforms (particularly the former, where it doesn't even really have a file system)
 
this is evil you should be using XDG paths on linux, appdata on windows, and whatever the hell mac does on mac
and i have no idea what this would do on wasm and mobile platforms (particularly the former, where it doesn't even really have a file system)
This is one of those problems that tests wisdom more than intelligence.

On a side note, I was reading about sys-V calling conventions last night. In order for a complex data type to be returnable by value from a function, it must fit into two eightbytes, (so two slots of 64 bits each), and within each of the eightbyte slots, all data must fit into the same register class. This latter part basically means it must either be ints or floats as far as native registers go. If these checks fail, the compiler will implicitly refactor your function into a return by reference format.
What this means is that when returning a pointer from a function, you can actually do the following:
C:
typedef struct {
  uint64_t metadata;
  void* data;
} tagged_ptr;

tagged_ptr my_func(/* ... */);
So, you can safely annotate a pointer with 64 bits of metadata in C without compromising your calling convention structure.
 
this is evil you should be using XDG paths on linux, appdata on windows, and whatever the hell mac does on mac
and i have no idea what this would do on wasm and mobile platforms (particularly the former, where it doesn't even really have a file system)
For the record, mac's equivalent is typically ~/Library/Application Support. If not there, CLI apps will often just check ~/.config.

Go even has a special function for this
UserConfigDir returns the default root directory to use for user-specific configuration data. Users should create their own application-specific subdirectory within this one and use that.

On Unix systems, it returns $XDG_CONFIG_HOME as specified by https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if non-empty, else $HOME/.config. On Darwin, it returns $HOME/Library/Application Support.On Windows, it returns %AppData%. On Plan 9, it returns $home/lib.

If the location cannot be determined (for example, $HOME is not defined) or the path in $XDG_CONFIG_HOME is relative, then it will return an error.
As would most languages, I'm sure.

I'm heavily against the polluting of home folders with random bullshit.
 
Last edited:
I've been playing with different LISPs recently. And I've come to conclusion that I like chez the most.
The fact that I can just simply write C bindings using standard library won me over.
JavaScript:
(define libSDL3 (load-shared-object "libSDL3.so.0"))
(define init-sdl (foreign-procedure "SDL_Init" (int) boolean))
(define create-window (foreign-procedure "SDL_CreateWindow" (string int int int) uptr))
(define create-renderer (foreign-procedure "SDL_CreateRenderer" (uptr uptr) uptr))

(define SDL-INIT-VIDEO 32)

(init-sdl SDL-INIT-VIDEO)
(define *window* (create-window "Hello Chez" 640 400 0))
(define *renderer* (create-renderer *window* 0))

(define-ftype sdl-rect
  (struct
    [x float]
    [y float]
    [w float]
    [h float]))

(define (make-sdl-rect x y w h)
  (let
    ([ptr (make-ftype-pointer
            sdl-rect (foreign-alloc (ftype-sizeof sdl-rect)))])
    (ftype-set! sdl-rect (x) ptr x)
    (ftype-set! sdl-rect (y) ptr y)
    (ftype-set! sdl-rect (w) ptr w)
    (ftype-set! sdl-rect (h) ptr h)
    ptr))

(define (render-rect rect)
  ((foreign-procedure "SDL_SetRenderDrawColor"
                      (uptr unsigned-8 unsigned-8 unsigned-8 unsigned-8) boolean)
   *renderer* 255 255 255 255)
  ((foreign-procedure "SDL_RenderFillRect"
                      (uptr (* sdl-rect)) boolean)
   *renderer* rect))

(render-rect (make-sdl-rect 300.0 180.0 20.0 20.0))
((foreign-procedure "SDL_RenderPresent" (uptr) boolean) *renderer*)
 
Last edited:
I've been playing with different LISPs recently. And I've come to conclusion that I like chez the most.
The fact that I can just simply write C bindings using standard library won me over.
JavaScript:
(define libSDL3 (load-shared-object "libSDL3.so.0"))
(define init-sdl (foreign-procedure "SDL_Init" (int) boolean))
(define create-window (foreign-procedure "SDL_CreateWindow" (string int int int) uptr))
(define create-renderer (foreign-procedure "SDL_CreateRenderer" (uptr uptr) uptr))

(define SDL-INIT-VIDEO 32)

(init-sdl SDL-INIT-VIDEO)
(define *window* (create-window "Hello Chez" 640 400 0))
(define *renderer* (create-renderer *window* 0))

(define-ftype sdl-rect
  (struct
    [x float]
    [y float]
    [w float]
    [h float]))

(define (make-sdl-rect x y w h)
  (let
    ([ptr (make-ftype-pointer
            sdl-rect (foreign-alloc (ftype-sizeof sdl-rect)))])
    (ftype-set! sdl-rect (x) ptr x)
    (ftype-set! sdl-rect (y) ptr y)
    (ftype-set! sdl-rect (w) ptr w)
    (ftype-set! sdl-rect (h) ptr h)
    ptr))

(define (render-rect rect)
  ((foreign-procedure "SDL_SetRenderDrawColor"
                      (uptr unsigned-8 unsigned-8 unsigned-8 unsigned-8) boolean)
   *renderer* 255 255 255 255)
  ((foreign-procedure "SDL_RenderFillRect"
                      (uptr (* sdl-rect)) boolean)
   *renderer* rect))

(render-rect (make-sdl-rect 300.0 180.0 20.0 20.0))
((foreign-procedure "SDL_RenderPresent" (uptr) boolean) *renderer*)
I think Guile also has the same sort of dlopen, live ffi. Pretty neat to be able to mess with this stuff live in the repl without a compilation step. Although one downside is, as you see, you don't have the easiest access to #define'd constants.
 
I think Guile also has the same sort of dlopen, live ffi. Pretty neat to be able to mess with this stuff live in the repl without a compilation step. Although one downside is, as you see, you don't have the easiest access to #define'd constants.
iiuc gambit just lets you put c snippets in your code that can #include but the catch is that they need to be compiled before you can use them in the repl
 
iiuc gambit just lets you put c snippets in your code that can #include but the catch is that they need to be compiled before you can use them in the repl
Yeah, I'm doing something like that with my current Chicken project.

My SDL init stuff looks like this:
Code:
(foreign-declare "#include <SDL3/SDL.h>")

(define SDL_INIT_AUDIO (foreign-value "SDL_INIT_AUDIO" unsigned-integer32))
(define SDL_INIT_VIDEO (foreign-value "SDL_INIT_VIDEO" unsigned-integer32))
(define SDL_INIT_JOYSTICK (foreign-value "SDL_INIT_JOYSTICK" unsigned-integer32))
(define SDL_INIT_HAPTIC (foreign-value "SDL_INIT_HAPTIC" unsigned-integer32))
(define SDL_INIT_GAMEPAD (foreign-value "SDL_INIT_GAMEPAD" unsigned-integer32))
(define SDL_INIT_EVENTS (foreign-value "SDL_INIT_EVENTS" unsigned-integer32))
(define SDL_INIT_SENSOR (foreign-value "SDL_INIT_SENSOR" unsigned-integer32))
(define SDL_INIT_CAMERA (foreign-value "SDL_INIT_CAMERA" unsigned-integer32))

(define SDL_Init (foreign-lambda bool SDL_Init unsigned-integer32))
(define SDL_InitSubSystem (foreign-lambda bool SDL_InitSubSystem unsigned-integer32))
(define SDL_Quit (foreign-lambda void SDL_Quit))
(define SDL_QuitSubSystem (foreign-lambda void SDL_QuitSubSystem unsigned-integer32))

(define SDL_GetError (foreign-lambda c-string SDL_GetError))
This file needs to be compiled.

Also very conveniently, Chicken offers syntax called foreign-lambda*, which you can write a C function and supply the body as a string literal.

And actually, Chicken doesn't supply a syntax for defining accessors for C structures, so my SDL_Event accessors look like this:
Code:
(define SDL_Event-type
  (foreign-lambda* unsigned-integer32 ((u8vector ev))
    "C_return(((SDL_Event*)ev)->type);"))

(define SDL_Event-motion-xrel
  (foreign-lambda* float ((u8vector ev))
    "C_return(((SDL_Event*)ev)->motion.xrel);"))
(define SDL_Event-motion-yrel
  (foreign-lambda* float ((u8vector ev))
    "C_return(((SDL_Event*)ev)->motion.yrel);"))
In this case, I'm storing SDL_Events as byte buffers (u8vectors) on the Chicken side. Other SDL data types (like if I was using SDL_Surfaces), I'd let SDL itself allocate them and just handle them as pointers, but SDL_Events are entirely self contained so they're a good candidate for garbage collection.

Any time a structure is simple enough that it can be allocated on the Scheme side, often that's preferable, less bookkeeping to fuck up. (Although maybe Chicken has some kind of finalizer API somewhere? idk)

Also the C_return is just a C macro for return, but Chicken's compiler implementation does some clever/funky stuff, so you need to use the special macro for stack cleanup before turning to Scheme code. But as far as I need to be concerned, it's just return.

The SDL_Event struct is a huge union with a bajillion members for all the different event types. I only have those three accessors defined right now. At some point I might throw together some shell scripts and see if I can generate all the accessors. But for now, I'm just defining the ones I need by hand as I find a need for them.

Right now I'm just testing click and drag of the tiles.
 
  • Informative
Reactions: ADHD Mate
- explanation of chicken ffi -
yeah this is almost exactly like the other scheme->c compilers down to needing a weird macro to return from the function
And actually, Chicken doesn't supply a syntax for defining accessors for C structures, so my SDL_Event accessors look like this:
you might just need to make the syntax yourself with whatever chicken's local syntax-case substitute is
 
  • Agree
Reactions: Marvin
you might just need to make the syntax yourself with whatever chicken's local syntax-case substitute is
I was going to just grep the headers and fuck around with sed until I got something workable and then edit it by hand but this is a far better approach.

Here's what I ended up doing:
Code:
;; ok syntax
;; (define-SDL_Event-getter return-type (foo bar))
;; =>
;; (define SDL_Event-foo-bar
;;   (foreign-lambda* return-type ((u8vector ev))
;;                    "C_return(((SDL_Event*)ev)->foo.bar);"))


(define-syntax define-SDL_Event-getter
  (er-macro-transformer
   (lambda (exp rename compare)
     (import (srfi-1)
             (srfi-2)
             (srfi-13)
             (chicken string)
             (chicken foreign)
             (chicken format))
     (or (and-let* ((_ (and (list? exp)
                            (= (length exp) 3)))

                    (_ (car exp))

                    (return-type (and (symbol? (cadr exp))
                                      (cadr exp)))

                    (member-path     (and (list? (caddr exp))
                                          (every symbol? (caddr exp))
                                          (caddr exp)))
                    (member-path-c   (string-join (map ->string member-path) "."))
                    (member-path-scm (string->symbol
                                      (string-join
                                       (map ->string member-path) "-")))

                    ;; renames
                    (&define          (rename 'define))
                    (&foreign-lambda* (rename 'foreign-lambda*))

                    (scheme-identifier
                     (string->symbol (format "SDL_Event-~a" member-path-scm)))
                    (c-body-string
                     (format "C_return(((SDL_Event*)ev)->~a);" member-path-c)))
           `(,&define ,scheme-identifier
                      (,&foreign-lambda* ,return-type ((u8vector ev))
                                         ,c-body-string)))
         (error "bad define-SDL_Event-getter syntax" exp)))))
Looks good in the repl:
Code:
#;2> (expand '(define-SDL_Event-getter return-type (foo bar)))
; loading /usr/lib/chicken/11/srfi-13.so ...
; loading /usr/lib/chicken/11/srfi-14.so ...
(##core#begin (##core#ensure-toplevel-definition SDL_Event-foo-bar) (##core#set! SDL_Event-foo-bar (foreign-lambda*62 return-type ((u8vector ev)) "C_return(((SDL_Event*)ev)->foo.bar);")))

And usage:
Code:
(define-foreign-type SDL_MouseID unsigned-int32)
(define-foreign-type SDL_MouseButtonFlags unsigned-int32)
(define-foreign-type Uint8 unsigned-byte)

(define-SDL_Event-getter SDL_WindowID (motion windowID))
(define-SDL_Event-getter SDL_MouseID (motion which))
(define-SDL_Event-getter SDL_MouseButtonFlags (motion state))
(define-SDL_Event-getter float (motion x))
(define-SDL_Event-getter float (motion y))
(define-SDL_Event-getter float (motion xrel))
(define-SDL_Event-getter float (motion yrel))

(define-SDL_Event-getter SDL_WindowID (button windowID))
(define-SDL_Event-getter SDL_MouseID (button which))
(define-SDL_Event-getter Uint8 (button button))
(define-SDL_Event-getter bool (button down))
(define-SDL_Event-getter Uint8 (button clicks))
(define-SDL_Event-getter float (button x))
(define-SDL_Event-getter float (button y))
(define-foreign-type is a Chicken macro to add type aliases for its FFI)
 
  • Informative
Reactions: ADHD Mate
Back