From 8c235a637187b5b7bc03d94929f4675944a589fe Mon Sep 17 00:00:00 2001 From: tomc271 Date: Fri, 10 Jul 2026 12:12:56 +0100 Subject: [PATCH 1/2] Use c++20 concepts to refactor uses of unnamed template parameter with default value. `concept IsField = std::is_base_of_v` --- include/bout/field.hxx | 31 +++++++++++++++++-------------- include/bout/field3d.hxx | 2 +- include/bout/fieldgroup.hxx | 26 +++----------------------- include/bout/where.hxx | 20 +++++++++++++++----- src/field/field3d.cxx | 8 +++----- src/mesh/coordinates.cxx | 4 ++-- src/solver/nvector.hxx | 10 +++++----- 7 files changed, 46 insertions(+), 55 deletions(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index a8e1952546..6c06aa0090 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -258,8 +258,11 @@ inline T filledFrom(const T& f, Function func, std::string region_string = "RGN_ return result; } +template +concept IsField = std::is_base_of_v; + /// Unary + operator. This doesn't do anything -template > +template T operator+(const T& f) { return f; } @@ -341,7 +344,7 @@ inline T fromFieldAligned(const T& f, const std::string& region = "RGN_ALL") { /// @param[in] f Input field /// @param[in] allpe Minimum over all processors? /// @param[in] rgn The region to calculate the result over -template > +template inline BoutReal min(const T& f, bool allpe = false, const std::string& rgn = "RGN_NOBNDRY") { @@ -390,7 +393,7 @@ inline BoutReal min(const BinaryExpr& f, bool allpe = false, /// @param[in] f The field to check /// @param[in] allpe Check over all processors /// @param[in] region The region to check for uniformity over -template > +template inline bool isUniform(const T& f, bool allpe = false, const std::string& region = "RGN_ALL") { bool result = true; @@ -418,7 +421,7 @@ inline bool isUniform(const T& f, bool allpe = false, /// @param[in] f The field to check /// @param[in] allpe Check over all processors /// @param[in] region The region to assume is uniform -template > +template inline BoutReal getUniform(const T& f, [[maybe_unused]] bool allpe = false, const std::string& region = "RGN_ALL") { #if CHECK > 1 @@ -443,7 +446,7 @@ inline BoutReal getUniform(const T& f, [[maybe_unused]] bool allpe = false, /// @param[in] f Input field /// @param[in] allpe Maximum over all processors? /// @param[in] rgn The region to calculate the result over -template > +template inline BoutReal max(const T& f, bool allpe = false, const std::string& rgn = "RGN_NOBNDRY") { @@ -494,7 +497,7 @@ inline BoutReal max(const BinaryExpr& f, bool allpe = false, /// @param[in] f Input field /// @param[in] allpe Mean over all processors? /// @param[in] rgn The region to calculate the result over -template > +template inline BoutReal mean(const T& f, bool allpe = false, const std::string& rgn = "RGN_NOBNDRY") { @@ -544,7 +547,7 @@ inline BoutReal mean(const BinaryExpr& f, bool allpe = false, /// This loops over the entire domain, including guard/boundary cells by /// default (can be changed using the \p rgn argument) /// If CHECK >= 3 then the result will be checked for non-finite numbers -template > +template T pow(const T& lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { ASSERT1(areFieldsCompatible(lhs, rhs)); @@ -557,7 +560,7 @@ T pow(const T& lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { return result; } -template > +template T pow(const T& lhs, BoutReal rhs, const std::string& rgn = "RGN_ALL") { // Check if the inputs are allocated @@ -572,7 +575,7 @@ T pow(const T& lhs, BoutReal rhs, const std::string& rgn = "RGN_ALL") { return result; } -template > +template T pow(BoutReal lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { // Check if the inputs are allocated @@ -639,7 +642,7 @@ std::optional getPerpYIndex(const BinaryExpr& expr) { } \ }; \ }; \ - template > \ + template \ inline auto name(const T& f, const std::string& rgn = "RGN_ALL") { \ if constexpr (std::is_same_v) { \ /* Check if the input is allocated */ \ @@ -696,7 +699,7 @@ struct Square { }; }; // namespace bout::op -template > +template inline auto SQ(const T& f, const std::string& rgn = "RGN_ALL") { if constexpr (std::is_same_v) { checkData(f); @@ -840,7 +843,7 @@ FIELD_FUNC(tanh, ::tanh) /// Check if all values of a field \p var are finite. /// Loops over all points including the boundaries by /// default (can be changed using the \p rgn argument -template > +template inline bool finite(const T& f, const std::string& rgn = "RGN_ALL") { if (!f.isAllocated()) { @@ -858,7 +861,7 @@ inline bool finite(const T& f, const std::string& rgn = "RGN_ALL") { /// Makes a copy of a field \p f, ensuring that the underlying data is /// not shared. -template > +template T copy(const T& f) { T result = f; result.allocate(); @@ -873,7 +876,7 @@ class Field3DParallel; /// @param[in] var Variable to apply floor to /// @param[in] f The floor value /// @param[in] rgn The region to calculate the result over -template > +template inline T floor(const T& var, BoutReal f, const std::string& rgn = "RGN_ALL") { checkData(var); T result = copy(var); diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 905e736999..79bb41cb8e 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -669,7 +669,7 @@ protected: _track(change, operation); } } - template > + template void _track(const T& change, std::string operation); void _track(const BoutReal& change, std::string operation); diff --git a/include/bout/fieldgroup.hxx b/include/bout/fieldgroup.hxx index 440655e7ce..22667e0df3 100644 --- a/include/bout/fieldgroup.hxx +++ b/include/bout/fieldgroup.hxx @@ -1,7 +1,6 @@ #ifndef BOUT_FIELDGROUP_H #define BOUT_FIELDGROUP_H -#include #include #include @@ -62,7 +61,7 @@ public: /// to FieldGroup, leading to an infinite loop. template explicit FieldGroup(Ts&... ts) { - add(ts...); + (add(ts), ...); } /// Copy contents of another FieldGroup \p other into this group. @@ -121,27 +120,8 @@ public: /// Add multiple fields to this group template - void add(Field& t, Ts&... ts) { - add(t); // Add the first using functions above - add(ts...); // Add the rest - } - - template - void add(Field3D& t, Ts&... ts) { - add(t); // Add the first using functions above - add(ts...); // Add the rest - } - - template - void add(Vector3D& t, Ts&... ts) { - add(t); // Add the first using functions above - add(ts...); // Add the rest - } - - template - void add(Vector2D& t, Ts&... ts) { - add(t); // Add the first using functions above - add(ts...); // Add the rest + requires(sizeof...(Ts) > 1) void add(Ts&... ts) { + (add(ts), ...); } /// Return number of fields diff --git a/include/bout/where.hxx b/include/bout/where.hxx index c798d75de8..bd9cd26672 100644 --- a/include/bout/where.hxx +++ b/include/bout/where.hxx @@ -31,14 +31,18 @@ #include "bout/field.hxx" #include "bout/field2d.hxx" #include "bout/field3d.hxx" +#include /// For each point, choose between two inputs based on a third input /// /// @param[in] test The value which determines which input to use /// @param[in] gt0 Uses this value if test > 0.0 /// @param[in] le0 Uses this value if test <= 0.0 -template > + +// Overload 1: Three fields +template > +requires IsField&& IsField&& IsField auto where(const T& test, const U& gt0, const V& le0) -> ResultType { ASSERT1_FIELDS_COMPATIBLE(test, gt0); ASSERT1_FIELDS_COMPATIBLE(test, le0); @@ -51,7 +55,9 @@ auto where(const T& test, const U& gt0, const V& le0) -> ResultType { return result; } -template > +// Overload 2: Two fields, one BoutReal (le0) +template > +requires IsField&& IsField auto where(const T& test, const U& gt0, BoutReal le0) -> ResultType { ASSERT1_FIELDS_COMPATIBLE(test, gt0); @@ -63,7 +69,9 @@ auto where(const T& test, const U& gt0, BoutReal le0) -> ResultType { return result; } -template > +// Overload 3: Two fields, one BoutReal (gt0) +template > +requires IsField&& IsField auto where(const T& test, BoutReal gt0, const V& le0) -> ResultType { ASSERT1_FIELDS_COMPATIBLE(test, le0); @@ -75,7 +83,9 @@ auto where(const T& test, BoutReal gt0, const V& le0) -> ResultType { return result; } -template +// Overload 4: One field, two BoutReals +template +requires IsField auto where(const T& test, BoutReal gt0, BoutReal le0) -> ResultType { ResultType result{emptyFrom(test)}; diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index a513cc6021..f227e5c24a 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -982,7 +982,7 @@ Field3D& Field3D::enableTracking(const std::string& name, return *this; } -template +template void Field3D::_track(const T& change, std::string operation) { if (tracking_state == 0) { return; @@ -1010,12 +1010,10 @@ void Field3D::_track(const T& change, std::string operation) { }); } -template void -Field3D::_track>(const Field3D&, - std::string); +template void Field3D::_track(const Field3D&, std::string); template void Field3D::_track(const Field3DParallel&, std::string); template void Field3D::_track(const Field2D&, std::string); -template void Field3D::_track<>(const FieldPerp&, std::string); +template void Field3D::_track(const FieldPerp&, std::string); void Field3D::_track(const BoutReal& change, std::string operation) { if (tracking_state == 0) { diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 5be6c2bdc1..6a56b64b34 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -56,7 +56,7 @@ namespace { // initializing yet, leading to an infinite recursion. // Also, here we interpolate for the boundary points at xstart/ystart and // (xend+1)/(yend+1) instead of extrapolating. -template > +template void fillGuards_impl(T& result, CELL_LOC location, const T& f, bool extrapolate_x, bool extrapolate_y, bool no_extra_interpolate = false) { const auto* localmesh = result.getMesh(); @@ -172,7 +172,7 @@ void fillGuards_impl(T& result, CELL_LOC location, const T& f, bool extrapolate_ /// Boundary guard cells are set by extrapolating from the grid, like /// 'free_o3' boundary conditions /// Corner guard cells are set to BoutNaN -template > +template auto interpolateAndExtrapolate(const T& f_, CELL_LOC location, bool extrapolate_x, bool extrapolate_y, bool no_extra_interpolate, ParallelTransform* pt_ = nullptr) -> T { diff --git a/src/solver/nvector.hxx b/src/solver/nvector.hxx index 39aa52d3ca..e53d262207 100644 --- a/src/solver/nvector.hxx +++ b/src/solver/nvector.hxx @@ -100,7 +100,7 @@ private: public: BoutNVector() = delete; // Enforce static access only - template > + template static N_Vector create(Ctx&& ctx, T& field, const bool evolve_bndry, const bool own = false) { N_Vector v = callWithSUNContext(N_VNewEmpty, std::forward(ctx)); @@ -186,12 +186,12 @@ public: return v; } - template > + template static void swap(const N_Vector v, T& field) { field.swapData(get_field(v)); } - template > + template static T& get(const N_Vector v) { return get_field(v); } @@ -205,12 +205,12 @@ public: return v; } - template > + template static void swap(const N_Vector v, T& field, std::size_t subvector) { return BoutNVector::swap(N_VGetSubvector_ManyVector(v, subvector), field); } - template > + template static T& get(const N_Vector v, std::size_t subvector) { return BoutNVector::get(N_VGetSubvector_ManyVector(v, subvector)); } From 4e2014f5843f041887e3e96b5e691627425d8dfe Mon Sep 17 00:00:00 2001 From: tomc271 Date: Fri, 3 Jul 2026 13:36:33 +0100 Subject: [PATCH 2/2] Refactor typeName implementation Replaced template specializations with a single `if constexpr` block. --- CMakeLists.txt | 3 +- include/bout/sys/type_name.hxx | 69 ++++++++++++++++----------------- src/sys/type_name.cxx | 71 ---------------------------------- tests/unit/CMakeLists.txt | 1 - 4 files changed, 34 insertions(+), 110 deletions(-) delete mode 100644 src/sys/type_name.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 3caf4d8caf..2ef7b62e98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -387,8 +387,7 @@ set(BOUT_SOURCES ./src/sys/range.cxx ./src/sys/slepclib.cxx ./src/sys/timer.cxx - ./src/sys/type_name.cxx - ./src/sys/utils.cxx + ./src/sys/utils.cxx ${CMAKE_CURRENT_BINARY_DIR}/include/bout/revision.hxx ${CMAKE_CURRENT_BINARY_DIR}/include/bout/version.hxx ) diff --git a/include/bout/sys/type_name.hxx b/include/bout/sys/type_name.hxx index fcd251bf7a..1a030ae102 100644 --- a/include/bout/sys/type_name.hxx +++ b/include/bout/sys/type_name.hxx @@ -8,8 +8,10 @@ #include "bout/bout_types.hxx" #include +#include // Required for std::is_same_v #include +// Forward declarations class Field2D; class Field3D; class FieldPerp; @@ -23,44 +25,39 @@ namespace utils { template std::string typeName() { - return typeid(T).name(); + if constexpr (std::is_same_v) + return "bool"; + else if constexpr (std::is_same_v) + return "int"; + else if constexpr (std::is_same_v) + return "string"; + // Specialised for BOUT++ types to ensure that the result is human-readable + else if constexpr (std::is_same_v) + return "BoutReal"; + else if constexpr (std::is_same_v) + return "Field2D"; + else if constexpr (std::is_same_v) + return "Field3D"; + else if constexpr (std::is_same_v) + return "FieldPerp"; + else if constexpr (std::is_same_v>) + return "Array"; + else if constexpr (std::is_same_v>) + return "Array"; + else if constexpr (std::is_same_v>) + return "Matrix"; + else if constexpr (std::is_same_v>) + return "Matrix"; + else if constexpr (std::is_same_v>) + return "Tensor"; + else if constexpr (std::is_same_v>) + return "Tensor"; + else { + return typeid(T).name(); + } } -template <> -std::string typeName(); - -template <> -std::string typeName(); - -template <> -std::string typeName(); - -// Specialised for BOUT++ types to ensure that the result is human-readable -template <> -std::string typeName(); - -template <> -std::string typeName(); - -template <> -std::string typeName(); - -template <> -std::string typeName(); - -template <> -std::string typeName>(); -template <> -std::string typeName>(); -template <> -std::string typeName>(); -template <> -std::string typeName>(); -template <> -std::string typeName>(); -template <> -std::string typeName>(); } // namespace utils } // namespace bout -#endif //TYPE_NAME_HXX +#endif // TYPE_NAME_HXX diff --git a/src/sys/type_name.cxx b/src/sys/type_name.cxx deleted file mode 100644 index 68272bd473..0000000000 --- a/src/sys/type_name.cxx +++ /dev/null @@ -1,71 +0,0 @@ -#include "bout/sys/type_name.hxx" - -#include "bout/field2d.hxx" -#include "bout/field3d.hxx" -#include "bout/fieldperp.hxx" - -namespace bout { -namespace utils { - -template <> -std::string typeName() { - return "bool"; -} - -template <> -std::string typeName() { - return "int"; -} - -template <> -std::string typeName() { - return "string"; -} - -template <> -std::string typeName() { - return "BoutReal"; -} - -template <> -std::string typeName() { - return "Field2D"; -} - -template <> -std::string typeName() { - return "Field3D"; -} - -template <> -std::string typeName() { - return "FieldPerp"; -} - -template <> -std::string typeName>() { - return "Array"; -} -template <> -std::string typeName>() { - return "Array"; -} -template <> -std::string typeName>() { - return "Matrix"; -} -template <> -std::string typeName>() { - return "Matrix"; -} -template <> -std::string typeName>() { - return "Tensor"; -} -template <> -std::string typeName>() { - return "Tensor"; -} - -} // namespace utils -} // namespace bout diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index e8c4da31ca..ecbd946f6f 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -116,7 +116,6 @@ set(serial_tests_source ./sys/test_output.cxx ./sys/test_range.cxx ./sys/test_timer.cxx - ./sys/test_type_name.cxx ./sys/test_utils.cxx ./sys/test_variant.cxx ./sys/test_raja.cxx