packages feed

language-c-inline 0.3.0.1 → 0.5.0.0

raw patch · 9 files changed

+356/−52 lines, 9 filesdep +containers

Dependencies added: containers

Files

Language/C/Inline/ObjC.hs view
@@ -12,6 +12,11 @@ -- This module exports the principal API for inline Objective-C.  module Language.C.Inline.ObjC (++  -- * Re-export types from 'Foreign.C'+  module Foreign.C.Types, CString, CStringLen, CWString, CWStringLen, Errno,++  -- * Combinators for inline Objective-C    objc_import, objc_interface, objc_implementation, objc, objc_emit ) where @@ -24,6 +29,7 @@ import Data.List import Foreign.C                  as C import Foreign.C.String           as C+import Foreign.C.Types import Foreign.Marshal            as C import Language.Haskell.TH        as TH import Language.Haskell.TH.Syntax as TH@@ -91,7 +97,7 @@     exportVar var       = do         {   -- Determine the argument and result types of the exported Haskell function-        ; (argTys, inIO, resTy) <- splitHaskellType <$> determineVarType var+        ; (tvs, argTys, inIO, resTy) <- splitHaskellType <$> determineVarType var              -- Determine C types         ; cArgTys <- mapM (haskellTypeToCType ObjC) argTys@@ -103,7 +109,7 @@         ; (bridgeResTy,  cBridgeResTy,  hsResMarshaller,  cResMarshaller)  <- generateHaskellToCMarshaller resTy cResTy              -- Haskell type of the foreign wrapper function-        ; let hsWrapperTy = haskellWrapperType bridgeArgTys bridgeResTy+        ; let hsWrapperTy = haskellWrapperType tvs bridgeArgTys bridgeResTy              -- Generate the Haskell wrapper         ; let cwrapperName = mkName . nameBase $ var@@ -133,17 +139,31 @@               $ty:cBridgeResTy $id:(show hswrapperName) ($params:(cParams cBridgeArgTys cArgVars));             |]             ++-            wrapperDef+            map makeStaticFunc wrapperDef         } -    splitHaskellType (ArrowT `AppT` arg `AppT` res)-      = let (args, inIO, res') = splitHaskellType res+    splitHaskellType (ForallT tvs _ctxt ty)                   -- collect quantified variables (drop the context)+      = let (tvs', args, inIO, res) = splitHaskellType ty         in-        (arg:args, inIO, res')-    splitHaskellType (ConT io `AppT` res) | io == ''IO-      = ([], True, res)+        (tvs ++ tvs', args, inIO, res)+    splitHaskellType (ArrowT `AppT` arg `AppT` res)           -- collect argument types+      = let (tvs, args, inIO, res') = splitHaskellType res+        in+        (tvs, arg:args, inIO, res')+    splitHaskellType (ConT io `AppT` res) | io == ''IO        -- is it an 'IO' function?+      = ([], [], True, res)     splitHaskellType res-      = ([], False, res)+      = ([], [], False, res)+    +    makeStaticFunc (FuncDef (Func    dspec f decl ps    body loc1) loc2)+      = FuncDef (Func    (addStatic dspec) f decl ps    body loc1) loc2+    makeStaticFunc (FuncDef (OldFunc dspec f decl ps ig body loc1) loc2)+      = FuncDef (OldFunc (addStatic dspec) f decl ps ig body loc1) loc2+    makeStaticFunc def = def+    +    addStatic (DeclSpec         st tqs ts loc) = DeclSpec         (Tstatic loc:st) tqs ts loc+    addStatic (AntiTypeDeclSpec st tqs ts loc) = AntiTypeDeclSpec (Tstatic loc:st) tqs ts loc+    addStatic declSpec                         = declSpec  forExpD :: Callconv -> String -> Name -> TypeQ -> DecQ forExpD cc str n ty@@ -176,7 +196,7 @@         generateCToHaskellMarshaller (ConT resTy) cResTy          -- Haskell type of the foreign wrapper function-    ; let hsWrapperTy = haskellWrapperType bridgeArgTys bridgeResTy+    ; let hsWrapperTy = haskellWrapperType [] bridgeArgTys bridgeResTy          -- FFI setup for the C wrapper     ; cwrapperName <- newName "cwrapper"@@ -208,12 +228,16 @@  -- Turn a list of argument types and a result type into a Haskell wrapper signature. ----- > haskellWrapperType [a1, .., an] r = [| a1 -> .. -> an -> IO r |]+-- > haskellWrapperType [tv1, .., tvm] [a1, .., an] r = [| forall tv1 .. tvm. a1 -> .. -> an -> IO r |] ---haskellWrapperType :: [TH.TypeQ] -> TH.TypeQ -> TH.TypeQ-haskellWrapperType []             resTy = [t| IO $resTy |]-haskellWrapperType (argTy:argTys) resTy = [t| $argTy -> $(haskellWrapperType argTys resTy) |]+haskellWrapperType :: [TH.TyVarBndr] -> [TH.TypeQ] -> TH.TypeQ -> TH.TypeQ+haskellWrapperType []  argTys resTy = wrapperBodyType argTys resTy                          -- monotype+haskellWrapperType tvs argTys resTy = forallT tvs (cxt []) (wrapperBodyType argTys resTy)   -- polytype +wrapperBodyType :: [TH.TypeQ] -> TH.TypeQ -> TH.TypeQ+wrapperBodyType []             resTy = [t| IO $resTy |]+wrapperBodyType (argTy:argTys) resTy = [t| $argTy -> $(wrapperBodyType argTys resTy) |]+ -- Generate the prototype of and function definition of a C marshalling wrapper. -- -- Given a C expression to be executed, this generator produces a C function that executes the expression with all@@ -297,7 +321,7 @@         ; appendFile objcFname_h (unlines (map mkImport headers) ++ "\n")         ; appendFile objcFname_h (show $ QC.ppr objc_h)         ; writeFile  objcFname_m (info origFname)-        ; appendFile objcFname_m ("#import \"" ++ objcFname_h ++ "\"\n\n")+        ; appendFile objcFname_m ("#import \"" ++ takeFileName objcFname_h ++ "\"\n\n")         ; appendFile objcFname_m (show $ QC.ppr objc_m)         }     ; objc_jumptable <- getForeignTable
Language/C/Inline/ObjC/Marshal.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}+{-# LANGUAGE PatternGuards, TemplateHaskell, QuasiQuotes #-}  -- | -- Module      : Language.C.Inline.ObjC.Marshal@@ -28,9 +28,12 @@ ) where    -- common libraries+import Data.Map                   as Map+import Data.Word import Foreign.C                  as C import Foreign.C.String           as C import Foreign.Marshal            as C+import Foreign.Ptr                as C import Foreign.StablePtr          as C import Language.Haskell.TH        as TH import Language.Haskell.TH.Syntax as TH@@ -47,7 +50,7 @@ -- Auxilliary functions -- -------------------- --- Check that the given TH name is that of a Haskell variable and determine its type.+-- |Check that the given TH name is that of a Haskell variable and determine its type. -- determineVarType :: TH.Name -> Q TH.Type determineVarType vname@@ -63,7 +66,7 @@           }     } --- Check that the given TH name is that of a Haskell type constructor.+-- |Check that the given TH name is that of a Haskell type constructor. -- checkTypeName :: TH.Name -> Q () checkTypeName tyname@@ -85,46 +88,112 @@ -- Determine foreign types -- ----------------------- --- Determine the C type that we map a given Haskell type to.+-- |Determine the C type that we map a given Haskell type to. -- haskellTypeToCType :: QC.Extensions -> TH.Type -> Q QC.Type-haskellTypeToCType lang (ListT `AppT` (ConT char))+haskellTypeToCType lang (ForallT _tvs _ctxt ty)           -- ignore quantifiers and contexts+  = haskellTypeToCType lang ty+haskellTypeToCType lang (ListT `AppT` (ConT char))        -- marshal '[Char]' as 'String'   | char == ''Char    = haskellTypeNameToCType lang ''String-haskellTypeToCType lang (ConT tc)+haskellTypeToCType lang ty@(ConT maybeC `AppT` argTy)     -- encode a 'Maybe' around a pointer type in the pointer+  | maybeC == ''Maybe+  = do+    { cargTy <- haskellTypeToCType lang argTy+    ; if isCPtrType cargTy+      then+        return cargTy+      else+        unknownType lang ty+    }+haskellTypeToCType lang (ConT tc)                         -- nullary type constructors are delegated   = haskellTypeNameToCType lang tc-haskellTypeToCType _lang ty+haskellTypeToCType lang ty@(VarT tv)                      -- can't marshal an unknown type+  = unknownType lang ty+haskellTypeToCType lang ty@(UnboxedTupleT _)              -- there is nothing like unboxed tuples in C+  = unknownType lang ty+haskellTypeToCType _lang ty                               -- everything else is marshalled as a stable pointer   = return [cty| typename HsStablePtr |] --- Determine the C type that we map a given Haskell type constructor to — i.e., we map all Haskell--- whose outermost constructor is the given type constructor to the returned C type..+unknownType lang ty = reportErrorAndFail lang $ "don't know a foreign type suitable for Haskell type '" ++ TH.pprint ty ++ "'"++-- |Determine the C type that we map a given Haskell type constructor to — i.e., we map all Haskell types+-- whose outermost constructor is the given type constructor to the returned C type. --+-- All types representing boxed values that are not explicitly mapped to a specific C type, are mapped to+-- stable pointers.+-- haskellTypeNameToCType :: QC.Extensions -> TH.Name -> Q QC.Type-haskellTypeNameToCType ObjC tyname-  | tyname == ''String = return [cty| typename NSString * |]-  | tyname == ''()     = return [cty| void |]-haskellTypeNameToCType _lang tyname-  = return [cty| typename HsStablePtr |]+haskellTypeNameToCType ext tyname+  = case Map.lookup tyname (haskellToCTypeMap ext) of+      Just cty -> return cty+      Nothing  -> do+        { info <- reify tyname+        ; case info of+            PrimTyConI _ _ True -> unknownUnboxedType+            _                   -> return [cty| typename HsStablePtr |]+        }+  where+    unknownUnboxedType = reportErrorAndFail ext $ +                           "don't know a foreign type suitable for the unboxed Haskell type '" ++ show tyname ++ "'"   +haskellToCTypeMap :: QC.Extensions -> Map TH.Name QC.Type+haskellToCTypeMap ObjC+  = Map.fromList+    [ (''CChar,   [cty| char |])+    , (''CSChar,  [cty| signed char |])+    , (''CUChar,  [cty| unsigned char |])+    , (''CShort,  [cty| short |])+    , (''CUShort, [cty| unsigned short |])+    , (''Int,     [cty| int |])+    , (''CInt,    [cty| int |])+    , (''Word,    [cty| unsigned int |])+    , (''CUInt,   [cty| unsigned int |])+    , (''CLong,   [cty| long |])+    , (''CULong,  [cty| unsigned long |])+    , (''CLLong,  [cty| long long |])+    , (''CULLong, [cty| unsigned long long |])+    --+    , (''Float,   [cty| float |])+    , (''CFloat,  [cty| float |])+    , (''Double,  [cty| double |])+    , (''CDouble, [cty| double |])+    --+    , (''String,  [cty| typename NSString * |])+    , (''(),      [cty| void |])+    ]+haskellToCTypeMap _lang+  = Map.empty +-- Check whether the given C type is an overt pointer.+--+isCPtrType :: QC.Type -> Bool+isCPtrType (Type _ (Ptr {}) _)           = True+isCPtrType (Type _ (BlockPtr {}) _)      = True+isCPtrType (Type _ (Array {}) _)         = True+isCPtrType ty+  | ty == [cty| typename HsStablePtr |]  = True+  | otherwise                            = False++ -- Determine marshallers and their bridging types -- ---------------------------------------------- --- Constructs Haskell code to marshal a value (used to marshal arguments and results).+-- |Constructs Haskell code to marshal a value (used to marshal arguments and results). -- -- * The first argument is the code referring to the value to be marshalled. -- * The second argument is the continuation that gets the marshalled value as an argument. -- type HaskellMarshaller = TH.ExpQ -> TH.ExpQ -> TH.ExpQ --- Constructs C code to marshal an argument (used to marshal arguments and results).+-- |Constructs C code to marshal an argument (used to marshal arguments and results). -- -- * The argument is the identifier of the value to be marshalled. -- * The result of the generated expression is the marshalled value. -- type CMarshaller = TH.Name -> QC.Exp --- Generate the type-specific marshalling code for Haskell to C land marshalling for a Haskell-C type pair.+-- |Generate the type-specific marshalling code for Haskell to C land marshalling for a Haskell-C type pair. -- -- The result has the following components: --@@ -134,7 +203,44 @@ -- * Generator for the C-side marshalling code. -- generateHaskellToCMarshaller :: TH.Type -> QC.Type -> Q (TH.TypeQ, QC.Type, HaskellMarshaller, CMarshaller)+generateHaskellToCMarshaller hsTy@(ConT maybe `AppT` argTy) cTy+  | maybe == ''Maybe && isCPtrType cTy+  = do +    { (argTy', cTy', hsMarsh, cMarsh) <- generateHaskellToCMarshaller argTy cTy+    ; ty <- argTy'+    ; case ty of+        ConT ptr `AppT` _ +          | ptr == ''C.Ptr       -> return ( argTy'+                                           , cTy'+                                           , \val cont -> [| case $val of+                                                               Nothing   -> $cont C.nullPtr+                                                               Just val' -> $(hsMarsh [|val'|] cont) |] +                                           , cMarsh+                                           )+          | ptr == ''C.StablePtr -> return ( argTy'+                                           , cTy'+                                           , \val cont -> [| case $val of+                                                               Nothing   -> $cont (C.castPtrToStablePtr C.nullPtr)+                                                               Just val' -> $(hsMarsh [|val'|] cont) |]+                                                               -- NB: the above cast works for GHC, but is in the grey area+                                                               --     of the FFI spec+                                           , cMarsh+                                           )+        _ -> reportErrorAndFail ObjC $ "missing 'Maybe' marshalling for '" ++ prettyQC cTy ++ "' to '" ++ TH.pprint hsTy ++ "'"+    } generateHaskellToCMarshaller hsTy cTy+  | Just hsMarshalTy <- Map.lookup cTy cIntegralMap    -- checking whether it is an integral type+  = return ( hsMarshalTy+           , cTy+           , \val cont -> [| $cont (fromIntegral $val) |]+           , \argName -> [cexp| $id:(show argName) |]+           )+  | Just hsMarshalTy <- Map.lookup cTy cFloatingMap    -- checking whether it is a floating type+  = return ( hsMarshalTy+           , cTy+           , \val cont -> [| $cont (realToFrac $val) |]+           , \argName -> [cexp| $id:(show argName) |]+           )   | cTy == [cty| typename NSString * |]    = return ( [t| C.CString |]            , [cty| char * |]@@ -150,7 +256,7 @@   | otherwise   = reportErrorAndFail ObjC $ "cannot marshal '" ++ TH.pprint hsTy ++ "' to '" ++ prettyQC cTy ++ "'" --- Generate the type-specific marshalling code for Haskell to C land marshalling for a C-Haskell type pair.+-- |Generate the type-specific marshalling code for Haskell to C land marshalling for a C-Haskell type pair. -- -- The result has the following components: --@@ -161,6 +267,18 @@ -- generateCToHaskellMarshaller :: TH.Type -> QC.Type -> Q (TH.TypeQ, QC.Type, HaskellMarshaller, CMarshaller) generateCToHaskellMarshaller hsTy cTy+  | Just hsMarshalTy <- Map.lookup cTy cIntegralMap    -- checking whether it is an integral type+  = return ( hsMarshalTy+           , cTy+           , \val cont -> [| $cont (fromIntegral $val) |]+           , \argName -> [cexp| $id:(show argName) |]+           )+  | Just hsMarshalTy <- Map.lookup cTy cFloatingMap    -- checking whether it is a floating type+  = return ( hsMarshalTy+           , cTy+           , \val cont -> [| $cont (realToFrac $val) |]+           , \argName -> [cexp| $id:(show argName) |]+           )   | cTy == [cty| typename NSString * |]   = return ( [t| C.CString |]            , [cty| char * |]@@ -192,3 +310,21 @@   | otherwise   = reportErrorAndFail ObjC $ "cannot marshall '" ++ prettyQC cTy ++ "' to '" ++ TH.pprint hsTy ++ "'"     +cIntegralMap = Map.fromList+               [ ([cty| char |],               [t| C.CChar |])+               , ([cty| signed char |],        [t| C.CChar |])+               , ([cty| unsigned char |],      [t| C.CUChar |])+               , ([cty| short |],              [t| C.CShort |])+               , ([cty| unsigned short |],     [t| C.CUShort |])+               , ([cty| int |],                [t| C.CInt |])+               , ([cty| unsigned int |],       [t| C.CUInt |])+               , ([cty| long |],               [t| C.CLong |])+               , ([cty| unsigned long |],      [t| C.CULong |])+               , ([cty| long long |],          [t| C.CLLong |])+               , ([cty| unsigned long long |], [t| C.CULLong |])+               ]++cFloatingMap = Map.fromList+               [ ([cty| float |] , [t| C.CFloat |])+               , ([cty| double |], [t| C.CDouble |])+               ]
language-c-inline.cabal view
@@ -1,5 +1,5 @@ Name:                   language-c-inline-Version:                0.3.0.1+Version:                0.5.0.0 Cabal-version:          >= 1.9.2 Tested-with:            GHC == 7.6.3 Build-type:             Simple@@ -16,6 +16,10 @@                         .                         Known bugs: <https://github.com/mchakravarty/language-c-inline/issues>                         .+                        * New in 0.5.0.0: Marshalling of numeric types+                        .+                        * New in 0.4.0.0: Maybe types are marshalled as pointers that may be nil & bug fixes.+                        .                         * New in 0.3.0.0: Boxed Haskell types without a dedicated type mapping are marshalled using stable                         pointers.                         .@@ -35,7 +39,6 @@ Stability:              Experimental  Extra-source-files:     README.md-                        tests/objc/concept/Makefile                         tests/objc/app/App.hs                         tests/objc/app/AppDelegate.hs                         tests/objc/app/Interpreter.hs@@ -47,8 +50,12 @@                         tests/objc/app/HSApp.app/Contents/Resources/en.lproj/Credits.rtf                         tests/objc/app/HSApp.app/Contents/Resources/en.lproj/InfoPlist.strings                         tests/objc/app/HSApp.app/Contents/Resources/en.lproj/MainMenu.nib+                        tests/objc/concept/Makefile                         tests/objc/minimal/Makefile                         tests/objc/minimal/Main.hs+                        tests/objc/record/Makefile+                        tests/objc/record/Main.hs+                        tests/objc/record/Particle.hs  Source-repository head   Type:                 git@@ -57,6 +64,7 @@ Library   Build-depends:        array,                         base              >= 4.0 && < 5,+                        containers        >= 0.4,                         filepath          >= 1.2,                         language-c-quote  >= 0.7,                         mainland-pretty   >= 0.2.5,
tests/objc/app/AppDelegate.hs view
@@ -30,7 +30,7 @@                          { result <- typeOf session expr                          ; return $ formatResult input result                          }-      (command, _)    -> return $ "Haskell> " ++ input ++ "\nUnknown command" ++ command ++ "\n"+      (command, _)    -> return $ "Haskell> " ++ input ++ "\nUnknown command '" ++ command ++ "'\n" evalExpr session expr   = do      { result <- eval session expr
tests/objc/app/Interpreter.hs view
@@ -21,9 +21,7 @@ import Control.Concurrent import Control.Exception            (SomeException, evaluate) import Control.Monad-import "MonadCatchIO-mtl" -       Control.Monad.CatchIO-import Control.Monad.Error+import Control.Monad.Catch  import System.IO @@ -80,9 +78,9 @@     ; putMVar inlet $ Just $       -- the interpreter command we send over to the interpreter thread         do           {                  -- demand the result to force any contained exceptions-          ; result <- (do { !result <- Interp.eval e-                          ; return result }-                      `catchError` (return . pprError))+          ; result <- do { !result <- Interp.eval e+                         ; return result }+                      `catch` (return . pprError)                       `catch` (return . (show :: SomeException -> String))           ; Interp.lift $ putMVar resultMV (Result result)           }@@ -100,9 +98,9 @@     ; putMVar inlet $ Just $       -- the interpreter command we send over to the interpreter thread         do           {                  -- demand the result to force any contained exceptions-          ; result <- (do { !result <- Interp.typeOf e-                          ; return result }-                      `catchError` (return . pprError))+          ; result <- do { !result <- Interp.typeOf e+                         ; return result }+                      `catch` (return . pprError)                       `catch` (return . (show :: SomeException -> String))           ; Interp.lift $ putMVar resultMV (Result result)           }@@ -120,11 +118,11 @@     ; putMVar inlet $ Just $       -- the interpreter command we send over to the interpreter thread         do           {                  -- demand the result to force any contained exceptions-          ; result <- (do { Interp.loadModules [mname]-                          ; mods <- Interp.getLoadedModules-                          ; Interp.setTopLevelModules mods-                          ; return ("Successfully loaded '" ++ mname ++ "'") }-                      `catchError` (return . pprError))+          ; result <- do { Interp.loadModules [mname]+                         ; mods <- Interp.getLoadedModules+                         ; Interp.setTopLevelModules mods+                         ; return ("Successfully loaded '" ++ mname ++ "'") }+                      `catch` (return . pprError)                       `catch` (return . (show :: SomeException -> String))           ; Interp.lift $ putMVar resultMV (Result result)           }@@ -133,6 +131,6 @@  pprError :: Interp.InterpreterError -> String pprError (Interp.UnknownError msg) = msg-pprError (Interp.WontCompile errs) = "Compile time error: \n" ++ concatMap Interp.errMsg errs+pprError (Interp.WontCompile errs) = "Compile time error: \n" ++ unlines (map Interp.errMsg errs) pprError (Interp.NotAllowed msg)   = "Permission denied: " ++ msg pprError (Interp.GhcException msg) = "Internal error: " ++ msg
tests/objc/concept/MainInlineObjC.hs view
@@ -4,5 +4,5 @@ main   = do     { objc_initialise-    ; dumpURL "https://raw.github.com/mchakravarty/language-c-inline/master/tests/objc/TestInlineObjC.hs"+    ; dumpURL "https://raw.githubusercontent.com/mchakravarty/language-c-inline/master/tests/objc/concept/TestInlineObjC.hs"     }
+ tests/objc/record/Main.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++import Language.C.Quote.ObjC+import Language.C.Inline.ObjC++objc_import ["<Foundation/Foundation.h>", "Particle_objc.h"]++go :: IO ()+go = $(objc [] ''() [cexp| ({ +    typename Particle *particle = [Particle particleWithMass:1.0]; +    NSLog(@"The mass is %f", particle.mass); +  }) |])++objc_emit+++main = objc_initialise >> go
+ tests/objc/record/Makefile view
@@ -0,0 +1,25 @@+HC      = ghc+CFLAGS  = -fobjc-arc -I$(shell $(HC) --print-libdir)/include+HCFLAGS =+LDFLAGS = -package template-haskell -package language-c-quote -package language-c-inline -framework Foundation++OBJS = Main.o Main_objc.o Particle.o Particle_objc.o ++default: Particle++%.o: %.hs+	$(HC) -c $< $(HCFLAGS)++Particle.o:+Main.o: Particle.o++Main_objc.m: Main.o+Particle_objc.m: Particle.o++Particle: $(OBJS)+	$(HC) -o $@ $^ $(LDFLAGS)++.PHONY: clean++clean:+	rm -f *.o *.hi Main_objc.[hm] Particle_objc.[hm] *_stub.h Particle
+ tests/objc/record/Particle.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++-- Marshalling a record structure++module Particle (objc_initialise) where++  -- language-c-inline+import Language.C.Quote.ObjC+import Language.C.Inline.ObjC++objc_import ["<Foundation/Foundation.h>", "HsFFI.h"]+++-- Haskell code used from Objective-C.++type Point = (Float, Float)++origin = (0, 0)++newtype Vector = Vector (Float, Float)++zero = Vector (0, 0)++data Particle = Particle +                { mass :: Float+                , loc  :: Point+                , vel  :: Vector+                , acc  :: Vector+                }++newParticle :: Float -> Particle+newParticle mass = Particle mass origin zero zero++particleMass :: Particle -> Float+particleMass = mass+++objc_interface [cunit|++@interface Particle : NSObject++@property (readonly) float mass;+++ (typename instancetype)particleWithMass:(float)mass;++- (typename instancetype)init;++- (typename instancetype)initWithMass:(float)mass;+++@end+|]+++objc_implementation ['newParticle, 'particleMass] [cunit|++@interface Particle ()++@property (readonly, assign, nonatomic) typename HsStablePtr particle;++@end++@implementation Particle++// Initialisation+++ (typename instancetype)particleWithMass:(float)mass+{+  return [[Particle alloc] initWithMass:mass];+}++- (typename instancetype)init+{+  return [self initWithMass:0.0];+}++- (typename instancetype)initWithMass:(float)mass+{+  self = [super init];+  if (self)+    _particle = newParticle(mass);+  return self;+}++// Getters and setters++- (float)mass+{+  return particleMass(self.particle);+}++@end+|]+++objc_emit