I'm writing Nim bindings for the C SDK release by Panic for the Playdate portable console.
However, I stumbled upon an issue writing a more ergonomic API over the existing one. I'm testing this on the Playdate simulator.
Polymorphism doesn't work as expected, here's my code:
# --- Binding stuff
type PlaydateSys {.importc: "const struct playdate_sys", header: "pd_api.h".} = object
logToConsole {.importc.}: proc (fmt: cstring) {.cdecl, varargs, raises: [].}
type PlaydateAPI {.importc: "PlaydateAPI", header: "pd_api.h".} = object
system* {.importc.}: ptr PlaydateSys
# ---
type Parent = ref object of RootObj
type Child = ref object of Parent
# This is the app entry point on the Playdate device and simulator
proc eventHandler(api: ptr PlaydateAPI, event: cuint, arg: cuint): cint {.exportc.} =
let child: Parent = Child()
api.system.logToConsole("%d", (child of Child)) # false, but should be true!
child of Child is evaluated as false
This happens only when I set arc or orc. (arc is the mm I'd want to use)
But when mm is set to none, child of Child is true, as expected.
Note that memory management strategies other than arc, orc and none make the Playdate Simulator crash instantly.
To build the above Nim code I first call compileOnly to generate C files using a simple config.nims:
switch("mm", "arc")
switch("noMain", "on")
switch("compileOnly", "on")
switch("nimcache", ".nim")
switch("cpu", "arm64") # I'm on Apple Silicon
task compile, "compile project":
exec "nim c src/main.nim"
Then I use the Playdate SDK provided makefile (slightly tweaked) and the other resources to build all the relevant source code.
This is the tweaked makefile: https://pastebin.com/HckGFUQy
These are the commands generated by make pdc:
mkdir -p build
clang -g -g -dynamiclib -rdynamic -lm -DTARGET_SIMULATOR=1 -DTARGET_EXTENSION=1 -I . -I /Users/sam/Developer/PlaydateSDK/C_API -I src/playdate/include -o build/pdex.dylib .nim/@m..@s..@s..@s..@s.choosenim@stoolchains@snim-1.6.10@slib@sstd@sprivate@sdigitsutils.nim.c .nim/@m..@s..@s..@s..@s.choosenim@stoolchains@snim-1.6.10@slib@ssystem.nim.c .nim/@m..@s..@s..@s..@s.choosenim@stoolchains@snim-1.6.10@slib@ssystem@sdollars.nim.c .nim/@mmain.nim.c /Users/sam/Developer/PlaydateSDK/C_API/buildsupport/setup.c
touch Source/pdex.bin
cp build/pdex.dylib Source
/Users/sam/Developer/PlaydateSDK/bin/pdc Source HelloWorld.pdx
src/playdate/include contains a copy of nimbase.h. Nothing out of the ordinary here.
But why do runtime type checks fail?
Simply replacing switch("mm", "arc") to switch("mm", "none") solves the issue, but I don't want to lose automatic memory management.
Not calling NimMain also causes all sorts of other issues too like global variables not being initialized. I ran into that one before too. :)
You probably also want your cpu to match the target cpu not the hosts. So it'd be just arm.
PS feel free to ask about the cortex m7's if you run into issues.
Also looks like they're using FreeRTOS under the hood so you can also set --os:freertos. I found --define:nimAllocPagesViaMalloc to work well with FreeRTOS and embedded too.
Thanks! The configuration I posted here is minimal, just to reproduce the issue.
I do already use nimAllocPagesViaMalloc (and also build for arm when targeting the device) but didn't know the Playdate is using FreeRTOS! Nice!