content: {-# 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 (CPTEnum _) _ ->
        [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))
            )
  