-
Notifications
You must be signed in to change notification settings - Fork 33
[Draft] better handling of shamrock python submodules with pybind11 (instense c++ sorcery) #1806
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 7 commits
d850654
5ecc638
ddd4868
4373db0
b7f36f5
483f8ca
2e3b7d3
8305074
e5968df
8ba7eea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| // -------------------------------------------------------// | ||
| // | ||
| // SHAMROCK code for hydrodynamics | ||
| // Copyright (c) 2021-2026 Timothée David--Cléris <tim.shamrock@proton.me> | ||
| // SPDX-License-Identifier: CeCILL Free Software License Agreement v2.1 | ||
| // Shamrock is licensed under the CeCILL 2.1 License, see LICENSE for more information | ||
| // | ||
| // -------------------------------------------------------// | ||
|
|
||
| #pragma once | ||
|
|
||
| /** | ||
| * @file print.hpp | ||
| * @author Timothée David--Cléris (tim.shamrock@proton.me) | ||
| * @brief Call a lambda at static or local scope construction time | ||
| */ | ||
|
|
||
| namespace shambase { | ||
|
|
||
| /** | ||
| * @brief Execute a lambda when a `call_lambda` object is constructed. | ||
| * | ||
| * Useful for static initialization side effects (registering data, | ||
| * registering modules, etc.) without needing a named function. | ||
| */ | ||
| struct call_lambda { | ||
|
|
||
| /// Call the lambda on construction | ||
| template<class Func> | ||
| inline explicit call_lambda(Func &&f) { | ||
| f(); | ||
| } | ||
| }; | ||
|
|
||
| } // namespace shambase | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -21,22 +21,154 @@ | |||||||||||||||||||||||||||
| * we can then wrap them conveniently in a single macro call. | ||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| #include "shambase/call_lambda.hpp" | ||||||||||||||||||||||||||||
| #include "shambase/exception.hpp" | ||||||||||||||||||||||||||||
| #include "shambase/unique_name_macro.hpp" | ||||||||||||||||||||||||||||
| #include <pybind11/pybind11.h> | ||||||||||||||||||||||||||||
| #include <map> | ||||||||||||||||||||||||||||
| #include <optional> | ||||||||||||||||||||||||||||
|
Comment on lines
27
to
+29
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The header is missing several standard library includes required by the registry_t template and the build_all_modules function. Specifically, it needs , <string_view>, , , , , and to be self-contained and avoid compilation errors in different translation units.
Suggested change
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| /// alias to pybind11 namespace | ||||||||||||||||||||||||||||
| namespace py = pybind11; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
| // Submodule registry | ||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| namespace shambindings::submodules { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
| // Generic registry | ||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| template<typename T> | ||||||||||||||||||||||||||||
| struct registry_t { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // note here that std::less<> is for transparent lookup with string_view | ||||||||||||||||||||||||||||
| using map_t = std::map<std::string, T, std::less<>>; | ||||||||||||||||||||||||||||
| std::unique_ptr<map_t> storage = {}; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| using getter_fn = std::function<T &()>; | ||||||||||||||||||||||||||||
| std::optional<std::map<std::string, getter_fn, std::less<>>> overrides; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| map_t &data() { | ||||||||||||||||||||||||||||
| if (!storage) { | ||||||||||||||||||||||||||||
| storage = std::make_unique<map_t>(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| return *storage; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| void reset() { | ||||||||||||||||||||||||||||
| storage.reset(); | ||||||||||||||||||||||||||||
| overrides = std::nullopt; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // enable override system lazily | ||||||||||||||||||||||||||||
| auto &override_map() { | ||||||||||||||||||||||||||||
| if (!overrides) { | ||||||||||||||||||||||||||||
| overrides.emplace(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| return *overrides; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| void set_override(std::string_view key, getter_fn fn) { | ||||||||||||||||||||||||||||
| override_map().insert_or_assign(std::string(key), std::move(fn)); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| void clear_overrides() { overrides = std::nullopt; } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // does not include overrides | ||||||||||||||||||||||||||||
| std::vector<std::string> keys() const { | ||||||||||||||||||||||||||||
| if (!storage) { | ||||||||||||||||||||||||||||
| return {}; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| std::vector<std::string> out; | ||||||||||||||||||||||||||||
| out.reserve(storage->size()); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| for (auto const &kv : *storage) { | ||||||||||||||||||||||||||||
| out.push_back(kv.first); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return out; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| T &get(std::string_view key) { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // global override first | ||||||||||||||||||||||||||||
| if (overrides) { | ||||||||||||||||||||||||||||
| if (auto it = overrides->find(key); it != overrides->end()) { | ||||||||||||||||||||||||||||
| return it->second(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| auto &map = data(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (auto it = map.find(key); it != map.end()) { | ||||||||||||||||||||||||||||
| return it->second; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| throw shambase::make_except_with_loc<std::out_of_range>( | ||||||||||||||||||||||||||||
| "registry entry not found: " + std::string(key)); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| void insert(std::string_view key, T &&value) { | ||||||||||||||||||||||||||||
| auto [it, inserted] = data().emplace(std::string(key), std::move(value)); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (!inserted) { | ||||||||||||||||||||||||||||
| throw shambase::make_except_with_loc<std::runtime_error>( | ||||||||||||||||||||||||||||
| "registry entry already exists: " + std::string(key)); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
| // Registry types | ||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| using module_factory_t = std::function<py::module()>; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
| // Global registries | ||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| registry_t<py::module> &modules(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| registry_t<module_factory_t> &builders(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
| // Global build call | ||||||||||||||||||||||||||||
| // ------------------------------------------------------------------ | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| inline void build_all_modules() { | ||||||||||||||||||||||||||||
| auto &module_map = modules().data(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Get snapshot of builder keys | ||||||||||||||||||||||||||||
| std::vector<std::string> keys = builders().keys(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Lexicographic order | ||||||||||||||||||||||||||||
| std::sort(keys.begin(), keys.end()); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| for (auto const &key : keys) { | ||||||||||||||||||||||||||||
| auto &builder = builders().get(key); | ||||||||||||||||||||||||||||
| module_map.emplace(key, builder()); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| } // namespace shambindings::submodules | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| /// function signature used to register python modules | ||||||||||||||||||||||||||||
| using fct_sig = std::function<void(py::module &)>; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| /// Register a python module init function to be ran on init | ||||||||||||||||||||||||||||
| void register_pybind_init_func(fct_sig); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| /// Utility struct to register python modules through static init | ||||||||||||||||||||||||||||
| struct PyBindStaticInit { | ||||||||||||||||||||||||||||
| /// Constructor to register the python init function using static init | ||||||||||||||||||||||||||||
| inline explicit PyBindStaticInit(fct_sig t) { register_pybind_init_func(std::move(t)); } | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
| #define Register_pymod_int(funcname, lambda_name) \ | ||||||||||||||||||||||||||||
| static void funcname(py::module &m); \ | ||||||||||||||||||||||||||||
| static shambase::call_lambda lambda_name([]() { \ | ||||||||||||||||||||||||||||
| register_pybind_init_func(funcname); \ | ||||||||||||||||||||||||||||
| }); \ | ||||||||||||||||||||||||||||
| static void funcname(py::module &m) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
| * @brief Register a python module init function using static initialisation | ||||||||||||||||||||||||||||
|
|
@@ -54,7 +186,18 @@ struct PyBindStaticInit { | |||||||||||||||||||||||||||
| * @endcode | ||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
| #define Register_pymod(placeholdername) \ | ||||||||||||||||||||||||||||
| void pymod_##placeholdername(py::module &m); \ | ||||||||||||||||||||||||||||
| void (*pymod_ptr_##placeholdername)(py::module & m) = pymod_##placeholdername; \ | ||||||||||||||||||||||||||||
| PyBindStaticInit pymod_class_obj_##placeholdername(pymod_ptr_##placeholdername); \ | ||||||||||||||||||||||||||||
| void pymod_##placeholdername(py::module &m) | ||||||||||||||||||||||||||||
| Register_pymod_int(pymod_##placeholdername, pymod_class_obj_##placeholdername) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| #define Register_pybind() \ | ||||||||||||||||||||||||||||
| Register_pymod_int(__shamrock_unique_name(pybind_), __shamrock_unique_name(pybind_class_obj_)) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| #define Register_pymodsubmodule_int(path, funcname, lambda_name) \ | ||||||||||||||||||||||||||||
| py::module funcname(); \ | ||||||||||||||||||||||||||||
| shambase::call_lambda lambda_name([]() { \ | ||||||||||||||||||||||||||||
| shambindings::submodules::builders().insert(path, funcname); \ | ||||||||||||||||||||||||||||
| }); \ | ||||||||||||||||||||||||||||
| py::module funcname() | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| #define Register_pymodsubmodule(path) \ | ||||||||||||||||||||||||||||
| Register_pymodsubmodule_int( \ | ||||||||||||||||||||||||||||
| path, __shamrock_unique_name(pybind_class_obj_), __shamrock_unique_name(pybind_)) | ||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,9 +37,19 @@ | |
| #include <complex> | ||
| #include <utility> | ||
|
|
||
| Register_pymod(shamphyslibinit) { | ||
| Register_pymodsubmodule("shamrock.phys") { | ||
| return shambindings::submodules::modules() | ||
| .get("shamrock") | ||
| .def_submodule("phys", "Physics Library"); | ||
| } | ||
|
|
||
| Register_pybind() {} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| Register_pybind() { | ||
|
|
||
| py::module &shamphys_module = shambindings::submodules::modules().get("shamrock.phys"); | ||
|
|
||
| py::module shamphys_module = m.def_submodule("phys", "Physics Library"); | ||
| // py::module shamphys_module = m.def_submodule("phys", "Physics Library"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| // Planets.hpp | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The @file documentation tag is incorrect. It should refer to call_lambda.hpp instead of print.hpp.