@@ -403,11 +403,330 @@ Running the app,
403
403
404
404
![ 5 squared] ( tutorial/squared.png )
405
405
406
- ## Strings and ByteString FFI
406
+ ## Passing Complex Data Types
407
407
408
- TODO
408
+ ### Bytes
409
409
410
- ## Function and Closure FFI
410
+ #### ` [UInt8] ` to ` ByteString `
411
411
412
- TODO
412
+ Call ` withUnsafeBufferPointer ` on a Swift ` Array ` to get
413
+ an ` UnsafeBufferPointer ` , and then read its ` .baseAddress `
414
+ property to get an ` UnsafePointer ` pass into the exported
415
+ Haskell function. The corresponding mutable variants are
416
+ ` withUnsafeMutableBufferPointer ` , ` UnsafeMutableBufferPointer ` ,
417
+ and ` UnsafeMutablePointer ` .
413
418
419
+ The generated Haskell headers use a single pointer type for all
420
+ pointers, ` HsPtr ` (` void * ` ), which is mutable (not ` const ` ). If
421
+ you know that a function does not mutate through a pointer, you
422
+ can use the ` HsPtr(mutating:) ` constructor to cast a non-mutable
423
+ pointer to a mutable pointer.
424
+
425
+ ``` swift
426
+ bytes.withUnsafeBufferPointer { bytesBufPtr in
427
+ someHaskellFunction (HsPtr (mutating : bytesBufPtr.baseAddress ), bytesBufPtr.count )
428
+ }
429
+ ```
430
+
431
+ If the function mutates the pointer's data, you must use
432
+ ` withUnsafeMutableBytes ` :
433
+
434
+ ``` swift
435
+ bytes.withUnsafeMutableBufferPointer { bytesBufPtr in
436
+ someHaskellFunction (bytesBufPtr.baseAddress , bytesBufPtr.count )
437
+ }
438
+ ```
439
+
440
+ To bring an array of bytes into a Haskell ` ByteString ` , use
441
+ ` Data.ByteString.packCStringLen ` :
442
+
443
+ ``` haskell
444
+ type CString = Ptr CChar
445
+ packCStringLen :: (CString , Int ) -> IO ByteString
446
+ ```
447
+
448
+ For example,
449
+
450
+ ```haskell
451
+ import Foreign.C
452
+ import Foreign.Ptr
453
+
454
+ import qualified Data.ByteString as B
455
+ import Data.Word
456
+
457
+ foreign export ccall countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize
458
+
459
+ countBytes :: Word8 -> Ptr CChar -> CSize -> IO CSize
460
+ countBytes needle haystack haystackLen = do
461
+ s <- B. packCStringLen (haystack, fromIntegral haystackLen)
462
+ pure (B. foldl (\ count b -> count + if b == needle then 1 else 0 ) 0 s)
463
+ ```
464
+
465
+ With a Swift wrapping function of
466
+
467
+ ``` swift
468
+ func count (byte : UInt8 , in bytes : [UInt8 ]) -> Int {
469
+ var r = 0
470
+ bytes.withUnsafeBytes { bytesPtr in
471
+ r = Int (SwiftHaskell.countBytes (byte, HsPtr (mutating : bytesPtr.baseAddress )))
472
+ }
473
+ return r
474
+ }
475
+ ```
476
+
477
+ #### ` ByteString ` to ` [UInt8] `
478
+
479
+ To pass a ` ByteString ` to an exported Swift function that
480
+ accepts a pointer and a length, use ` useAsCStringLen ` :
481
+
482
+ ``` haskell
483
+ import Data.ByteString (ByteString )
484
+ import qualified Data.ByteString as B
485
+
486
+ foreign import ccall " someSwiftFunction" someSwiftFunction :: Ptr CChar -> CSize -> IO ()
487
+
488
+ passByteString :: ByteString -> IO ()
489
+ passByteString s =
490
+ B. useAsCStringLen s $ \ (p, n) ->
491
+ someSwiftFunction p (fromIntegral n)
492
+ ```
493
+
494
+ To return the contents of a ` ByteString ` , call ` mallocArray ` to
495
+ allocate a new array with C's ` malloc ` allocator and copy the
496
+ ` ByteString ` data into it. The Swift caller is then responsible
497
+ for calling ` free ` on the pointer. Use ` Foreign.Storable.poke `
498
+ to also return the size by writing into a passed pointer.
499
+
500
+ ``` haskell
501
+ import Data.ByteString (ByteString )
502
+ import qualified Data.ByteString as B
503
+ import qualified Data.ByteString.Unsafe as BU
504
+ import Foreign.Storable (poke )
505
+
506
+ mallocCopyByteString :: ByteString -> IO (Ptr CChar , Int )
507
+ mallocCopyByteString s =
508
+ BU. unsafeUseAsCStringLen s $ \ (p, n) -> do
509
+ a <- mallocArray n
510
+ copyArray a p n
511
+ pure (a, n)
512
+
513
+ foreign export ccall getSequence :: Ptr CSize -> IO (Ptr CChar )
514
+
515
+ getSequence :: Ptr CSize -> IO (Ptr CChar )
516
+ getSequence sizePtr = do
517
+ (p, n) <- mallocCopyByteString (B. pack [1 .. 10 ])
518
+ poke sizePtr (fromIntegral n)
519
+ pure p
520
+ ```
521
+
522
+ The imported ` getSequence ` function returns a
523
+ ` UnsafeMutableRawPointer ` in Swift. To copy the elements into
524
+ a Swift array, first assign a type to the memory using the
525
+ ` .assumingMemoryBound(to:) ` method. Then wrap the pointer
526
+ and length in an ` UnsafeBufferPointer ` and pass it to the
527
+ array constructor, which copies the elements into a new array
528
+ using the ` Collection ` protocol that ` UnsafeBufferPointer `
529
+ implements.
530
+
531
+ ``` swift
532
+ func getSequence () -> [UInt8 ] {
533
+ var n = 0
534
+ let p = SwiftHaskell.getSequence (& n).assumingMemoryBound (to : UInt8 .self )
535
+ let a = [UInt8 ](UnsafeBufferPointer (start : p, count : n))
536
+ free (p)
537
+ return a
538
+ }
539
+ ```
540
+
541
+ ### Functions and Closures
542
+
543
+ #### Passing Swift Functions to Haskell
544
+
545
+ C function pointers have a type constructor of ` FunPtr ` in
546
+ Haskell. For example, ` FunPtr (CInt -> CSize -> IO ()) `
547
+ corresponds to ` void (*)(int, size_t) ` .
548
+
549
+ To convert ` FunPtr ` s into callable Haskell functions, use a
550
+ ` foreign import ccall "dynamic" ` declaration to ask the compiler
551
+ to generate a conversion function for that function type:
552
+
553
+ ``` haskell
554
+ foreign export ccall callbackExample :: FunPtr (CInt -> IO () ) -> IO ()
555
+ foreign import ccall " dynamic" unwrapCallback :: FunPtr (CInt -> IO () ) -> (CInt -> IO () )
556
+
557
+ callbackExample :: FunPtr (CInt -> IO () ) -> IO ()
558
+ callbackExample f = (unwrapCallback f) 3
559
+ ```
560
+
561
+ If there is no context that needs to be captured, Swift
562
+ functions can be passed in almost directly. However, like
563
+ ` HsPtr ` , the generated headers only use a single function
564
+ pointer type, ` HsFunPtr ` (` void (*)(void) ` ), so a little casting
565
+ is usually necessary:
566
+
567
+ ``` swift
568
+ func callbackExample (f : (@convention (c) (CInt ) -> Void )) {
569
+ let hsf: HsFunPtr = unsafeBitCast (f, to : HsFunPtr.self )
570
+ SwiftHaskell.callbackExample (hsf)
571
+ }
572
+ ```
573
+
574
+ To pass Swift closures with context, we can use the traditional
575
+ ` void * ` context pointer solution. Passing context however
576
+ means that we need to keep it alive while the callback is held,
577
+ and release it when we're done with it. For that, we can use
578
+ ` Foreign.ForeignPtr ` .
579
+
580
+ We'll wrap the context with
581
+
582
+ ``` haskell
583
+ type FinalizerPtr a = FunPtr (Ptr a -> IO () )
584
+ newForeignPtr :: FinalizerPtr a -> Ptr a -> IO (ForeignPtr a )
585
+ ```
586
+
587
+ and then apply it to the function with
588
+
589
+ ```haskell
590
+ withForeignPtr :: ForeignPtr a -> (Ptr a -> IO b ) -> IO b
591
+ ```
592
+
593
+ Together we have:
594
+
595
+ ```haskell
596
+ import Control.Concurrent
597
+ import Foreign.C
598
+ import Foreign.ForeignPtr
599
+ import Foreign.Ptr
600
+
601
+ foreign export ccall contextCallbackExample
602
+ :: Ptr ()
603
+ -> FunPtr (Ptr () -> IO () )
604
+ -> FunPtr (Ptr () -> CInt -> IO () )
605
+ -> IO ()
606
+ foreign import ccall " dynamic" unwrapContextCallback
607
+ :: FunPtr (Ptr () -> CInt -> IO () )
608
+ -> (Ptr () -> CInt -> IO () )
609
+
610
+ contextCallbackExample
611
+ :: Ptr () -- ^ Context pointer
612
+ -> FunPtr (Ptr () -> IO () ) -- ^ Context release function
613
+ -> FunPtr (Ptr () -> CInt -> IO () ) -- ^ Callback function
614
+ -> IO ()
615
+ contextCallbackExample ctxp releaseCtx callbackPtr = do
616
+ ctxfp <- newForeignPtr releaseCtx ctxp
617
+ let callback :: CInt -> IO ()
618
+ callback result = withForeignPtr ctxfp $ \ ctxp' ->
619
+ (unwrapContextCallback callbackPtr) ctxp' result
620
+ _ <- forkIO $ do
621
+ let result = 3 -- perform your complex computation here
622
+ callback result
623
+ pure ()
624
+ ```
625
+
626
+ The context pointer that we pass from the Swift side will be an
627
+ object containing the closure itself. The function passed as
628
+ the function pointer will merely cast the object to the known
629
+ closure type and call it.
630
+
631
+ To convert our Swift closure into a raw pointer, we'll use
632
+ Swift's ` Unmanaged ` wrapper type. These are the methods we'll
633
+ use from it:
634
+
635
+ ``` swift
636
+ public struct Unmanaged <Instance : AnyObject > {
637
+ public static func passRetained (_ value : Instance) -> Unmanaged <Instance>
638
+ public func toOpaque () -> UnsafeMutableRawPointer
639
+ public static func fromOpaque (_ value : UnsafeRawPointer ) -> Unmanaged <Instance>
640
+ public func takeUnretainedValue () -> Instance
641
+ public func takeRetainedValue () -> Instance
642
+ }
643
+ ```
644
+
645
+ Since Swift functions do not implement the ` AnyObject ` protocol
646
+ (they are not class types), we'll need to wrap them in a object
647
+ first.
648
+
649
+ Additionally, referring directly to a Swift function name will
650
+ give a Swift function type, which is not bit-compatible with a C
651
+ function type. Before casting to ` HsFunPtr ` , we'll need to use a
652
+ safe ` as ` cast to a ` @convention(c) ` type.
653
+
654
+ ``` swift
655
+ func contextCallbackExample (f : ((CInt ) -> Void )) {
656
+ class Wrap <T > {
657
+ var inner: T
658
+
659
+ init (_ inner : T) {
660
+ self .inner = inner
661
+ }
662
+ }
663
+ func release (context : HsPtr) {
664
+ let _: Wrap<(CInt ) -> Void > = Unmanaged .fromOpaque (context).takeRetainedValue ()
665
+ }
666
+ func call (context : HsPtr, value : CInt ) {
667
+ let wf: Wrap<(CInt ) -> Void > = Unmanaged .fromOpaque (context).takeUnretainedValue ()
668
+ let f = wf.inner
669
+ f (value)
670
+ }
671
+ let release_hs = unsafeBitCast (
672
+ release as @convention (c) (HsPtr) -> Void , to : HsFunPtr.self )
673
+ let call_hs = unsafeBitCast (
674
+ call as @convention (c) (HsPtr, CInt ) -> Void , to : HsFunPtr.self )
675
+ let ctx = Unmanaged .passRetained (Wrap (f)).toOpaque ()
676
+ SwiftHaskell.contextCallbackExample (ctx, release_hs, call_hs)
677
+ }
678
+ ```
679
+
680
+ #### Passing Haskell Functions to Swift
681
+
682
+ In addition to the static ` foreign export ` , we can export
683
+ dynamically created Haskell functions with `foreign export
684
+ "wrapper"`. Unlike when passing Swift closures, a separate
685
+ context pointer is not needed as the Haskell runtime supplies a
686
+ distinct function pointer address for each wrapped function.
687
+
688
+ ``` haskell
689
+ import Foreign.C
690
+ import Foreign.Ptr
691
+
692
+ foreign export ccall makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt ))
693
+ foreign import ccall " wrapper" wrapMultiplier
694
+ :: (CInt -> CInt )
695
+ -> IO (FunPtr (CInt -> CInt ))
696
+
697
+ makeMultiplier :: CInt -> IO (FunPtr (CInt -> CInt ))
698
+ makeMultiplier x = wrapMultiplier (x * )
699
+ ```
700
+
701
+ To free the ` FunPtr ` , export ` Foreign.Ptr.freeHaskellFunPtr ` and
702
+ call it from Swift when you're done with the function.
703
+
704
+ ``` haskell
705
+ foreign export ccall freeMultiplier :: FunPtr (CInt -> CInt ) -> IO ()
706
+
707
+ freeMultiplier :: FunPtr (CInt -> CInt ) -> IO ()
708
+ freeMultiplier = freeHaskellFunPtr
709
+ ```
710
+
711
+ Wrap the Haskell function in a Swift class to manage its
712
+ lifetime:
713
+
714
+ ``` swift
715
+ class Multiplier {
716
+ let funPtr: HsFunPtr
717
+
718
+ init (_ x : CInt ) {
719
+ self .funPtr = SwiftHaskell.makeMultiplier (x)
720
+ }
721
+
722
+ func multiply (_ y : CInt ) -> CInt {
723
+ typealias F = @convention (c) (CInt ) -> CInt
724
+ let f = unsafeBitCast (self .funPtr , to : F.self )
725
+ return f (y)
726
+ }
727
+
728
+ deinit {
729
+ SwiftHaskell.freeMultiplier (self .funPtr )
730
+ }
731
+ }
732
+ ```
0 commit comments