packages feed

fficxx-0.7.0.0: src/FFICXX/Generate/Code/Cpp.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

module FFICXX.Generate.Code.Cpp where

import Data.Char (toUpper)
import Data.Functor.Identity (Identity)
import Data.List (intercalate, intersperse)
import FFICXX.Generate.Code.Primitive
  ( CFunSig (..),
    accessorCFunSig,
    argToCallCExp,
    argsToCTypVar,
    argsToCTypVarNoSelf,
    c2Cxx,
    cxx2C,
    genericFuncArgs,
    genericFuncRet,
    returnCType,
    tmplAccessorToTFun,
    tmplAllArgsToCTypVar,
    tmplAppTypeFromForm,
    tmplArgToCTypVar,
    tmplArgToCallCExp,
    tmplMemFuncArgToCTypVar,
    tmplMemFuncReturnCType,
    tmplReturnCType,
  )
import FFICXX.Generate.Name
  ( aliasedFuncName,
    cppFuncName,
    ffiClassName,
    ffiTmplFuncName,
    hsTemplateMemberFunctionName,
  )
import FFICXX.Generate.Type.Class
  ( Accessor (Getter, Setter),
    Arg (..),
    CPPTypes (..),
    CTypes (..),
    Class (..),
    Form (FormNested, FormSimple),
    Function (..),
    IsConst (Const, NoConst),
    Selfness (NoSelf, Self),
    TLOrdinary (..),
    TLTemplate (..),
    TemplateAppInfo (..),
    TemplateClass (..),
    TemplateFunction (..),
    TemplateMemberFunction (..),
    Types (..),
    Variable (..),
    argsFromOpExp,
    isDeleteFunc,
    isNewFunc,
    isStaticFunc,
    isVirtualFunc,
    opSymbol,
    virtualFuncs,
  )
import FFICXX.Generate.Type.Module (ClassImportHeader (..))
import FFICXX.Generate.Util (firstUpper, toUppers)
import qualified FFICXX.Runtime.CodeGen.Cxx as R
import FFICXX.Runtime.TH (IsCPrimitive (CPrim, NonCPrim))

--
--
-- Class Declaration and Definition
--

----
---- Declaration
----

---- "Class Type Declaration" Instances

typedefStmts :: String -> [R.CStatement Identity]
typedefStmts classname =
  [ R.TypeDef (R.CTVerbatim ("struct " <> classname_tag)) (R.sname classname_t),
    R.TypeDef (R.CTVerbatim (classname_t <> " *")) (R.sname classname_p),
    R.TypeDef (R.CTVerbatim (classname_t <> " const*")) (R.sname ("const_" <> classname_p))
  ]
  where
    classname_tag = classname <> "_tag"
    classname_t = classname <> "_t"
    classname_p = classname <> "_p"

genCppHeaderMacroType :: Class -> [R.CStatement Identity]
genCppHeaderMacroType c =
  [R.Comment "Opaque type definition for $classname"]
    <> typedefStmts (ffiClassName c)

---- "Class Declaration Virtual" Declaration

genCppHeaderMacroVirtual :: Class -> R.CMacro Identity
genCppHeaderMacroVirtual aclass =
  let funcDecls =
        map R.CDeclaration
          . map (funcToDecl aclass)
          . virtualFuncs
          . class_funcs
          $ aclass
      macrocname = map toUpper (ffiClassName aclass)
      macroname = macrocname <> "_DECL_VIRT"
   in R.Define (R.sname macroname) [R.sname "Type"] funcDecls

genCppHeaderMacroNonVirtual :: Class -> R.CMacro Identity
genCppHeaderMacroNonVirtual c =
  let funcDecls =
        map R.CDeclaration
          . map (funcToDecl c)
          . filter (not . isVirtualFunc)
          . class_funcs
          $ c
      macrocname = map toUpper (ffiClassName c)
      macroname = macrocname <> "_DECL_NONVIRT"
   in R.Define (R.sname macroname) [R.sname "Type"] funcDecls

---- "Class Declaration Accessor" Declaration

genCppHeaderMacroAccessor :: Class -> R.CMacro Identity
genCppHeaderMacroAccessor c =
  let funcDecls = map R.CDeclaration $ accessorsToDecls (class_vars c)
      macrocname = map toUpper (ffiClassName c)
      macroname = macrocname <> "_DECL_ACCESSOR"
   in R.Define (R.sname macroname) [R.sname "Type"] funcDecls

---- "Class Declaration Virtual/NonVirtual/Accessor" Instances

genCppHeaderInstVirtual :: (Class, Class) -> R.CStatement Identity
genCppHeaderInstVirtual (p, c) =
  let macroname = map toUpper (ffiClassName p) <> "_DECL_VIRT"
   in R.CMacroApp (R.sname macroname) [R.sname (ffiClassName c)]

genCppHeaderInstNonVirtual :: Class -> R.CStatement Identity
genCppHeaderInstNonVirtual c =
  let macroname = map toUpper (ffiClassName c) <> "_DECL_NONVIRT"
   in R.CMacroApp (R.sname macroname) [R.sname (ffiClassName c)]

genCppHeaderInstAccessor :: Class -> R.CStatement Identity
genCppHeaderInstAccessor c =
  let macroname = map toUpper (ffiClassName c) <> "_DECL_ACCESSOR"
   in R.CMacroApp (R.sname macroname) [R.sname (ffiClassName c)]

----
---- Definition
----

---- "Class Definition Virtual" Declaration

genCppDefMacroVirtual :: Class -> R.CMacro Identity
genCppDefMacroVirtual aclass =
  let funcDefStr =
        intercalate "\n"
          . map (R.renderCStmt . funcToDef aclass)
          . virtualFuncs
          . class_funcs
          $ aclass
      macrocname = map toUpper (ffiClassName aclass)
      macroname = macrocname <> "_DEF_VIRT"
   in R.Define (R.sname macroname) [R.sname "Type"] [R.CVerbatim funcDefStr]

---- "Class Definition NonVirtual" Declaration

genCppDefMacroNonVirtual :: Class -> R.CMacro Identity
genCppDefMacroNonVirtual aclass =
  let funcDefStr =
        intercalate "\n"
          . map (R.renderCStmt . funcToDef aclass)
          . filter (not . isVirtualFunc)
          . class_funcs
          $ aclass
      macrocname = map toUpper (ffiClassName aclass)
      macroname = macrocname <> "_DEF_NONVIRT"
   in R.Define (R.sname macroname) [R.sname "Type"] [R.CVerbatim funcDefStr]

---- Define Macro to provide Accessor C-C++ shim code for a class

genCppDefMacroAccessor :: Class -> R.CMacro Identity
genCppDefMacroAccessor c =
  let funcDefs = concatMap (\v -> [accessorToDef v Getter, accessorToDef v Setter]) (class_vars c)
      macrocname = map toUpper (ffiClassName c)
      macroname = macrocname <> "_DEF_ACCESSOR"
   in R.Define (R.sname macroname) [R.sname "Type"] funcDefs

---- Define Macro to provide TemplateMemberFunction C-C++ shim code for a class

genCppDefMacroTemplateMemberFunction ::
  Class ->
  TemplateMemberFunction ->
  R.CMacro Identity
genCppDefMacroTemplateMemberFunction c f =
  R.Define
    (R.sname macroname)
    (map R.sname (tmf_params f))
    [ R.CExtern [R.CDeclaration decl],
      tmplMemberFunToDef c f,
      autoinst
    ]
  where
    nsuffix = intersperse (R.NamePart "_") $ map R.NamePart (tmf_params f)
    macroname = hsTemplateMemberFunctionName c f
    decl = tmplMemberFunToDecl c f
    autoinst =
      R.CInit
        ( R.CVarDecl
            R.CTAuto
            (R.CName (R.NamePart ("a_" <> macroname <> "_") : nsuffix))
        )
        (R.CVar (R.CName (R.NamePart (macroname <> "_") : nsuffix)))

---- Invoke Macro to define Virtual/NonVirtual method for a class

genCppDefInstVirtual :: (Class, Class) -> R.CStatement Identity
genCppDefInstVirtual (p, c) =
  let macroname = map toUpper (ffiClassName p) <> "_DEF_VIRT"
   in R.CMacroApp (R.sname macroname) [R.sname (ffiClassName c)]

genCppDefInstNonVirtual :: Class -> R.CStatement Identity
genCppDefInstNonVirtual c =
  let macroname = toUppers (ffiClassName c) <> "_DEF_NONVIRT"
   in R.CMacroApp (R.sname macroname) [R.sname (ffiClassName c)]

genCppDefInstAccessor :: Class -> R.CStatement Identity
genCppDefInstAccessor c =
  let macroname = toUppers (ffiClassName c) <> "_DEF_ACCESSOR"
   in R.CMacroApp (R.sname macroname) [R.sname (ffiClassName c)]

-----------------

genAllCppHeaderInclude :: ClassImportHeader -> [R.CMacro Identity]
genAllCppHeaderInclude header =
  map R.Include (cihIncludedHPkgHeadersInCPP header <> cihIncludedCPkgHeaders header)

----

-------------------------
-- TOP LEVEL FUNCTIONS --
-------------------------

topLevelDecl :: TLOrdinary -> R.CFunDecl Identity
topLevelDecl TopLevelFunction {..} = R.CFunDecl ret func args
  where
    ret = returnCType toplevelfunc_ret
    func = R.sname ("TopLevel_" <> maybe toplevelfunc_name id toplevelfunc_alias)
    args = argsToCTypVarNoSelf toplevelfunc_args
topLevelDecl TopLevelVariable {..} = R.CFunDecl ret func []
  where
    ret = returnCType toplevelvar_ret
    func = R.sname ("TopLevel_" <> maybe toplevelvar_name id toplevelvar_alias)

genTopLevelCppDefinition :: TLOrdinary -> R.CStatement Identity
genTopLevelCppDefinition tf@TopLevelFunction {..} =
  let decl = topLevelDecl tf
      body =
        returnCpp
          NonCPrim
          (toplevelfunc_ret)
          (R.CApp (R.CVar (R.sname toplevelfunc_name)) (map argToCallCExp toplevelfunc_args))
   in R.CDefinition Nothing decl body
genTopLevelCppDefinition tv@TopLevelVariable {..} =
  let decl = topLevelDecl tv
      body = returnCpp NonCPrim (toplevelvar_ret) (R.CVar (R.sname toplevelvar_name))
   in R.CDefinition Nothing decl body

genTmplFunCpp ::
  IsCPrimitive ->
  TemplateClass ->
  TemplateFunction ->
  R.CMacro Identity
genTmplFunCpp b t@TmplCls {..} f =
  R.Define
    (R.sname macroname)
    (map R.sname ("callmod" : tclass_params))
    [ R.CExtern [R.CDeclaration decl],
      tmplFunToDef b t f,
      autoinst
    ]
  where
    nsuffix = intersperse (R.NamePart "_") $ map R.NamePart tclass_params
    suffix = case b of CPrim -> "_s"; NonCPrim -> ""
    macroname = tclass_name <> "_" <> ffiTmplFuncName f <> suffix
    decl = tmplFunToDecl b t f
    autoinst =
      R.CInit
        ( R.CVarDecl
            R.CTAuto
            (R.CName (R.NamePart "a_" : R.NamePart "callmod" : R.NamePart ("_" <> tclass_name <> "_" <> ffiTmplFuncName f <> "_") : nsuffix))
        )
        (R.CVar (R.CName (R.NamePart (tclass_name <> "_" <> ffiTmplFuncName f <> "_") : nsuffix)))

genTLTmplFunCpp ::
  IsCPrimitive ->
  TLTemplate ->
  R.CMacro Identity
genTLTmplFunCpp b t@TopLevelTemplateFunction {..} =
  R.Define
    (R.sname macroname)
    (map R.sname ("callmod" : topleveltfunc_params))
    [ R.CExtern [R.CDeclaration decl],
      topLevelTemplateFunToDef b t,
      autoinst
    ]
  where
    nsuffix = intersperse (R.NamePart "_") $ map R.NamePart topleveltfunc_params
    suffix = case b of CPrim -> "_s"; NonCPrim -> ""
    macroname = firstUpper topleveltfunc_name <> "_instance" <> suffix
    decl = topLevelTemplateFunToDecl b t
    autoinst =
      R.CInit
        ( R.CVarDecl
            R.CTAuto
            (R.CName (R.NamePart "a_" : R.NamePart "callmod" : R.NamePart ("_TL_" <> topleveltfunc_name <> "_") : nsuffix))
        )
        (R.CVar (R.CName (R.NamePart ("TL_" <> topleveltfunc_name <> "_") : nsuffix)))

genTmplVarCpp ::
  IsCPrimitive ->
  TemplateClass ->
  Variable ->
  [R.CMacro Identity]
genTmplVarCpp b t@TmplCls {..} var@(Variable (Arg {})) =
  [gen var Getter, gen var Setter]
  where
    nsuffix = intersperse (R.NamePart "_") $ map R.NamePart tclass_params
    suffix = case b of CPrim -> "_s"; NonCPrim -> ""
    gen v a =
      let f = tmplAccessorToTFun v a
          macroname = tclass_name <> "_" <> ffiTmplFuncName f <> suffix
       in R.Define
            (R.sname macroname)
            (map R.sname ("callmod" : tclass_params))
            [ R.CExtern [R.CDeclaration (tmplFunToDecl b t f)],
              tmplVarToDef b t v a,
              R.CInit
                ( R.CVarDecl
                    R.CTAuto
                    (R.CName (R.NamePart "a_" : R.NamePart "callmod" : R.NamePart ("_" <> tclass_name <> "_" <> ffiTmplFuncName f <> "_") : nsuffix))
                )
                (R.CVar (R.CName (R.NamePart (tclass_name <> "_" <> ffiTmplFuncName f <> "_") : nsuffix)))
            ]

-- |
genTmplClassCpp ::
  IsCPrimitive ->
  TemplateClass ->
  -- | (member functions, member accessors)
  ([TemplateFunction], [Variable]) ->
  R.CMacro Identity
genTmplClassCpp b TmplCls {..} (fs, vs) =
  R.Define (R.sname macroname) params body
  where
    params = map R.sname ("callmod" : tclass_params)
    suffix = case b of CPrim -> "_s"; NonCPrim -> ""
    tname = tclass_name
    macroname = tname <> "_instance" <> suffix
    macro1 f@TFun {} = R.CMacroApp (R.sname (tname <> "_" <> ffiTmplFuncName f <> suffix)) params
    macro1 f@TFunNew {} = R.CMacroApp (R.sname (tname <> "_" <> ffiTmplFuncName f <> suffix)) params
    macro1 TFunDelete = R.CMacroApp (R.sname (tname <> "_delete" <> suffix)) params
    macro1 f@TFunOp {} = R.CMacroApp (R.sname (tname <> "_" <> ffiTmplFuncName f <> suffix)) params
    body =
      map macro1 fs
        ++ (map macro1 . concatMap (\v -> [tmplAccessorToTFun v Getter, tmplAccessorToTFun v Setter])) vs

-- |
returnCpp ::
  IsCPrimitive ->
  Types ->
  R.CExp Identity ->
  [R.CStatement Identity]
returnCpp b ret caller =
  case ret of
    Void ->
      [R.CExpSA caller]
    SelfType ->
      [ R.CReturn $
          R.CTApp
            (R.sname "from_nonconst_to_nonconst")
            [ R.CTSimple (R.CName [R.NamePart "Type", R.NamePart "_t"]),
              R.CTSimple (R.sname "Type")
            ]
            [R.CCast (R.CTStar (R.CTSimple (R.sname "Type"))) caller]
      ]
    CT (CRef _) _ ->
      [R.CReturn $ R.CAddr caller]
    CT _ _ ->
      [R.CReturn caller]
    CPT (CPTClass c') isconst ->
      [ R.CReturn $
          R.CTApp
            ( case isconst of
                NoConst -> R.sname "from_nonconst_to_nonconst"
                Const -> R.sname "from_const_to_nonconst"
            )
            [R.CTSimple (R.sname (str <> "_t")), R.CTSimple (R.sname str)]
            [R.CCast (R.CTStar (R.CTSimple (R.sname str))) caller]
      ]
      where
        str = ffiClassName c'
    CPT (CPTClassRef c') isconst ->
      [ R.CReturn $
          R.CTApp
            ( case isconst of
                NoConst -> R.sname "from_nonconst_to_nonconst"
                Const -> R.sname "from_const_to_nonconst"
            )
            [R.CTSimple (R.sname (str <> "_t")), R.CTSimple (R.sname str)]
            [R.CAddr caller]
      ]
      where
        str = ffiClassName c'
    CPT (CPTClassCopy c') isconst ->
      [ R.CReturn $
          R.CTApp
            ( case isconst of
                NoConst -> R.sname "from_nonconst_to_nonconst"
                Const -> R.sname "from_const_to_nonconst"
            )
            [R.CTSimple (R.sname (str <> "_t")), R.CTSimple (R.sname str)]
            [R.CNew (R.sname str) [caller]]
      ]
      where
        str = ffiClassName c'
    CPT (CPTClassMove c') isconst ->
      -- TODO: check whether this is working or not.
      [ R.CReturn $
          R.CApp
            (R.CVar (R.sname "std::move"))
            [ R.CTApp
                ( case isconst of
                    NoConst -> R.sname "from_nonconst_to_nonconst"
                    Const -> R.sname "from_const_to_nonconst"
                )
                [R.CTSimple (R.sname (str <> "_t")), R.CTSimple (R.sname str)]
                [R.CAddr caller]
            ]
      ]
      where
        str = ffiClassName c'
    TemplateApp (TemplateAppInfo _ _ cpptype) ->
      [ R.CInit
          (R.CVarDecl (R.CTStar (R.CTVerbatim cpptype)) (R.sname "r"))
          (R.CNew (R.sname cpptype) [caller]),
        R.CReturn $
          R.CTApp
            (R.sname "static_cast")
            [R.CTStar R.CTVoid]
            [R.CVar (R.sname "r")]
      ]
    TemplateAppRef (TemplateAppInfo _ _ cpptype) ->
      [ R.CInit
          (R.CVarDecl (R.CTStar (R.CTVerbatim cpptype)) (R.sname "r"))
          (R.CNew (R.sname cpptype) [caller]),
        R.CReturn $
          R.CTApp
            (R.sname "static_cast")
            [R.CTStar R.CTVoid]
            [R.CVar (R.sname "r")]
      ]
    TemplateAppMove (TemplateAppInfo _ _ cpptype) ->
      [ R.CInit
          (R.CVarDecl (R.CTStar (R.CTVerbatim cpptype)) (R.sname "r"))
          (R.CNew (R.sname cpptype) [caller]),
        R.CReturn $
          R.CApp
            (R.CVar (R.sname "std::move"))
            [ R.CTApp
                (R.sname "static_cast")
                [R.CTStar R.CTVoid]
                [R.CVar (R.sname "r")]
            ]
      ]
    TemplateType _ ->
      error "returnCpp: TemplateType"
    TemplateParam typ ->
      [ R.CReturn $
          case b of
            CPrim -> caller
            NonCPrim ->
              R.CTApp
                (R.sname "from_nonconst_to_nonconst")
                [R.CTSimple (R.CName [R.NamePart typ, R.NamePart "_t"]), R.CTSimple (R.sname typ)]
                [R.CCast (R.CTStar (R.CTSimple (R.sname typ))) $ R.CAddr caller]
      ]
    TemplateParamPointer typ ->
      [ R.CReturn $
          case b of
            CPrim -> caller
            NonCPrim ->
              R.CTApp
                (R.sname "from_nonconst_to_nonconst")
                [R.CTSimple (R.CName [R.NamePart typ, R.NamePart "_t"]), R.CTSimple (R.sname typ)]
                [caller]
      ]

-- Function Declaration and Definition

funcToDecl :: Class -> Function -> R.CFunDecl Identity
funcToDecl c func
  | isNewFunc func || isStaticFunc func =
    let ret = returnCType (genericFuncRet func)
        fname =
          R.CName [R.NamePart "Type", R.NamePart ("_" <> aliasedFuncName c func)]
        args = argsToCTypVarNoSelf (genericFuncArgs func)
     in R.CFunDecl ret fname args
  | otherwise =
    let ret = returnCType (genericFuncRet func)
        fname =
          R.CName [R.NamePart "Type", R.NamePart ("_" <> aliasedFuncName c func)]
        args = argsToCTypVar (genericFuncArgs func)
     in R.CFunDecl ret fname args

funcToDef :: Class -> Function -> R.CStatement Identity
funcToDef c func
  | isNewFunc func =
    let body =
          [ R.CInit
              (R.CVarDecl (R.CTStar (R.CTSimple (R.sname "Type"))) (R.sname "newp"))
              (R.CNew (R.sname "Type") $ map argToCallCExp (genericFuncArgs func)),
            R.CReturn $
              R.CTApp
                (R.sname "from_nonconst_to_nonconst")
                [R.CTSimple (R.CName [R.NamePart "Type", R.NamePart "_t"]), R.CTSimple (R.sname "Type")]
                [R.CVar (R.sname "newp")]
          ]
     in R.CDefinition Nothing (funcToDecl c func) body
  | isDeleteFunc func =
    let body =
          [ R.CDelete $
              R.CTApp
                (R.sname "from_nonconst_to_nonconst")
                [R.CTSimple (R.sname "Type"), R.CTSimple (R.CName [R.NamePart "Type", R.NamePart "_t"])]
                [R.CVar (R.sname "p")]
          ]
     in R.CDefinition Nothing (funcToDecl c func) body
  | isStaticFunc func =
    let body =
          returnCpp NonCPrim (genericFuncRet func) $
            R.CApp (R.CVar (R.sname (cppFuncName c func))) (map argToCallCExp (genericFuncArgs func))
     in R.CDefinition Nothing (funcToDecl c func) body
  | otherwise =
    let caller =
          R.CBinOp
            R.CArrow
            ( R.CApp
                ( R.CEMacroApp
                    (R.sname "TYPECASTMETHOD")
                    [R.sname "Type", R.sname (aliasedFuncName c func), R.sname (class_name c)]
                )
                [R.CVar (R.sname "p")]
            )
            (R.CApp (R.CVar (R.sname (cppFuncName c func))) (map argToCallCExp (genericFuncArgs func)))
        body = returnCpp NonCPrim (genericFuncRet func) caller
     in R.CDefinition Nothing (funcToDecl c func) body

-- template function declaration and definition

tmplFunToDecl ::
  IsCPrimitive ->
  TemplateClass ->
  TemplateFunction ->
  R.CFunDecl Identity
tmplFunToDecl b t@TmplCls {..} f =
  let nsuffix = intersperse (R.NamePart "_") $ map R.NamePart tclass_params
   in case f of
        TFun {..} ->
          let ret = tmplReturnCType b tfun_ret
              func = R.CName (R.NamePart (tclass_name <> "_" <> ffiTmplFuncName f <> "_") : nsuffix)
              args = tmplAllArgsToCTypVar b Self t tfun_args
           in R.CFunDecl ret func args
        TFunNew {..} ->
          let ret = tmplReturnCType b (TemplateType t)
              func = R.CName (R.NamePart (tclass_name <> "_" <> ffiTmplFuncName f <> "_") : nsuffix)
              args = tmplAllArgsToCTypVar b NoSelf t tfun_new_args
           in R.CFunDecl ret func args
        TFunDelete ->
          let ret = R.CTVoid
              func = R.CName (R.NamePart (tclass_name <> "_delete_") : nsuffix)
              args = tmplAllArgsToCTypVar b Self t []
           in R.CFunDecl ret func args
        TFunOp {..} ->
          let ret = tmplReturnCType b tfun_ret
              func = R.CName (R.NamePart (tclass_name <> "_" <> ffiTmplFuncName f <> "_") : nsuffix)
              args = tmplAllArgsToCTypVar b Self t (argsFromOpExp tfun_opexp)
           in R.CFunDecl ret func args

-- | top-level (bare) template function declaration
topLevelTemplateFunToDecl ::
  IsCPrimitive ->
  TLTemplate ->
  R.CFunDecl Identity
topLevelTemplateFunToDecl b (TopLevelTemplateFunction {..}) =
  let nsuffix = intersperse (R.NamePart "_") $ map R.NamePart topleveltfunc_params
      ret = tmplReturnCType b topleveltfunc_ret
      func = R.CName (R.NamePart ("TL_" <> topleveltfunc_name <> "_") : nsuffix)
      args = map (tmplArgToCTypVar b) topleveltfunc_args
   in R.CFunDecl ret func args

-- | function definition in a template class
tmplFunToDef ::
  IsCPrimitive ->
  TemplateClass ->
  TemplateFunction ->
  R.CStatement Identity
tmplFunToDef b t@TmplCls {..} f =
  R.CDefinition (Just R.Inline) (tmplFunToDecl b t f) body
  where
    typparams = map (R.CTSimple . R.sname) tclass_params
    body =
      case f of
        TFunNew {..} ->
          let caller =
                case tclass_cxxform of
                  FormSimple tclass ->
                    R.CTNew
                      (R.sname tclass)
                      typparams
                      (map (tmplArgToCallCExp b) tfun_new_args)
                  FormNested tclass inner ->
                    R.CTNewI
                      (R.sname tclass)
                      (R.sname inner)
                      typparams
                      (map (tmplArgToCallCExp b) tfun_new_args)
           in [R.CReturn $ R.CTApp (R.sname "static_cast") [R.CTStar R.CTVoid] [caller]]
        TFunDelete ->
          [ R.CDelete $
              R.CTApp
                (R.sname "static_cast")
                [R.CTStar $ tmplAppTypeFromForm tclass_cxxform typparams]
                [R.CVar (R.sname "p")]
          ]
        TFun {..} ->
          returnCpp b (tfun_ret) $
            R.CBinOp
              R.CArrow
              ( R.CTApp
                  (R.sname "static_cast")
                  [R.CTStar $ tmplAppTypeFromForm tclass_cxxform typparams]
                  [R.CVar $ R.sname "p"]
              )
              ( R.CApp
                  (R.CVar (R.sname tfun_oname))
                  (map (tmplArgToCallCExp b) tfun_args)
              )
        TFunOp {..} ->
          returnCpp b (tfun_ret) $
            R.CBinOp
              R.CArrow
              ( R.CTApp
                  (R.sname "static_cast")
                  [R.CTStar $ tmplAppTypeFromForm tclass_cxxform typparams]
                  [R.CVar $ R.sname "p"]
              )
              ( R.CApp
                  (R.CVar (R.sname ("operator" <> opSymbol tfun_opexp)))
                  (map (tmplArgToCallCExp b) (argsFromOpExp tfun_opexp))
              )

-- | function definition in a template class
topLevelTemplateFunToDef ::
  IsCPrimitive ->
  TLTemplate ->
  R.CStatement Identity
topLevelTemplateFunToDef b t@TopLevelTemplateFunction {..} =
  R.CDefinition (Just R.Inline) (topLevelTemplateFunToDecl b t) body
  where
    typparams = map (R.CTSimple . R.sname) topleveltfunc_params
    body =
      returnCpp b (topleveltfunc_ret) $
        R.CTApp
          (R.sname topleveltfunc_oname)
          typparams
          (map (tmplArgToCallCExp b) topleveltfunc_args)

-- |
tmplVarToDef ::
  IsCPrimitive ->
  TemplateClass ->
  Variable ->
  Accessor ->
  R.CStatement Identity
tmplVarToDef b t@TmplCls {..} v@(Variable (Arg {..})) a =
  R.CDefinition (Just R.Inline) (tmplFunToDecl b t f) body
  where
    f = tmplAccessorToTFun v a
    typparams = map (R.CTSimple . R.sname) tclass_params
    body =
      case f of
        TFun {..} ->
          let varexp =
                R.CBinOp
                  R.CArrow
                  ( R.CTApp
                      (R.sname "static_cast")
                      [R.CTStar $ tmplAppTypeFromForm tclass_cxxform typparams]
                      [R.CVar $ R.sname "p"]
                  )
                  (R.CVar (R.sname arg_name))
           in case a of
                Getter -> returnCpp b (tfun_ret) varexp
                Setter ->
                  [ R.CExpSA $
                      R.CBinOp
                        R.CAssign
                        varexp
                        (c2Cxx arg_type (R.CVar (R.sname "value")))
                  ]
        _ -> error "tmplVarToDef: should not happen"

-- Accessor Declaration and Definition

accessorToDecl :: Variable -> Accessor -> R.CFunDecl Identity
accessorToDecl v a =
  let csig = accessorCFunSig (arg_type (unVariable v)) a
      ret = returnCType (cRetType csig)
      fname =
        R.CName
          [ R.NamePart "Type",
            R.NamePart
              ( "_"
                  <> arg_name (unVariable v)
                  <> "_"
                  <> case a of Getter -> "get"; Setter -> "set"
              )
          ]
      args = argsToCTypVar (cArgTypes csig)
   in R.CFunDecl ret fname args

accessorsToDecls :: [Variable] -> [R.CFunDecl Identity]
accessorsToDecls vs =
  concatMap (\v -> [accessorToDecl v Getter, accessorToDecl v Setter]) vs

accessorToDef :: Variable -> Accessor -> R.CStatement Identity
accessorToDef v a =
  let varexp =
        R.CBinOp
          R.CArrow
          ( R.CTApp
              (R.sname "from_nonconst_to_nonconst")
              [R.CTSimple (R.sname "Type"), R.CTSimple (R.CName [R.NamePart "Type", R.NamePart "_t"])]
              [R.CVar (R.sname "p")]
          )
          (R.CVar (R.sname (arg_name (unVariable v))))
      body Getter = R.CReturn $ cxx2C (arg_type (unVariable v)) varexp
      body Setter =
        R.CExpSA $
          R.CBinOp
            R.CAssign
            varexp
            (c2Cxx (arg_type (unVariable v)) (R.CVar (R.sname "x")))
   in R.CDefinition Nothing (accessorToDecl v a) [body a]

-- Template Member Function Declaration and Definition

-- TODO: Handle simple type
tmplMemberFunToDecl :: Class -> TemplateMemberFunction -> R.CFunDecl Identity
tmplMemberFunToDecl c f =
  let nsuffix = intersperse (R.NamePart "_") $ map R.NamePart (tmf_params f)
      ret = tmplMemFuncReturnCType c (tmf_ret f)
      fname =
        R.CName (R.NamePart (hsTemplateMemberFunctionName c f <> "_") : nsuffix)
      args = map (tmplMemFuncArgToCTypVar c) ((Arg SelfType "p") : tmf_args f)
   in R.CFunDecl ret fname args

-- TODO: Handle simple type
tmplMemberFunToDef :: Class -> TemplateMemberFunction -> R.CStatement Identity
tmplMemberFunToDef c f =
  R.CDefinition (Just R.Inline) (tmplMemberFunToDecl c f) body
  where
    tparams = map (R.CTSimple . R.sname) (tmf_params f)
    body =
      returnCpp NonCPrim (tmf_ret f) $
        R.CBinOp
          R.CArrow
          ( R.CTApp
              (R.sname "from_nonconst_to_nonconst")
              [R.CTSimple (R.sname (ffiClassName c)), R.CTSimple (R.sname (ffiClassName c <> "_t"))]
              [R.CVar $ R.sname "p"]
          )
          ( R.CTApp
              (R.sname (tmf_name f))
              tparams
              (map (tmplArgToCallCExp NonCPrim) (tmf_args f))
          )