From 197a3d9d05677de9c6e14c2ffb3a871aecab59e4 Mon Sep 17 00:00:00 2001
From: zy69guqi <richard.angersbach@fau.de>
Date: Thu, 6 Mar 2025 17:47:51 +0100
Subject: [PATCH 1/9] Add composer for "extern C" prefix

---
 src/pystencilssfg/composer/basic_composer.py | 8 +++++++-
 src/pystencilssfg/emission/file_printer.py   | 5 ++++-
 src/pystencilssfg/ir/entities.py             | 6 ++++++
 src/pystencilssfg/lang/expressions.py        | 2 +-
 4 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/src/pystencilssfg/composer/basic_composer.py b/src/pystencilssfg/composer/basic_composer.py
index 31337a6..9e58d78 100644
--- a/src/pystencilssfg/composer/basic_composer.py
+++ b/src/pystencilssfg/composer/basic_composer.py
@@ -91,6 +91,7 @@ class KernelsAdder:
         self._cursor = cursor
         self._kernel_namespace = knamespace
         self._inline: bool = False
+        self._externC: bool = False
         self._loc: SfgNamespaceBlock | None = None
 
     def inline(self) -> KernelsAdder:
@@ -98,6 +99,11 @@ class KernelsAdder:
         self._inline = True
         return self
 
+    def externC(self) -> KernelsAdder:
+        """Generate kernel definitions ``extern "C"`` in the header file."""
+        self._externC = True
+        return self
+
     def add(self, kernel: Kernel, name: str | None = None):
         """Adds an existing pystencils AST to this namespace.
         If a name is specified, the AST's function name is changed."""
@@ -116,7 +122,7 @@ class KernelsAdder:
             kernel.name = kernel_name
 
         khandle = SfgKernelHandle(
-            kernel_name, self._kernel_namespace, kernel, inline=self._inline
+            kernel_name, self._kernel_namespace, kernel, inline=self._inline, externC=self._externC
         )
         self._kernel_namespace.add_kernel(khandle)
 
diff --git a/src/pystencilssfg/emission/file_printer.py b/src/pystencilssfg/emission/file_printer.py
index 648e419..d6b9296 100644
--- a/src/pystencilssfg/emission/file_printer.py
+++ b/src/pystencilssfg/emission/file_printer.py
@@ -84,9 +84,12 @@ class SfgFilePrinter:
     ) -> str:
         match declared_entity:
             case SfgKernelHandle(kernel):
+                func_prefix = "extern C" if declared_entity.externC else ""
+                func_prefix += " inline" if declared_entity.inline else ""
+
                 kernel_printer = CAstPrinter(
                     indent_width=self._indent_width,
-                    func_prefix="inline" if declared_entity.inline else None,
+                    func_prefix=func_prefix,
                 )
                 return kernel_printer.print_signature(kernel) + ";"
 
diff --git a/src/pystencilssfg/ir/entities.py b/src/pystencilssfg/ir/entities.py
index 0edde22..850bbde 100644
--- a/src/pystencilssfg/ir/entities.py
+++ b/src/pystencilssfg/ir/entities.py
@@ -147,6 +147,7 @@ class SfgKernelHandle(SfgCodeEntity):
         namespace: SfgKernelNamespace,
         kernel: Kernel,
         inline: bool = False,
+        externC: bool = False,
     ):
         super().__init__(name, namespace)
 
@@ -154,6 +155,7 @@ class SfgKernelHandle(SfgCodeEntity):
         self._parameters = [SfgKernelParamVar(p) for p in kernel.parameters]
 
         self._inline: bool = inline
+        self._externC: bool = externC
 
         self._scalar_params: set[SfgVar] = set()
         self._fields: set[Field] = set()
@@ -188,6 +190,10 @@ class SfgKernelHandle(SfgCodeEntity):
     def inline(self) -> bool:
         return self._inline
 
+    @property
+    def externC(self) -> bool:
+        return self._externC
+
 
 class SfgKernelNamespace(SfgNamespace):
     """A namespace grouping together a number of kernels."""
diff --git a/src/pystencilssfg/lang/expressions.py b/src/pystencilssfg/lang/expressions.py
index 135a54e..abe863a 100644
--- a/src/pystencilssfg/lang/expressions.py
+++ b/src/pystencilssfg/lang/expressions.py
@@ -483,7 +483,7 @@ def includes(obj: ExprLike | PsType) -> set[HeaderFile]:
         case PsType():
             headers = set(HeaderFile.parse(h) for h in obj.required_headers)
             if isinstance(obj, PsIntegerType):
-                headers.add(HeaderFile.parse("<cstdint>"))
+                headers.add(HeaderFile.parse("<cstdint>"))  # TODO: switch for stdint.h
             return headers
 
         case SfgVar(_, dtype):
-- 
GitLab


From e1a88a201076bb1999f4ec74b505335225aaa561 Mon Sep 17 00:00:00 2001
From: zy69guqi <richard.angersbach@fau.de>
Date: Mon, 10 Mar 2025 16:02:33 +0100
Subject: [PATCH 2/9] Remove extern C from kernelhandles, add EXTERNC macro,
 introduce c_interfacing config option

---
 src/pystencilssfg/composer/basic_composer.py | 17 ++++++++++-------
 src/pystencilssfg/config.py                  |  6 ++++++
 src/pystencilssfg/context.py                 |  7 +++++++
 src/pystencilssfg/emission/file_printer.py   |  8 ++++----
 src/pystencilssfg/generator.py               | 12 ++++++++++++
 src/pystencilssfg/ir/entities.py             |  9 +++------
 6 files changed, 42 insertions(+), 17 deletions(-)

diff --git a/src/pystencilssfg/composer/basic_composer.py b/src/pystencilssfg/composer/basic_composer.py
index 9e58d78..9a870d9 100644
--- a/src/pystencilssfg/composer/basic_composer.py
+++ b/src/pystencilssfg/composer/basic_composer.py
@@ -91,7 +91,6 @@ class KernelsAdder:
         self._cursor = cursor
         self._kernel_namespace = knamespace
         self._inline: bool = False
-        self._externC: bool = False
         self._loc: SfgNamespaceBlock | None = None
 
     def inline(self) -> KernelsAdder:
@@ -99,11 +98,6 @@ class KernelsAdder:
         self._inline = True
         return self
 
-    def externC(self) -> KernelsAdder:
-        """Generate kernel definitions ``extern "C"`` in the header file."""
-        self._externC = True
-        return self
-
     def add(self, kernel: Kernel, name: str | None = None):
         """Adds an existing pystencils AST to this namespace.
         If a name is specified, the AST's function name is changed."""
@@ -122,7 +116,7 @@ class KernelsAdder:
             kernel.name = kernel_name
 
         khandle = SfgKernelHandle(
-            kernel_name, self._kernel_namespace, kernel, inline=self._inline, externC=self._externC
+            kernel_name, self._kernel_namespace, kernel, inline=self._inline
         )
         self._kernel_namespace.add_kernel(khandle)
 
@@ -389,6 +383,8 @@ class SfgBasicComposer(SfgIComposer):
 
         if self._ctx.impl_file is None:
             seq.inline()
+        if self._ctx.c_interfacing:
+            seq.externC()
 
         return seq
 
@@ -678,6 +674,7 @@ class SfgFunctionSequencerBase:
 
         #   Qualifiers
         self._inline: bool = False
+        self._externC: bool = False
         self._constexpr: bool = False
 
         #   Attributes
@@ -704,6 +701,11 @@ class SfgFunctionSequencerBase:
         self._inline = True
         return self
 
+    def externC(self):
+        """Mark this function as ``extern "C"``."""
+        self._externC = True
+        return self
+
     def constexpr(self):
         """Mark this function as ``constexpr``."""
         self._constexpr = True
@@ -727,6 +729,7 @@ class SfgFunctionSequencer(SfgFunctionSequencerBase):
             tree,
             return_type=self._return_type,
             inline=self._inline,
+            externC=self._externC,
             constexpr=self._constexpr,
             attributes=self._attributes,
             required_params=self._params,
diff --git a/src/pystencilssfg/config.py b/src/pystencilssfg/config.py
index 34d2f27..44aaf51 100644
--- a/src/pystencilssfg/config.py
+++ b/src/pystencilssfg/config.py
@@ -123,6 +123,12 @@ class SfgConfig(ConfigBase):
     This will cause all definitions to be generated ``inline``.
     """
 
+    c_interfacing: BasicOption[bool] = BasicOption(False)
+    """If set to `True`, generates header files compatible for interfacing with C.
+    
+    This will cause all definitions to be generated ``extern "C"``.
+    """
+
     outer_namespace: BasicOption[str | _GlobalNamespace] = BasicOption(GLOBAL_NAMESPACE)
     """The outermost namespace in the generated file. May be a valid C++ nested namespace qualifier
     (like ``a::b::c``) or `GLOBAL_NAMESPACE` if no outer namespace should be generated.
diff --git a/src/pystencilssfg/context.py b/src/pystencilssfg/context.py
index 1622a1e..6d76b7e 100644
--- a/src/pystencilssfg/context.py
+++ b/src/pystencilssfg/context.py
@@ -21,6 +21,7 @@ class SfgContext:
         self,
         header_file: SfgSourceFile,
         impl_file: SfgSourceFile | None,
+        c_interfacing: bool,
         namespace: str | None = None,
         codestyle: CodeStyle | None = None,
         argv: Sequence[str] | None = None,
@@ -29,6 +30,8 @@ class SfgContext:
         self._argv = argv
         self._project_info = project_info
 
+        self._c_interfacing = c_interfacing
+
         self._outer_namespace = namespace
         self._inner_namespace: str | None = None
 
@@ -77,6 +80,10 @@ class SfgContext:
     def header_file(self) -> SfgSourceFile:
         return self._header_file
 
+    @property
+    def c_interfacing(self) -> bool:
+        return self._c_interfacing
+
     @property
     def impl_file(self) -> SfgSourceFile | None:
         return self._impl_file
diff --git a/src/pystencilssfg/emission/file_printer.py b/src/pystencilssfg/emission/file_printer.py
index d6b9296..74ec3a2 100644
--- a/src/pystencilssfg/emission/file_printer.py
+++ b/src/pystencilssfg/emission/file_printer.py
@@ -84,12 +84,9 @@ class SfgFilePrinter:
     ) -> str:
         match declared_entity:
             case SfgKernelHandle(kernel):
-                func_prefix = "extern C" if declared_entity.externC else ""
-                func_prefix += " inline" if declared_entity.inline else ""
-
                 kernel_printer = CAstPrinter(
                     indent_width=self._indent_width,
-                    func_prefix=func_prefix,
+                    func_prefix="inline" if declared_entity.inline else "",
                 )
                 return kernel_printer.print_signature(kernel) + ";"
 
@@ -195,6 +192,9 @@ class SfgFilePrinter:
         if func.inline and not inclass:
             code += "inline "
 
+        if isinstance(func, SfgFunction) and func.externC and not inclass:
+            code += "EXTERNC "
+
         if isinstance(func, SfgMethod) and inclass:
             if func.static:
                 code += "static "
diff --git a/src/pystencilssfg/generator.py b/src/pystencilssfg/generator.py
index c314d67..5110fcf 100644
--- a/src/pystencilssfg/generator.py
+++ b/src/pystencilssfg/generator.py
@@ -78,6 +78,7 @@ class SourceFileGenerator:
             config.override(sfg_config)
 
         self._header_only: bool = config.get_option("header_only")
+        self._c_interfacing: bool = config.get_option("c_interfacing")
         self._output_dir: Path = config.get_option("output_directory")
 
         output_files = config._get_output_files(basename)
@@ -102,6 +103,16 @@ class SourceFileGenerator:
         #   TODO: Find a way to not hard-code the restrict qualifier in pystencils
         self._header_file.elements.append("#define RESTRICT __restrict__")
 
+        #   TODO: Find a way to not hard-code the 'extern" C"' qualifier in pystencils
+        self._header_file.elements.append(
+            """#ifdef __cplusplus\n
+                #define EXTERNC extern \"C\"\n
+                #else\n
+                #define EXTERNC \n
+                #endif\n
+                """
+        )
+
         outer_namespace: str | _GlobalNamespace = config.get_option("outer_namespace")
 
         namespace: str | None
@@ -113,6 +124,7 @@ class SourceFileGenerator:
         self._context = SfgContext(
             self._header_file,
             self._impl_file,
+            self._c_interfacing,
             namespace,
             config.codestyle,
             argv=script_args,
diff --git a/src/pystencilssfg/ir/entities.py b/src/pystencilssfg/ir/entities.py
index 850bbde..e20b2b7 100644
--- a/src/pystencilssfg/ir/entities.py
+++ b/src/pystencilssfg/ir/entities.py
@@ -147,7 +147,6 @@ class SfgKernelHandle(SfgCodeEntity):
         namespace: SfgKernelNamespace,
         kernel: Kernel,
         inline: bool = False,
-        externC: bool = False,
     ):
         super().__init__(name, namespace)
 
@@ -155,7 +154,6 @@ class SfgKernelHandle(SfgCodeEntity):
         self._parameters = [SfgKernelParamVar(p) for p in kernel.parameters]
 
         self._inline: bool = inline
-        self._externC: bool = externC
 
         self._scalar_params: set[SfgVar] = set()
         self._fields: set[Field] = set()
@@ -190,10 +188,6 @@ class SfgKernelHandle(SfgCodeEntity):
     def inline(self) -> bool:
         return self._inline
 
-    @property
-    def externC(self) -> bool:
-        return self._externC
-
 
 class SfgKernelNamespace(SfgNamespace):
     """A namespace grouping together a number of kernels."""
@@ -228,6 +222,7 @@ class CommonFunctionProperties:
     parameters: tuple[SfgVar, ...]
     return_type: PsType
     inline: bool
+    externC: bool
     constexpr: bool
     attributes: Sequence[str]
 
@@ -264,6 +259,7 @@ class SfgFunction(SfgCodeEntity, CommonFunctionProperties):
         tree: SfgCallTreeNode,
         return_type: PsType = void,
         inline: bool = False,
+        externC: bool = False,
         constexpr: bool = False,
         attributes: Sequence[str] = (),
         required_params: Sequence[SfgVar] | None = None,
@@ -278,6 +274,7 @@ class SfgFunction(SfgCodeEntity, CommonFunctionProperties):
             parameters,
             return_type,
             inline,
+            externC,
             constexpr,
             attributes,
         )
-- 
GitLab


From 86c4669913988222ef2a167b0afc386a742e50cc Mon Sep 17 00:00:00 2001
From: zy69guqi <richard.angersbach@fau.de>
Date: Mon, 10 Mar 2025 16:11:54 +0100
Subject: [PATCH 3/9] Remove option to mark individual functions as extern C

---
 src/pystencilssfg/composer/basic_composer.py | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/src/pystencilssfg/composer/basic_composer.py b/src/pystencilssfg/composer/basic_composer.py
index 9a870d9..75c023b 100644
--- a/src/pystencilssfg/composer/basic_composer.py
+++ b/src/pystencilssfg/composer/basic_composer.py
@@ -384,7 +384,7 @@ class SfgBasicComposer(SfgIComposer):
         if self._ctx.impl_file is None:
             seq.inline()
         if self._ctx.c_interfacing:
-            seq.externC()
+            seq._externC = True
 
         return seq
 
@@ -701,11 +701,6 @@ class SfgFunctionSequencerBase:
         self._inline = True
         return self
 
-    def externC(self):
-        """Mark this function as ``extern "C"``."""
-        self._externC = True
-        return self
-
     def constexpr(self):
         """Mark this function as ``constexpr``."""
         self._constexpr = True
-- 
GitLab


From fddcf9dc2596ed1decd3b7e4fe2dc1e1dd444311 Mon Sep 17 00:00:00 2001
From: zy69guqi <richard.angersbach@fau.de>
Date: Wed, 12 Mar 2025 14:52:21 +0100
Subject: [PATCH 4/9] Implement hybrid include mechanism for C interfacing

---
 src/pystencilssfg/composer/basic_composer.py |  5 +++++
 src/pystencilssfg/emission/file_printer.py   | 21 +++++++++++++++++++
 src/pystencilssfg/generator.py               | 22 +++++++++++++++++---
 src/pystencilssfg/ir/syntax.py               | 11 ++++++++++
 src/pystencilssfg/lang/expressions.py        |  2 +-
 5 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/src/pystencilssfg/composer/basic_composer.py b/src/pystencilssfg/composer/basic_composer.py
index 75c023b..d598970 100644
--- a/src/pystencilssfg/composer/basic_composer.py
+++ b/src/pystencilssfg/composer/basic_composer.py
@@ -15,6 +15,7 @@ from pystencils import (
 )
 from pystencils.codegen import Kernel
 from pystencils.types import create_type, UserTypeSpec, PsType
+from pystencilssfg.ir import SfgSourceFileType
 
 from ..context import SfgContext, SfgCursor
 from .custom import CustomGenerator
@@ -716,6 +717,10 @@ class SfgFunctionSequencer(SfgFunctionSequencerBase):
     """Sequencer for constructing functions."""
 
     def __call__(self, *args: SequencerArg) -> None:
+        # check if header is in HYBRID mode for c_interfacing enabled
+        if self._cursor.context.c_interfacing:
+            assert isinstance(self._cursor.context.header_file.file_type, SfgSourceFileType.HYBRID_HEADER)
+
         """Populate the function body"""
         tree = make_sequence(*args)
         func = SfgFunction(
diff --git a/src/pystencilssfg/emission/file_printer.py b/src/pystencilssfg/emission/file_printer.py
index 74ec3a2..b2f6692 100644
--- a/src/pystencilssfg/emission/file_printer.py
+++ b/src/pystencilssfg/emission/file_printer.py
@@ -42,7 +42,28 @@ class SfgFilePrinter:
         if file.file_type == SfgSourceFileType.HEADER:
             code += "#pragma once\n\n"
 
+        includes = ""
         for header in file.includes:
+            incl = str(header) if header.system_header else f'"{str(header)}"'
+            includes += f"#include {incl}\n"
+
+        if file.file_type == SfgSourceFileType.HYBRID_HEADER:
+            hybrid_includes = ""
+            for header in file.includes:
+                incl = str(header) if header.system_header else f'"{str(header)}"'
+                hybrid_includes += f"#include {incl}\n"
+
+            # include different headers and wrap around guard distinguishing C++/C compilations
+            code += f"""
+            #ifdef __cplusplus\n
+            {includes}
+            #else\n
+            {hybrid_includes}
+            #endif\n"""
+        else:
+            code += includes
+
+        for header in file.hybrid_includes:
             incl = str(header) if header.system_header else f'"{str(header)}"'
             code += f"#include {incl}\n"
 
diff --git a/src/pystencilssfg/generator.py b/src/pystencilssfg/generator.py
index 5110fcf..1eb30ce 100644
--- a/src/pystencilssfg/generator.py
+++ b/src/pystencilssfg/generator.py
@@ -85,9 +85,9 @@ class SourceFileGenerator:
 
         from .ir import SfgSourceFile, SfgSourceFileType
 
-        self._header_file = SfgSourceFile(
-            output_files[0].name, SfgSourceFileType.HEADER
-        )
+        header_type = SfgSourceFileType.HYBRID_HEADER \
+            if self._c_interfacing else SfgSourceFileType.HEADER
+        self._header_file = SfgSourceFile(output_files[0].name, header_type)
         self._impl_file: SfgSourceFile | None
 
         if self._header_only:
@@ -164,6 +164,22 @@ class SourceFileGenerator:
         )
         self._header_file.includes.sort(key=self._include_sort_key)
 
+        if self._c_interfacing:
+            # from: https://en.cppreference.com/w/cpp/header
+            c_compatibility_headers = [
+                "<cassert", "<cctype>", "<cerrno>", "<cfenv>", "<cfloat>",
+                "<cinttypes>", "<climits>", "<clocale>", "<cmath>",
+                "<csetjmp>", "<csignal>", "<cstdarg>", "<cstddef>", "<cstdint>",
+                "<cstdio>", "<cstdlib>", "<cstring>", "<ctime>", "<cuchar>",
+                "<cwchar>", "<cwctype>"
+            ]
+
+            for inc in self._header_file.includes:
+                if inc.system_header and inc.__str__() in c_compatibility_headers:
+                    c_header = inc.__str__().replace("<c", "<")
+                    self._header_file.hybrid_includes += HeaderFile(
+                        c_header, system_header=True)
+
         if self._impl_file is not None:
             impl_includes = collect_includes(self._impl_file)
             #   If some header is already included by the generated header file, do not duplicate that inclusion
diff --git a/src/pystencilssfg/ir/syntax.py b/src/pystencilssfg/ir/syntax.py
index cdbd4c2..71d7fd0 100644
--- a/src/pystencilssfg/ir/syntax.py
+++ b/src/pystencilssfg/ir/syntax.py
@@ -181,6 +181,7 @@ SfgNamespaceElement = (
 
 class SfgSourceFileType(Enum):
     HEADER = auto()
+    HYBRID_HEADER = auto()
     TRANSLATION_UNIT = auto()
 
 
@@ -200,6 +201,7 @@ class SfgSourceFile:
         self._file_type: SfgSourceFileType = file_type
         self._prelude: str | None = prelude
         self._includes: list[HeaderFile] = []
+        self._hybrid_includes: list[HeaderFile] = []
         self._elements: list[SfgNamespaceElement] = []
 
     @property
@@ -230,6 +232,15 @@ class SfgSourceFile:
     def includes(self, incl: Iterable[HeaderFile]):
         self._includes = list(incl)
 
+    @property
+    def hybrid_includes(self) -> list[HeaderFile]:
+        """Sequence of header files to be included at the top of this file"""
+        return self._hybrid_includes
+
+    @hybrid_includes.setter
+    def hybrid_includes(self, incl: Iterable[HeaderFile]):
+        self._hybrid_includes = list(incl)
+
     @property
     def elements(self) -> list[SfgNamespaceElement]:
         """Sequence of source elements comprising the body of this file"""
diff --git a/src/pystencilssfg/lang/expressions.py b/src/pystencilssfg/lang/expressions.py
index abe863a..135a54e 100644
--- a/src/pystencilssfg/lang/expressions.py
+++ b/src/pystencilssfg/lang/expressions.py
@@ -483,7 +483,7 @@ def includes(obj: ExprLike | PsType) -> set[HeaderFile]:
         case PsType():
             headers = set(HeaderFile.parse(h) for h in obj.required_headers)
             if isinstance(obj, PsIntegerType):
-                headers.add(HeaderFile.parse("<cstdint>"))  # TODO: switch for stdint.h
+                headers.add(HeaderFile.parse("<cstdint>"))
             return headers
 
         case SfgVar(_, dtype):
-- 
GitLab


From 41d1276c057cc3f747c8cdcab2f6ac114b2abcca Mon Sep 17 00:00:00 2001
From: zy69guqi <richard.angersbach@fau.de>
Date: Wed, 12 Mar 2025 16:10:42 +0100
Subject: [PATCH 5/9] Fix lint

---
 src/pystencilssfg/config.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pystencilssfg/config.py b/src/pystencilssfg/config.py
index 44aaf51..7ced262 100644
--- a/src/pystencilssfg/config.py
+++ b/src/pystencilssfg/config.py
@@ -125,7 +125,7 @@ class SfgConfig(ConfigBase):
 
     c_interfacing: BasicOption[bool] = BasicOption(False)
     """If set to `True`, generates header files compatible for interfacing with C.
-    
+
     This will cause all definitions to be generated ``extern "C"``.
     """
 
-- 
GitLab


From 34012b361ae2800ce55e56ed3065f34e4deacf63 Mon Sep 17 00:00:00 2001
From: zy69guqi <richard.angersbach@fau.de>
Date: Wed, 12 Mar 2025 16:12:31 +0100
Subject: [PATCH 6/9] Fix typcheck

---
 src/pystencilssfg/composer/basic_composer.py | 2 +-
 src/pystencilssfg/generator.py               | 4 ++--
 src/pystencilssfg/ir/entities.py             | 3 +++
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/pystencilssfg/composer/basic_composer.py b/src/pystencilssfg/composer/basic_composer.py
index d598970..cdf425d 100644
--- a/src/pystencilssfg/composer/basic_composer.py
+++ b/src/pystencilssfg/composer/basic_composer.py
@@ -719,7 +719,7 @@ class SfgFunctionSequencer(SfgFunctionSequencerBase):
     def __call__(self, *args: SequencerArg) -> None:
         # check if header is in HYBRID mode for c_interfacing enabled
         if self._cursor.context.c_interfacing:
-            assert isinstance(self._cursor.context.header_file.file_type, SfgSourceFileType.HYBRID_HEADER)
+            assert self._cursor.context.header_file.file_type == SfgSourceFileType.HYBRID_HEADER
 
         """Populate the function body"""
         tree = make_sequence(*args)
diff --git a/src/pystencilssfg/generator.py b/src/pystencilssfg/generator.py
index 1eb30ce..b3916f4 100644
--- a/src/pystencilssfg/generator.py
+++ b/src/pystencilssfg/generator.py
@@ -177,8 +177,8 @@ class SourceFileGenerator:
             for inc in self._header_file.includes:
                 if inc.system_header and inc.__str__() in c_compatibility_headers:
                     c_header = inc.__str__().replace("<c", "<")
-                    self._header_file.hybrid_includes += HeaderFile(
-                        c_header, system_header=True)
+                    self._header_file.hybrid_includes += [HeaderFile(
+                        c_header, system_header=True)]
 
         if self._impl_file is not None:
             impl_includes = collect_includes(self._impl_file)
diff --git a/src/pystencilssfg/ir/entities.py b/src/pystencilssfg/ir/entities.py
index e20b2b7..41f5daa 100644
--- a/src/pystencilssfg/ir/entities.py
+++ b/src/pystencilssfg/ir/entities.py
@@ -383,6 +383,8 @@ class SfgMethod(SfgClassMember, CommonFunctionProperties):
         self._virtual = virtual
         self._override = override
 
+        externC = False
+
         parameters = self.collect_params(tree, required_params)
 
         CommonFunctionProperties.__init__(
@@ -391,6 +393,7 @@ class SfgMethod(SfgClassMember, CommonFunctionProperties):
             parameters,
             return_type,
             inline,
+            externC,
             constexpr,
             attributes,
         )
-- 
GitLab


From 0c03bf061766c2f3faf5f990d77763199e5d6837 Mon Sep 17 00:00:00 2001
From: zy69guqi <richard.angersbach@fau.de>
Date: Wed, 12 Mar 2025 16:51:55 +0100
Subject: [PATCH 7/9] Fix tests

---
 src/pystencilssfg/context.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pystencilssfg/context.py b/src/pystencilssfg/context.py
index 6d76b7e..4815650 100644
--- a/src/pystencilssfg/context.py
+++ b/src/pystencilssfg/context.py
@@ -21,7 +21,7 @@ class SfgContext:
         self,
         header_file: SfgSourceFile,
         impl_file: SfgSourceFile | None,
-        c_interfacing: bool,
+        c_interfacing: bool = False,
         namespace: str | None = None,
         codestyle: CodeStyle | None = None,
         argv: Sequence[str] | None = None,
-- 
GitLab


From b5645502823c72194418002e43f9eff9f868f773 Mon Sep 17 00:00:00 2001
From: zy69guqi <richard.angersbach@fau.de>
Date: Wed, 12 Mar 2025 17:02:04 +0100
Subject: [PATCH 8/9] Fix emission of hybrid header includes

---
 src/pystencilssfg/emission/file_printer.py | 6 +-----
 src/pystencilssfg/generator.py             | 5 ++---
 2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/src/pystencilssfg/emission/file_printer.py b/src/pystencilssfg/emission/file_printer.py
index b2f6692..ed1c073 100644
--- a/src/pystencilssfg/emission/file_printer.py
+++ b/src/pystencilssfg/emission/file_printer.py
@@ -49,7 +49,7 @@ class SfgFilePrinter:
 
         if file.file_type == SfgSourceFileType.HYBRID_HEADER:
             hybrid_includes = ""
-            for header in file.includes:
+            for header in file.hybrid_includes:
                 incl = str(header) if header.system_header else f'"{str(header)}"'
                 hybrid_includes += f"#include {incl}\n"
 
@@ -63,10 +63,6 @@ class SfgFilePrinter:
         else:
             code += includes
 
-        for header in file.hybrid_includes:
-            incl = str(header) if header.system_header else f'"{str(header)}"'
-            code += f"#include {incl}\n"
-
         if file.includes:
             code += "\n"
 
diff --git a/src/pystencilssfg/generator.py b/src/pystencilssfg/generator.py
index b3916f4..8f720b4 100644
--- a/src/pystencilssfg/generator.py
+++ b/src/pystencilssfg/generator.py
@@ -176,9 +176,8 @@ class SourceFileGenerator:
 
             for inc in self._header_file.includes:
                 if inc.system_header and inc.__str__() in c_compatibility_headers:
-                    c_header = inc.__str__().replace("<c", "<")
-                    self._header_file.hybrid_includes += [HeaderFile(
-                        c_header, system_header=True)]
+                    c_header = inc.__str__().replace("<c", "<").replace(">", ".h>")
+                    self._header_file.hybrid_includes += [HeaderFile.parse(c_header)]
 
         if self._impl_file is not None:
             impl_includes = collect_includes(self._impl_file)
-- 
GitLab


From 3912add0d1ff9b99704dc3e4e55288c30dc92407 Mon Sep 17 00:00:00 2001
From: zy69guqi <richard.angersbach@fau.de>
Date: Wed, 12 Mar 2025 17:03:36 +0100
Subject: [PATCH 9/9] Revert unnecessary change

---
 src/pystencilssfg/emission/file_printer.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pystencilssfg/emission/file_printer.py b/src/pystencilssfg/emission/file_printer.py
index ed1c073..ebd89cc 100644
--- a/src/pystencilssfg/emission/file_printer.py
+++ b/src/pystencilssfg/emission/file_printer.py
@@ -103,7 +103,7 @@ class SfgFilePrinter:
             case SfgKernelHandle(kernel):
                 kernel_printer = CAstPrinter(
                     indent_width=self._indent_width,
-                    func_prefix="inline" if declared_entity.inline else "",
+                    func_prefix="inline" if declared_entity.inline else None,
                 )
                 return kernel_printer.print_signature(kernel) + ";"
 
-- 
GitLab