finnvoor / PlaydateKit

Create games for Playdate using Swift.
https://finnvoor.github.io/PlaydateKit/documentation/playdatekit
Creative Commons Zero v1.0 Universal
198 stars 21 forks source link

Discussion: Replacing or complementing `StaticString` with `String` in APIs #59

Closed tyetrask closed 4 months ago

tyetrask commented 5 months ago

(Hello and thank you for all of your work on PlaydateKit, it's really great!)

I wanted to start a discussion around either 1) moving from StaticString to String now that some basic support has landed in Swift Embedded or if StaticString is still desirable (for performance/simplicity/memory/? reasons), 2) complementing any initializers/functions that accept StaticString with an overload for String.

I've been experimenting with this in my working fork of PlaydateKit and so far things seem to be working out pretty well. I'm curious to hear if there's interest in this, and if so, which direction would be preferred. If there's interest and a direction is determined, I would be happy to open pull requests and help move things along! Thanks for reading 🙏!

As just one example, for System.log():

Current

public static func log(_ log: StaticString) {
    let logToConsole = unsafeBitCast(
        system.logToConsole.unsafelyUnwrapped,
        to: (@convention(c) (UnsafePointer<CChar>?) -> Void).self
    )
    log.utf8Start.withMemoryRebound(
        to: CChar.self,
        capacity: log.utf8CodeUnitCount + 1
    ) { pointer in
        logToConsole(pointer)
    }
}

With String instead:

public static func log(_ log: String) {
    let logToConsole = unsafeBitCast(
        system.logToConsole.unsafelyUnwrapped,
        to: (@convention(c) (UnsafePointer<CChar>?) -> Void).self
    )

    // No idea if this is the best way to go about this, by the way!
    var utf8Log = log.utf8CString
    withUnsafeMutablePointer(to: &utf8Log[0]) { pointer in
        logToConsole(pointer)
    }
}

Which, of course, allows for potentially useful things like:

var message = ""
if Bool.random() {
  message = "Yay!"
} else {
  message = "Oh no!"
}

System.log("A logged message: \(message)")

(This is a silly example but a more concrete one is e.g. using a dynamically generated String as an argument to Graphics.Bitmap(path: ...) and I'm sure there are potentially many more!)

finnvoor commented 5 months ago

Hi! That's definitely next on the list of things to add, I'm just waiting for the fix for https://github.com/apple/swift/issues/74386 to land in a nightly build as it was causing issues (should be in the next few days)

tyetrask commented 5 months ago

Ah, wonderful! Thank you so much 🙌 ! I hadn't noticed the crash because in my working fork I had just been replacing StaticString in the signatures rather than overloading them 😅.

(If it makes sense then to close this issue, please do!)