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 +39/−15
- Language/C/Inline/ObjC/Marshal.hs +154/−18
- language-c-inline.cabal +10/−2
- tests/objc/app/AppDelegate.hs +1/−1
- tests/objc/app/Interpreter.hs +13/−15
- tests/objc/concept/MainInlineObjC.hs +1/−1
- tests/objc/record/Main.hs +17/−0
- tests/objc/record/Makefile +25/−0
- tests/objc/record/Particle.hs +96/−0
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