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] 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