diff --git a/docs/usage/building.md b/docs/usage/building.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..01d129500861dfe25b9bb18152d2ac73842a004e 100644 --- a/docs/usage/building.md +++ b/docs/usage/building.md @@ -0,0 +1,30 @@ + +## Namespaces + +Conceptually, there exist two different kinds of namespaces: *kernel namespaces* for the generated kernels, +and a single *code namespace* for all the generated code. +Both get mapped to standard C++ namespaces, in the end, but they fulfill different purposes in the code generator. + +*Kernel namespaces* are used for grouping generated kernels together, e.g. to avoid name collisions. +If, for example, a code generation script combines kernels and functions produced by different components, each +component may create its own kernel namespace to isolate its kernels. + +The *code namespace*, in contrast, envelops all the generated code. Its fully qualified name is built from two parts: + + - The *outer namespace* is defined in the [generator configuration][pystencilssfg.SfgConfiguration], typically by + the global project configuration; + - The *inner namespace* is defined by the code generation script, e.g. via [`SfgComposer.namespace`][pystencilssfg.SfgComposer.namespace]. + +These namespaces will finally occur in the generated implementation file as: + +```C++ +namespace outer_namespace::inner_namespace { + +namespace kernels { + /* kernel definitions */ +} // namespace kernels + +/* function definitions */ + +} // namespace outer_namespace::inner_namespace +``` diff --git a/integration/CMakeDemo/codegen_config.py b/integration/CMakeDemo/codegen_config.py index 67f309315f3b6bb95146d4be4a0e40dbd6b57d0e..40a70f91c70e0d9cec2a5f748bd002a99f875395 100644 --- a/integration/CMakeDemo/codegen_config.py +++ b/integration/CMakeDemo/codegen_config.py @@ -11,6 +11,6 @@ def sfg_config(): return SfgConfiguration( header_extension='hpp', source_extension='cpp', - base_namespace='cmake_demo', + outer_namespace='cmake_demo', project_info=project_info ) diff --git a/integration/MakeDemo/kernels.py b/integration/MakeDemo/kernels.py index dc790d2da4f4ae6df74dfb6fe54db7563d7d8080..add435f06bc5ecd454c1472a5f2632efe242603f 100644 --- a/integration/MakeDemo/kernels.py +++ b/integration/MakeDemo/kernels.py @@ -1,11 +1,19 @@ +# type: ignore + import sympy as sp from pystencils import fields, kernel -from pystencilssfg import SourceFileGenerator +from pystencilssfg import SourceFileGenerator, SfgConfiguration from pystencilssfg.source_concepts.cpp import mdspan_ref -with SourceFileGenerator() as sfg: +sfg_config = SfgConfiguration( + outer_namespace="make_demo" +) + +with SourceFileGenerator(sfg_config) as sfg: + sfg.namespace("jacobi") + u_src, u_dst, f = fields("u_src, u_dst, f(1) : double[2D]", layout="fzyx") h = sp.Symbol("h") diff --git a/integration/MakeDemo/main.cpp b/integration/MakeDemo/main.cpp index 21c801572b71267b86a08a56782cd40ddeb4ae81..033261f0f090d0e6ed1bfeec2d999e29a7ac8e90 100644 --- a/integration/MakeDemo/main.cpp +++ b/integration/MakeDemo/main.cpp @@ -39,7 +39,7 @@ int main(int argc, char ** argv){ } for(uint32_t i = 0; i < n_iters; ++i){ - jacobi_smooth(f, h, dst, src); + make_demo::jacobi::jacobi_smooth(f, h, dst, src); std::swap(src, dst); } diff --git a/src/pystencilssfg/composer.py b/src/pystencilssfg/composer.py index f44979803ea2b211e9df7dd399359d07142a7940..8d2c9766b697517dfe1dae1921b53ecf0efd0007 100644 --- a/src/pystencilssfg/composer.py +++ b/src/pystencilssfg/composer.py @@ -24,6 +24,10 @@ class SfgComposer: @property def context(self): return self._ctx + + def namespace(self, namespace: str): + """Set the inner code namespace. Throws an exception if a namespace was already set.""" + self._ctx.set_namespace(namespace) @property def kernels(self) -> SfgKernelNamespace: diff --git a/src/pystencilssfg/configuration.py b/src/pystencilssfg/configuration.py index b93478aef8106dbc1adf07936a8118d780157303..355be5e4c4d7bdd1a06724de253de9732971e30a 100644 --- a/src/pystencilssfg/configuration.py +++ b/src/pystencilssfg/configuration.py @@ -55,7 +55,7 @@ class SfgConfiguration: header_only: bool | None = None """If set to `True`, generate only a header file without accompaning source file.""" - base_namespace: str | None = None + outer_namespace: str | None = None """The outermost namespace in the generated file. May be a valid C++ nested namespace qualifier (like `a::b::c`) or `None` if no outer namespace should be generated.""" @@ -88,7 +88,7 @@ DEFAULT_CONFIG = SfgConfiguration( header_extension='h', source_extension='cpp', header_only=False, - base_namespace=None, + outer_namespace=None, codestyle=SfgCodeStyle(), output_directory="" ) diff --git a/src/pystencilssfg/context.py b/src/pystencilssfg/context.py index 0c083831b4095e4552176b11a3cfa676da719e98..548d9e5727b642a6dfb2e75b3947420b8156b766 100644 --- a/src/pystencilssfg/context.py +++ b/src/pystencilssfg/context.py @@ -15,7 +15,9 @@ class SfgContext: self._code_namespace = None # Source Components + self._prelude: list[str] = [] self._includes: set[SfgHeaderInclude] = set() + self._definitions: list[str] = [] self._kernel_namespaces = {self._default_kernel_namespace.name: self._default_kernel_namespace} self._functions: dict[str, SfgFunction] = dict() @@ -31,8 +33,8 @@ class SfgContext: return self._argv @property - def root_namespace(self) -> str | None: - return self._config.base_namespace + def outer_namespace(self) -> str | None: + return self._config.outer_namespace @property def inner_namespace(self) -> str | None: @@ -40,7 +42,7 @@ class SfgContext: @property def fully_qualified_namespace(self) -> str | None: - match (self.root_namespace, self.inner_namespace): + match (self.outer_namespace, self.inner_namespace): case None, None: return None case outer, None: return outer case None, inner: return inner @@ -53,15 +55,37 @@ class SfgContext: return self._config.codestyle # ---------------------------------------------------------------------------------------------- - # Kernel Namespaces + # Prelude, Includes, Definitions, Namespace # ---------------------------------------------------------------------------------------------- + def prelude_comments(self) -> Generator[str, None, None]: + """The prelude is a comment block printed at the top of both generated files.""" + yield from self._prelude + + def append_to_prelude(self, code_str: str): + self._prelude.append(code_str) + def includes(self) -> Generator[SfgHeaderInclude, None, None]: + """Includes of headers. Public includes are added to the header file, private includes + are added to the implementation file.""" yield from self._includes def add_include(self, include: SfgHeaderInclude): self._includes.add(include) + def definitions(self) -> Generator[str, None, None]: + """Definitions are code lines printed at the top of the header file, after the includes.""" + yield from self._definitions + + def add_definition(self, definition: str): + self._definitions.append(definition) + + def set_namespace(self, namespace: str): + if self._code_namespace is not None: + raise SfgException("The code namespace was already set.") + + self._code_namespace = namespace + # ---------------------------------------------------------------------------------------------- # Kernel Namespaces # ----------------------------------------------------------------------------------------------