From 10eb44a15357b7bc9778390ddf0e936e6e74a0ad Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Thu, 15 Oct 2026 07:47:33 +0200 Subject: [PATCH 1/7] use dispatch instead of visit --- src/t8_schemes/t8_scheme.hxx | 362 ++++++++++++++++++++++------------- 1 file changed, 227 insertions(+), 135 deletions(-) diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index a115d58c67..4fd769904f 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -28,6 +28,9 @@ #ifndef T8_SCHEME_HXX #define T8_SCHEME_HXX +#include +#include +#include #include #include #include @@ -117,6 +120,69 @@ struct t8_scheme mutable t8_refcount_t rc; /**< The reference count of the scheme. Mutable so that the class can be const and the ref counter is still mutable. TODO: Replace by shared_ptr when forest becomes a class. */ + /** Call \a TFunc on the alternative \a TIndex of an eclass scheme variant. + * This is one entry of the dispatch table built by \ref make_dispatch_table. + * \tparam TIndex The index of the variant alternative. + * \tparam TFunc A captureless callable that takes the eclass scheme and \a args. + * \tparam TArgs The types of the arguments. + * \param [in] scheme The eclass scheme variant. Must hold the alternative \a TIndex. + * \param [in] args The arguments passed on to \a TFunc. + * \return The return value of \a TFunc. + */ + template + static inline decltype (auto) + dispatch_to (const scheme_var &scheme, TArgs... args) + { + /* The dispatch table guarantees that the index matches. Telling the compiler lets it drop the check in get_if. */ + if (scheme.index () != TIndex) { + __builtin_unreachable (); + } + return TFunc {}(*std::get_if (&scheme), args...); + } + + /** Build a table with one \ref dispatch_to function pointer per variant alternative. + * \tparam TFunc A captureless callable that takes the eclass scheme and the arguments. + * \tparam TArgs The types of the arguments. + * \tparam TIndices The indices of all variant alternatives. + * \return The table of function pointers, indexed by the variant index. + */ + template + static constexpr auto + make_dispatch_table (std::index_sequence) + { + using TReturn + = decltype (dispatch_to<0, TFunc, TArgs...> (std::declval (), std::declval ()...)); + return std::array { + &dispatch_to... + }; + } + + /** Call \a func on the eclass scheme of \a tree_class. + * We use this instead of std::visit, since std::visit is considerably slower on this hot path: + * Its visitor captures the arguments by reference, so they have to be stored on the stack, it checks for + * a valueless variant and, for more than 11 alternatives (libstdc++), it calls an extra thunk for each alternative. + * Here, the arguments are passed by value and there is exactly one indirect call per dispatch, + * comparable to a virtual function call. + * \note The arguments are copied. Do not use this for scheme functions with non-const reference parameters. + * \tparam TFunc A captureless callable that takes the eclass scheme and \a args. + * \tparam TArgs The types of the arguments. Must be trivially copyable. + * \param [in] tree_class The eclass of the current tree. + * \param [in] func The callable. Only its type is used. + * \param [in] args The arguments passed on to \a func. + * \return The return value of \a func. + */ + template + inline decltype (auto) + dispatch (const t8_eclass_t tree_class, [[maybe_unused]] TFunc func, TArgs... args) const + { + static_assert ((std::is_trivially_copyable_v && ...), "Scheme dispatch arguments are passed by value."); + static constexpr auto dispatch_table + = make_dispatch_table (std::make_index_sequence> {}); + const scheme_var &scheme = eclass_schemes[tree_class]; + T8_ASSERT (!scheme.valueless_by_exception ()); + return dispatch_table[scheme.index ()](scheme, args...); + } + public: /** * Increase the reference count of the scheme. @@ -174,7 +240,7 @@ struct t8_scheme inline t8_eclass_t get_eclass_scheme_eclass (const t8_eclass_t tree_class) const { - return std::visit ([&] (auto &&scheme) { return scheme.get_eclass (); }, eclass_schemes[tree_class]); + return dispatch (tree_class, [] (const auto &scheme) { return scheme.get_eclass (); }); } /** Get the dimension of the eclass scheme. @@ -184,7 +250,7 @@ struct t8_scheme inline size_t get_eclass_scheme_dimension (const t8_eclass_t tree_class) const { - return std::visit ([&] (auto &&scheme) { return scheme.get_dimension (); }, eclass_schemes[tree_class]); + return dispatch (tree_class, [] (const auto &scheme) { return scheme.get_dimension (); }); } /** Return the size of any element of a given class. @@ -196,7 +262,7 @@ struct t8_scheme inline size_t get_element_size (const t8_eclass_t tree_class) const { - return std::visit ([&] (auto &&scheme) { return scheme.get_element_size (); }, eclass_schemes[tree_class]); + return dispatch (tree_class, [] (const auto &scheme) { return scheme.get_element_size (); }); }; /** Returns true, if there is one element in the tree, that does not refine into 2^dim children. @@ -207,7 +273,7 @@ struct t8_scheme inline bool refines_irregular (const t8_eclass_t tree_class) const { - return std::visit ([&] (auto &&scheme) { return scheme.refines_irregular (); }, eclass_schemes[tree_class]); + return dispatch (tree_class, [] (const auto &scheme) { return scheme.refines_irregular (); }); }; /** Return the maximum allowed level for any element of a given class. @@ -217,7 +283,7 @@ struct t8_scheme inline int get_maxlevel (const t8_eclass_t tree_class) const { - return std::visit ([&] (auto &&scheme) { return scheme.get_maxlevel (); }, eclass_schemes[tree_class]); + return dispatch (tree_class, [] (const auto &scheme) { return scheme.get_maxlevel (); }); }; /** Return the level of a particular element. @@ -228,7 +294,8 @@ struct t8_scheme inline int element_get_level (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_level (element); }, eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_level (args...); }, element); }; /** Copy all entries of \a source to \a dest. \a dest must be an existing @@ -242,7 +309,8 @@ struct t8_scheme inline void element_copy (const t8_eclass_t tree_class, const t8_element_t *source, t8_element_t *dest) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_copy (source, dest); }, eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_copy (args...); }, source, dest); }; /** Compare two elements. @@ -256,8 +324,8 @@ struct t8_scheme inline int element_compare (const t8_eclass_t tree_class, const t8_element_t *elem1, const t8_element_t *elem2) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_compare (elem1, elem2); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_compare (args...); }, elem1, elem2); }; /** Check if two elements are equal. @@ -269,8 +337,8 @@ struct t8_scheme inline bool element_is_equal (const t8_eclass_t tree_class, const t8_element_t *elem1, const t8_element_t *elem2) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_is_equal (elem1, elem2); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_equal (args...); }, elem1, elem2); }; /** @@ -283,8 +351,8 @@ struct t8_scheme inline bool element_is_refinable (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_is_refinable (element); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_refinable (args...); }, element); }; /** Compute the parent of a given element \a element and store it in \a parent. @@ -303,8 +371,9 @@ struct t8_scheme inline void element_get_parent (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *parent) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_parent (element, parent); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_parent (args...); }, element, + parent); }; /** Compute the number of siblings of an element. That is the number of @@ -317,8 +386,8 @@ struct t8_scheme inline int element_get_num_siblings (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_num_siblings (element); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_num_siblings (args...); }, element); }; /** Compute a specific sibling of a given element \a element and store it in \a sibling. @@ -337,8 +406,9 @@ struct t8_scheme element_get_sibling (const t8_eclass_t tree_class, const t8_element_t *element, const int sibid, t8_element_t *sibling) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_sibling (element, sibid, sibling); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_sibling (args...); }, element, + sibid, sibling); }; /** Compute the number of corners of a given element. @@ -349,8 +419,8 @@ struct t8_scheme inline int element_get_num_corners (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_num_corners (element); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_num_corners (args...); }, element); }; /** Compute the number of faces of a given element. @@ -361,8 +431,8 @@ struct t8_scheme inline int element_get_num_faces (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_num_faces (element); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_num_faces (args...); }, element); }; /** Compute the maximum number of faces of a given element and all of its @@ -374,8 +444,9 @@ struct t8_scheme inline int element_get_max_num_faces (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_max_num_faces (element); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_max_num_faces (args...); }, + element); }; /** Return the number of children of an element when it is refined. @@ -392,18 +463,17 @@ struct t8_scheme element_get_num_children (const t8_eclass_t tree_class, const t8_element_t *element, TArgs &&...additional_arguments) const { - return std::visit ( - [&] (auto &&scheme) -> int { - if constexpr (requires { - scheme.element_get_num_children (element, std::forward (additional_arguments)...); - }) { - return scheme.element_get_num_children (element, std::forward (additional_arguments)...); + return dispatch ( + tree_class, + [] (const auto &scheme, const t8_element_t *elem, auto... args) -> int { + if constexpr (requires { scheme.element_get_num_children (elem, args...); }) { + return scheme.element_get_num_children (elem, args...); } else { SC_ABORT ("element_get_num_children is not supported by this scheme for these arguments"); } }, - eclass_schemes[tree_class]); + element, additional_arguments...); } /** Return the max number of children of an eclass. @@ -413,7 +483,7 @@ struct t8_scheme inline int get_max_num_children (const t8_eclass_t tree_class) const { - return std::visit ([&] (auto &&scheme) { return scheme.get_max_num_children (); }, eclass_schemes[tree_class]); + return dispatch (tree_class, [] (const auto &scheme) { return scheme.get_max_num_children (); }); }; /** Return the number of children of an element's face when the element is refined. @@ -425,8 +495,9 @@ struct t8_scheme inline int element_get_num_face_children (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_num_face_children (element, face); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_num_face_children (args...); }, + element, face); }; /** Return the corner number of an element's face corner. @@ -446,8 +517,9 @@ struct t8_scheme element_get_face_corner (const t8_eclass_t tree_class, const t8_element_t *element, const int face, const int corner) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_face_corner (element, face, corner); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_face_corner (args...); }, element, + face, corner); }; /** Return the face numbers of the faces sharing an element's corner. @@ -467,8 +539,9 @@ struct t8_scheme element_get_corner_face (const t8_eclass_t tree_class, const t8_element_t *element, const int corner, const int face) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_corner_face (element, corner, face); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_corner_face (args...); }, element, + corner, face); }; /** Construct the child element of a given number. @@ -483,8 +556,9 @@ struct t8_scheme element_get_child (const t8_eclass_t tree_class, const t8_element_t *element, const int childid, t8_element_t *child) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_child (element, childid, child); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_child (args...); }, element, + childid, child); }; /** Construct all children of a given element. @@ -506,18 +580,17 @@ struct t8_scheme element_get_children (const t8_eclass_t tree_class, const t8_element_t *element, const int length, t8_element_t *c[], TArgs &&...additional_arguments) const { - std::visit ( - [&] (auto &&scheme) -> void { - if constexpr (requires { - scheme.element_get_children (element, length, c, std::forward (additional_arguments)...); - }) { - scheme.element_get_children (element, length, c, std::forward (additional_arguments)...); + dispatch ( + tree_class, + [] (const auto &scheme, const t8_element_t *elem, const int len, t8_element_t **children, auto... args) -> void { + if constexpr (requires { scheme.element_get_children (elem, len, children, args...); }) { + scheme.element_get_children (elem, len, children, args...); } else { SC_ABORT ("element_get_children is not supported by this scheme for these arguments."); } }, - eclass_schemes[tree_class]); + element, length, c, additional_arguments...); } /** Compute the child id of an element. @@ -528,8 +601,8 @@ struct t8_scheme inline int element_get_child_id (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_child_id (element); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_child_id (args...); }, element); }; /** Compute the ancestor id of an element, that is the child id @@ -543,8 +616,9 @@ struct t8_scheme inline int element_get_ancestor_id (const t8_eclass_t tree_class, const t8_element_t *element, const int level) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_ancestor_id (element, level); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_ancestor_id (args...); }, element, + level); }; /** Query whether element A is an ancestor of the element B. @@ -558,8 +632,9 @@ struct t8_scheme bool element_is_ancestor (const t8_eclass_t tree_class, const t8_element_t *element_A, const t8_element_t *element_B) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_is_ancestor (element_A, element_B); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_ancestor (args...); }, element_A, + element_B); } /** Query whether a given set of elements is a family or not. @@ -572,7 +647,8 @@ struct t8_scheme inline bool elements_are_family (const t8_eclass_t tree_class, const t8_element_t *const *fam) const { - return std::visit ([&] (auto &&scheme) { return scheme.elements_are_family (fam); }, eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.elements_are_family (args...); }, fam); }; /** Compute the nearest common ancestor of two elements. That is, @@ -590,7 +666,9 @@ struct t8_scheme element_get_nca (const t8_eclass_t tree_class, const t8_element_t *elem1, const t8_element_t *elem2, t8_element_t *const nca) const { - std::visit ([&] (auto &&scheme) { return scheme.element_get_nca (elem1, elem2, nca); }, eclass_schemes[tree_class]); + dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_nca (args...); }, elem1, elem2, + nca); T8_ASSERT (element_is_ancestor (tree_class, nca, elem1)); T8_ASSERT (element_is_ancestor (tree_class, nca, elem2)); }; @@ -607,8 +685,9 @@ struct t8_scheme inline t8_element_shape_t element_get_face_shape (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_face_shape (element, face); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_face_shape (args...); }, element, + face); }; /** Given an element and a face of the element, compute all children of @@ -630,11 +709,9 @@ struct t8_scheme element_get_children_at_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face, t8_element_t *children[], int num_children, int *child_indices) const { - return std::visit ( - [&] (auto &&scheme) { - return scheme.element_get_children_at_face (element, face, children, num_children, child_indices); - }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_children_at_face (args...); }, + element, face, children, num_children, child_indices); }; /** Given a face of an element and a child number of a child of that face, return the face number @@ -662,8 +739,9 @@ struct t8_scheme element_face_get_child_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face, const int face_child) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_face_get_child_face (element, face, face_child); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_face_get_child_face (args...); }, + element, face, face_child); }; /** Given a face of an element return the face number @@ -679,8 +757,9 @@ struct t8_scheme inline int element_face_get_parent_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_face_get_parent_face (element, face); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_face_get_parent_face (args...); }, + element, face); }; /** Given a face of an element and a level coarser than (or equal to) @@ -699,9 +778,9 @@ struct t8_scheme element_face_get_ancestor_face (const t8_eclass_t tree_class, const t8_element_t *element, const int ancestor_level, const int face) const { - return std::visit ( - [&] (auto &&scheme) { return scheme.element_face_get_ancestor_face (element, ancestor_level, face); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_face_get_ancestor_face (args...); }, + element, ancestor_level, face); } /** Given an element and a face of this element. If the face lies on the @@ -721,8 +800,9 @@ struct t8_scheme inline int element_get_tree_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_tree_face (element, face); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_tree_face (args...); }, element, + face); }; /** Suppose we have two trees that share a common face f. @@ -751,9 +831,9 @@ struct t8_scheme element_transform_face (const t8_eclass_t tree_class, const t8_element_t *elem1, t8_element_t *elem2, const int orientation, const int sign, const int is_smaller_face) const { - return std::visit ( - [&] (auto &&scheme) { return scheme.element_transform_face (elem1, elem2, orientation, sign, is_smaller_face); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_transform_face (args...); }, elem1, + elem2, orientation, sign, is_smaller_face); }; /** Given a boundary face inside a root tree's face construct @@ -773,8 +853,9 @@ struct t8_scheme element_extrude_face (const t8_eclass_t tree_class, const t8_element_t *face, t8_element_t *element, const int root_face) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_extrude_face (face, element, root_face, this); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_extrude_face (args...); }, face, + element, root_face, this); }; /** Construct the boundary element at a specific face. @@ -792,8 +873,9 @@ struct t8_scheme element_get_boundary_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face, t8_element_t *boundary) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_boundary_face (element, face, boundary, this); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_boundary_face (args...); }, element, + face, boundary, this); }; /** Construct the first descendant of an element at a given level that touches a given face. @@ -809,9 +891,9 @@ struct t8_scheme element_get_first_descendant_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face, t8_element_t *first_desc, const int level) const { - return std::visit ( - [&] (auto &&scheme) { return scheme.element_get_first_descendant_face (element, face, first_desc, level); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_first_descendant_face (args...); }, + element, face, first_desc, level); }; /** Construct the last descendant of an element at a given level that touches a given face. @@ -827,9 +909,9 @@ struct t8_scheme element_get_last_descendant_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face, t8_element_t *last_desc, const int level) const { - return std::visit ( - [&] (auto &&scheme) { return scheme.element_get_last_descendant_face (element, face, last_desc, level); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_last_descendant_face (args...); }, + element, face, last_desc, level); }; /** Compute whether a given element shares a given face with its root tree. @@ -842,8 +924,9 @@ struct t8_scheme inline bool element_is_root_boundary (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_is_root_boundary (element, face); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_root_boundary (args...); }, element, + face); }; /** Construct the face neighbor of a given element if this face neighbor @@ -866,9 +949,9 @@ struct t8_scheme element_get_face_neighbor_inside (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *neigh, const int face, int *neigh_face) const { - return std::visit ( - [&] (auto &&scheme) { return scheme.element_get_face_neighbor_inside (element, neigh, face, neigh_face); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_face_neighbor_inside (args...); }, + element, neigh, face, neigh_face); }; /** Return the shape of an allocated element according its type. @@ -881,7 +964,8 @@ struct t8_scheme inline t8_element_shape_t element_get_shape (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_shape (element); }, eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_shape (args...); }, element); }; /** Initialize the entries of an allocated element according to a @@ -896,8 +980,9 @@ struct t8_scheme element_set_linear_id (const t8_eclass_t tree_class, t8_element_t *element, const int level, const t8_linearidx_t id) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_set_linear_id (element, level, id); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_set_linear_id (args...); }, element, + level, id); }; /** Compute the linear id of a given element in a hypothetical uniform @@ -910,8 +995,9 @@ struct t8_scheme inline t8_linearidx_t element_get_linear_id (const t8_eclass_t tree_class, const t8_element_t *element, const int level) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_linear_id (element, level); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_linear_id (args...); }, element, + level); }; /** Compute the first descendant of a given element. @@ -925,8 +1011,9 @@ struct t8_scheme element_get_first_descendant (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *desc, const int level) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_first_descendant (element, desc, level); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_first_descendant (args...); }, + element, desc, level); }; /** Compute the last descendant of a given element. @@ -940,8 +1027,9 @@ struct t8_scheme element_get_last_descendant (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *desc, const int level) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_get_last_descendant (element, desc, level); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_last_descendant (args...); }, + element, desc, level); }; /** Construct the successor in a uniform refinement of a given element. @@ -952,8 +1040,9 @@ struct t8_scheme inline void element_construct_successor (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *successor) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_construct_successor (element, successor); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_construct_successor (args...); }, + element, successor); }; /** Compute the coordinates of a given element vertex inside a reference tree @@ -970,9 +1059,10 @@ struct t8_scheme element_get_vertex_reference_coords (const t8_eclass_t tree_class, const t8_element_t *element, const int vertex, double coords[]) const { - return std::visit ( - [&] (auto &&scheme) { return scheme.element_get_vertex_reference_coords (element, vertex, coords); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, + [] (const auto &scheme, auto... args) { return scheme.element_get_vertex_reference_coords (args...); }, element, + vertex, coords); }; /** Convert points in the reference space of an element to points in the @@ -989,9 +1079,9 @@ struct t8_scheme element_get_reference_coords (const t8_eclass_t tree_class, const t8_element_t *element, const double *ref_coords, const size_t num_coords, double *out_coords) const { - return std::visit ( - [&] (auto &&scheme) { return scheme.element_get_reference_coords (element, ref_coords, num_coords, out_coords); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_reference_coords (args...); }, + element, ref_coords, num_coords, out_coords); }; /** Count how many leaf descendants of a given uniform level an element would produce. @@ -1009,8 +1099,8 @@ struct t8_scheme inline t8_gloidx_t element_count_leaves (const t8_eclass_t tree_class, const t8_element_t *t, const int level) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_count_leaves (t, level); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_count_leaves (args...); }, t, level); }; /** Count how many leaf descendants of a given uniform level the root element will produce. @@ -1025,8 +1115,8 @@ struct t8_scheme inline t8_gloidx_t count_leaves_from_root (const t8_eclass_t tree_class, const int level) const { - return std::visit ([&] (auto &&scheme) { return scheme.count_leaves_from_root (level); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.count_leaves_from_root (args...); }, level); }; #if T8_ENABLE_DEBUG @@ -1050,7 +1140,8 @@ struct t8_scheme inline int element_is_valid (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_is_valid (element); }, eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_valid (args...); }, element); }; /** @@ -1063,8 +1154,8 @@ struct t8_scheme inline void element_debug_print (const t8_eclass_t tree_class, const t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_debug_print (element); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_debug_print (args...); }, element); }; #endif @@ -1079,8 +1170,9 @@ struct t8_scheme element_to_string (const t8_eclass_t tree_class, const t8_element_t *element, char *debug_string, const int string_size) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_to_string (element, debug_string, string_size); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_to_string (args...); }, element, + debug_string, string_size); }; /** Allocate memory for \a length many elements of a given class and initialize them, @@ -1108,8 +1200,8 @@ struct t8_scheme inline void element_new (const t8_eclass_t tree_class, const int length, t8_element_t **elements) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_new (length, elements); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_new (args...); }, length, elements); }; /** Initialize an array of allocated elements. @@ -1130,8 +1222,8 @@ struct t8_scheme inline void element_init (const t8_eclass_t tree_class, const int length, t8_element_t *elements) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_init (length, elements); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_init (args...); }, length, elements); }; /** Deinitialize an array of allocated elements. @@ -1146,8 +1238,8 @@ struct t8_scheme inline void element_deinit (const t8_eclass_t tree_class, const int length, t8_element_t *elements) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_deinit (length, elements); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_deinit (args...); }, length, elements); }; /** Deallocate an array of elements. @@ -1162,8 +1254,8 @@ struct t8_scheme inline void element_destroy (const t8_eclass_t tree_class, const int length, t8_element_t **elements) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_destroy (length, elements); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_destroy (args...); }, length, elements); }; /** create the root element @@ -1173,7 +1265,8 @@ struct t8_scheme inline void set_to_root (const t8_eclass_t tree_class, t8_element_t *element) const { - return std::visit ([&] (auto &&scheme) { return scheme.set_to_root (element); }, eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.set_to_root (args...); }, element); }; /** Pack multiple elements into contiguous memory, so they can be sent via MPI. @@ -1189,11 +1282,9 @@ struct t8_scheme element_MPI_Pack (const t8_eclass_t tree_class, t8_element_t **const elements, const unsigned int count, void *send_buffer, int buffer_size, int *position, sc_MPI_Comm comm) const { - return std::visit ( - [&] (auto &&scheme) { - return scheme.element_MPI_Pack (elements, count, send_buffer, buffer_size, position, comm); - }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_MPI_Pack (args...); }, elements, count, + send_buffer, buffer_size, position, comm); }; /** Determine an upper bound for the size of the packed message of \a count elements @@ -1205,8 +1296,9 @@ struct t8_scheme inline void element_MPI_Pack_size (const t8_eclass_t tree_class, const unsigned int count, sc_MPI_Comm comm, int *pack_size) const { - return std::visit ([&] (auto &&scheme) { return scheme.element_MPI_Pack_size (count, comm, pack_size); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_MPI_Pack_size (args...); }, count, comm, + pack_size); }; /** Unpack multiple elements from contiguous memory that was received via MPI. @@ -1222,9 +1314,9 @@ struct t8_scheme element_MPI_Unpack (t8_eclass_t tree_class, void *recvbuf, const int buffer_size, int *position, t8_element_t **elements, const unsigned int count, sc_MPI_Comm comm) const { - return std::visit ( - [&] (auto &&scheme) { return scheme.element_MPI_Unpack (recvbuf, buffer_size, position, elements, count, comm); }, - eclass_schemes[tree_class]); + return dispatch ( + tree_class, [] (const auto &scheme, auto... args) { return scheme.element_MPI_Unpack (args...); }, recvbuf, + buffer_size, position, elements, count, comm); }; }; From ec1b5d37251316f04de00008a0ad2e3f5601c2e8 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Thu, 15 Oct 2026 07:49:45 +0200 Subject: [PATCH 2/7] enable symbolic function optimization --- src/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 27115abb7c..627c0d2c5b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,6 +22,13 @@ if ( T8CODE_BUILD_AS_SHARED_LIBRARY ) add_library( T8 SHARED ) set_target_properties( T8 PROPERTIES VERSION ${T8CODE_VERSION_MAJOR}.${T8CODE_VERSION_MINOR}.${T8CODE_VERSION_PATCH} SOVERSION ${T8CODE_VERSION_RAW}) set_target_properties( T8 PROPERTIES POSITION_INDEPENDENT_CODE ON ) + # Bind calls to functions defined inside libt8 directly instead of through the PLT. + # This avoids a second indirect jump on every scheme function call (see t8_scheme::dispatch). + include( CheckLinkerFlag ) + check_linker_flag( CXX "-Wl,-Bsymbolic-functions" T8CODE_LINKER_SUPPORTS_BSYMBOLIC_FUNCTIONS ) + if ( T8CODE_LINKER_SUPPORTS_BSYMBOLIC_FUNCTIONS ) + target_link_options( T8 PRIVATE "-Wl,-Bsymbolic-functions" ) + endif() else() add_library( T8 STATIC ) endif() From 441f50ccd27277cdfabb8bcd0f9eae8af336e33a Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Thu, 15 Oct 2026 09:40:20 +0200 Subject: [PATCH 3/7] fix documentation --- src/t8_schemes/t8_scheme.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index 4fd769904f..a88811267e 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -121,7 +121,7 @@ struct t8_scheme rc; /**< The reference count of the scheme. Mutable so that the class can be const and the ref counter is still mutable. TODO: Replace by shared_ptr when forest becomes a class. */ /** Call \a TFunc on the alternative \a TIndex of an eclass scheme variant. - * This is one entry of the dispatch table built by \ref make_dispatch_table. + * This is one entry of the dispatch table built by make_dispatch_table. * \tparam TIndex The index of the variant alternative. * \tparam TFunc A captureless callable that takes the eclass scheme and \a args. * \tparam TArgs The types of the arguments. @@ -140,7 +140,7 @@ struct t8_scheme return TFunc {}(*std::get_if (&scheme), args...); } - /** Build a table with one \ref dispatch_to function pointer per variant alternative. + /** Build a table with one dispatch_to function pointer per variant alternative. * \tparam TFunc A captureless callable that takes the eclass scheme and the arguments. * \tparam TArgs The types of the arguments. * \tparam TIndices The indices of all variant alternatives. From 6f90fa7f0c41037aac2a8fdc6aaad8cc087d2361 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Thu, 15 Oct 2026 12:59:38 +0200 Subject: [PATCH 4/7] remove compiler dependent code --- src/t8_schemes/t8_scheme.hxx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index a88811267e..50ae56d37c 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -133,10 +133,8 @@ struct t8_scheme static inline decltype (auto) dispatch_to (const scheme_var &scheme, TArgs... args) { - /* The dispatch table guarantees that the index matches. Telling the compiler lets it drop the check in get_if. */ - if (scheme.index () != TIndex) { - __builtin_unreachable (); - } + /* The dispatch table guarantees that the index matches. */ + T8_ASSERT (scheme.index () == TIndex); return TFunc {}(*std::get_if (&scheme), args...); } From d12a655976ec1145f9062b8a88467b5b51471a8f Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Mon, 19 Oct 2026 12:25:32 +0200 Subject: [PATCH 5/7] add an assertion --- src/t8_schemes/t8_scheme.hxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index 50ae56d37c..6ada4cb693 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -133,6 +133,8 @@ struct t8_scheme static inline decltype (auto) dispatch_to (const scheme_var &scheme, TArgs... args) { + static_assert (std::is_invocable_v &, const TArgs &...>, + "Scheme dispatch: TFunc must be callable with the eclass scheme and the const arguments."); /* The dispatch table guarantees that the index matches. */ T8_ASSERT (scheme.index () == TIndex); return TFunc {}(*std::get_if (&scheme), args...); From 6dafd166f757a7ac492597d0d6e5b4c312e1827d Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Mon, 19 Oct 2026 12:26:03 +0200 Subject: [PATCH 6/7] reowrding --- src/t8_schemes/t8_scheme.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index 6ada4cb693..cf496cca4b 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -160,7 +160,7 @@ struct t8_scheme /** Call \a func on the eclass scheme of \a tree_class. * We use this instead of std::visit, since std::visit is considerably slower on this hot path: * Its visitor captures the arguments by reference, so they have to be stored on the stack, it checks for - * a valueless variant and, for more than 11 alternatives (libstdc++), it calls an extra thunk for each alternative. + * a valueless variant and, for more than 11 alternatives (libstdc++), it calls an extra subroutine for each alternative. * Here, the arguments are passed by value and there is exactly one indirect call per dispatch, * comparable to a virtual function call. * \note The arguments are copied. Do not use this for scheme functions with non-const reference parameters. From 79ba32be428a2a25bb1706d8175caf9e37b469e9 Mon Sep 17 00:00:00 2001 From: Sandro Elsweijer Date: Mon, 19 Oct 2026 12:26:23 +0200 Subject: [PATCH 7/7] make schemes require const --- src/t8_schemes/t8_scheme.hxx | 181 ++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 79 deletions(-) diff --git a/src/t8_schemes/t8_scheme.hxx b/src/t8_schemes/t8_scheme.hxx index cf496cca4b..6edd06ed61 100644 --- a/src/t8_schemes/t8_scheme.hxx +++ b/src/t8_schemes/t8_scheme.hxx @@ -137,7 +137,7 @@ struct t8_scheme "Scheme dispatch: TFunc must be callable with the eclass scheme and the const arguments."); /* The dispatch table guarantees that the index matches. */ T8_ASSERT (scheme.index () == TIndex); - return TFunc {}(*std::get_if (&scheme), args...); + return TFunc {}(*std::get_if (&scheme), std::as_const (args)...); } /** Build a table with one dispatch_to function pointer per variant alternative. @@ -163,8 +163,11 @@ struct t8_scheme * a valueless variant and, for more than 11 alternatives (libstdc++), it calls an extra subroutine for each alternative. * Here, the arguments are passed by value and there is exactly one indirect call per dispatch, * comparable to a virtual function call. - * \note The arguments are copied. Do not use this for scheme functions with non-const reference parameters. - * \tparam TFunc A captureless callable that takes the eclass scheme and \a args. + * \note The arguments are copied, so scheme functions with non-const reference parameters are not supported. + * To enforce this at compile time, the arguments are passed to \a func as const and \a func must take + * them as const, e.g. `[] (const auto &scheme, const auto... args) { ... }`. Binding them to a non-const + * reference parameter then fails to compile. + * \tparam TFunc A captureless callable that takes the eclass scheme and \a args as const. * \tparam TArgs The types of the arguments. Must be trivially copyable. * \param [in] tree_class The eclass of the current tree. * \param [in] func The callable. Only its type is used. @@ -295,7 +298,7 @@ struct t8_scheme element_get_level (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_level (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_level (args...); }, element); }; /** Copy all entries of \a source to \a dest. \a dest must be an existing @@ -310,7 +313,7 @@ struct t8_scheme element_copy (const t8_eclass_t tree_class, const t8_element_t *source, t8_element_t *dest) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_copy (args...); }, source, dest); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_copy (args...); }, source, dest); }; /** Compare two elements. @@ -325,7 +328,8 @@ struct t8_scheme element_compare (const t8_eclass_t tree_class, const t8_element_t *elem1, const t8_element_t *elem2) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_compare (args...); }, elem1, elem2); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_compare (args...); }, elem1, + elem2); }; /** Check if two elements are equal. @@ -338,7 +342,8 @@ struct t8_scheme element_is_equal (const t8_eclass_t tree_class, const t8_element_t *elem1, const t8_element_t *elem2) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_equal (args...); }, elem1, elem2); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_is_equal (args...); }, elem1, + elem2); }; /** @@ -352,7 +357,8 @@ struct t8_scheme element_is_refinable (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_refinable (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_is_refinable (args...); }, + element); }; /** Compute the parent of a given element \a element and store it in \a parent. @@ -372,7 +378,7 @@ struct t8_scheme element_get_parent (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *parent) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_parent (args...); }, element, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_parent (args...); }, element, parent); }; @@ -387,7 +393,8 @@ struct t8_scheme element_get_num_siblings (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_num_siblings (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_num_siblings (args...); }, + element); }; /** Compute a specific sibling of a given element \a element and store it in \a sibling. @@ -407,7 +414,7 @@ struct t8_scheme t8_element_t *sibling) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_sibling (args...); }, element, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_sibling (args...); }, element, sibid, sibling); }; @@ -420,7 +427,8 @@ struct t8_scheme element_get_num_corners (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_num_corners (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_num_corners (args...); }, + element); }; /** Compute the number of faces of a given element. @@ -432,7 +440,8 @@ struct t8_scheme element_get_num_faces (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_num_faces (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_num_faces (args...); }, + element); }; /** Compute the maximum number of faces of a given element and all of its @@ -445,14 +454,14 @@ struct t8_scheme element_get_max_num_faces (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_max_num_faces (args...); }, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_max_num_faces (args...); }, element); }; /** Return the number of children of an element when it is refined. * \param [in] tree_class The eclass of the current tree. * \param [in] element The element whose number of children is returned. - * \param [in] additional_arguments Additional arguments you want to provide. + * \param [in] additional_arguments Additional arguments you want to provide. * For subelements, this can be the subelement type. * Have a look at the scheme specific functions for more details. * \tparam TArgs Type of extra arguments you want to provide. Normally, this is auto deduced. @@ -465,7 +474,7 @@ struct t8_scheme { return dispatch ( tree_class, - [] (const auto &scheme, const t8_element_t *elem, auto... args) -> int { + [] (const auto &scheme, const t8_element_t *const elem, const auto... args) -> int { if constexpr (requires { scheme.element_get_num_children (elem, args...); }) { return scheme.element_get_num_children (elem, args...); } @@ -496,8 +505,9 @@ struct t8_scheme element_get_num_face_children (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_num_face_children (args...); }, - element, face); + tree_class, + [] (const auto &scheme, const auto... args) { return scheme.element_get_num_face_children (args...); }, element, + face); }; /** Return the corner number of an element's face corner. @@ -518,8 +528,8 @@ struct t8_scheme const int corner) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_face_corner (args...); }, element, - face, corner); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_face_corner (args...); }, + element, face, corner); }; /** Return the face numbers of the faces sharing an element's corner. @@ -540,8 +550,8 @@ struct t8_scheme const int face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_corner_face (args...); }, element, - corner, face); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_corner_face (args...); }, + element, corner, face); }; /** Construct the child element of a given number. @@ -557,7 +567,7 @@ struct t8_scheme t8_element_t *child) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_child (args...); }, element, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_child (args...); }, element, childid, child); }; @@ -568,7 +578,7 @@ struct t8_scheme * the number of children. * \param [in,out] c The storage for these \a length elements must exist. * On output, all children are valid. - * \param [in] additional_arguments Additional arguments you want to provide. + * \param [in] additional_arguments Additional arguments you want to provide. * For subelements, this can be the subelement type. * Have a look at the scheme specific functions for more details. * \tparam TArgs Type of extra arguments you want to provide. Normally, this is auto deduced. @@ -582,7 +592,8 @@ struct t8_scheme { dispatch ( tree_class, - [] (const auto &scheme, const t8_element_t *elem, const int len, t8_element_t **children, auto... args) -> void { + [] (const auto &scheme, const t8_element_t *const elem, const int len, t8_element_t **const children, + const auto... args) -> void { if constexpr (requires { scheme.element_get_children (elem, len, children, args...); }) { scheme.element_get_children (elem, len, children, args...); } @@ -602,7 +613,8 @@ struct t8_scheme element_get_child_id (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_child_id (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_child_id (args...); }, + element); }; /** Compute the ancestor id of an element, that is the child id @@ -617,12 +629,12 @@ struct t8_scheme element_get_ancestor_id (const t8_eclass_t tree_class, const t8_element_t *element, const int level) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_ancestor_id (args...); }, element, - level); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_ancestor_id (args...); }, + element, level); }; /** Query whether element A is an ancestor of the element B. - * An element A is ancestor of an element B if A == B or if B can + * An element A is ancestor of an element B if A == B or if B can * be obtained from A via successive refinement. * \param [in] tree_class The eclass of the current tree. * \param [in] element_A An element of class \a eclass in scheme \a scheme. @@ -633,8 +645,8 @@ struct t8_scheme element_is_ancestor (const t8_eclass_t tree_class, const t8_element_t *element_A, const t8_element_t *element_B) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_ancestor (args...); }, element_A, - element_B); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_is_ancestor (args...); }, + element_A, element_B); } /** Query whether a given set of elements is a family or not. @@ -648,7 +660,7 @@ struct t8_scheme elements_are_family (const t8_eclass_t tree_class, const t8_element_t *const *fam) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.elements_are_family (args...); }, fam); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.elements_are_family (args...); }, fam); }; /** Compute the nearest common ancestor of two elements. That is, @@ -667,8 +679,8 @@ struct t8_scheme t8_element_t *const nca) const { dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_nca (args...); }, elem1, elem2, - nca); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_nca (args...); }, elem1, + elem2, nca); T8_ASSERT (element_is_ancestor (tree_class, nca, elem1)); T8_ASSERT (element_is_ancestor (tree_class, nca, elem2)); }; @@ -686,8 +698,8 @@ struct t8_scheme element_get_face_shape (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_face_shape (args...); }, element, - face); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_face_shape (args...); }, + element, face); }; /** Given an element and a face of the element, compute all children of @@ -710,7 +722,7 @@ struct t8_scheme t8_element_t *children[], int num_children, int *child_indices) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_children_at_face (args...); }, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_children_at_face (args...); }, element, face, children, num_children, child_indices); }; @@ -740,7 +752,7 @@ struct t8_scheme const int face_child) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_face_get_child_face (args...); }, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_face_get_child_face (args...); }, element, face, face_child); }; @@ -758,7 +770,7 @@ struct t8_scheme element_face_get_parent_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_face_get_parent_face (args...); }, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_face_get_parent_face (args...); }, element, face); }; @@ -779,8 +791,9 @@ struct t8_scheme const int face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_face_get_ancestor_face (args...); }, - element, ancestor_level, face); + tree_class, + [] (const auto &scheme, const auto... args) { return scheme.element_face_get_ancestor_face (args...); }, element, + ancestor_level, face); } /** Given an element and a face of this element. If the face lies on the @@ -801,8 +814,8 @@ struct t8_scheme element_get_tree_face (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_tree_face (args...); }, element, - face); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_tree_face (args...); }, + element, face); }; /** Suppose we have two trees that share a common face f. @@ -832,8 +845,8 @@ struct t8_scheme const int orientation, const int sign, const int is_smaller_face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_transform_face (args...); }, elem1, - elem2, orientation, sign, is_smaller_face); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_transform_face (args...); }, + elem1, elem2, orientation, sign, is_smaller_face); }; /** Given a boundary face inside a root tree's face construct @@ -854,7 +867,7 @@ struct t8_scheme const int root_face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_extrude_face (args...); }, face, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_extrude_face (args...); }, face, element, root_face, this); }; @@ -874,8 +887,8 @@ struct t8_scheme t8_element_t *boundary) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_boundary_face (args...); }, element, - face, boundary, this); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_boundary_face (args...); }, + element, face, boundary, this); }; /** Construct the first descendant of an element at a given level that touches a given face. @@ -892,7 +905,8 @@ struct t8_scheme t8_element_t *first_desc, const int level) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_first_descendant_face (args...); }, + tree_class, + [] (const auto &scheme, const auto... args) { return scheme.element_get_first_descendant_face (args...); }, element, face, first_desc, level); }; @@ -910,7 +924,8 @@ struct t8_scheme t8_element_t *last_desc, const int level) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_last_descendant_face (args...); }, + tree_class, + [] (const auto &scheme, const auto... args) { return scheme.element_get_last_descendant_face (args...); }, element, face, last_desc, level); }; @@ -925,8 +940,8 @@ struct t8_scheme element_is_root_boundary (const t8_eclass_t tree_class, const t8_element_t *element, const int face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_root_boundary (args...); }, element, - face); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_is_root_boundary (args...); }, + element, face); }; /** Construct the face neighbor of a given element if this face neighbor @@ -950,7 +965,8 @@ struct t8_scheme const int face, int *neigh_face) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_face_neighbor_inside (args...); }, + tree_class, + [] (const auto &scheme, const auto... args) { return scheme.element_get_face_neighbor_inside (args...); }, element, neigh, face, neigh_face); }; @@ -965,7 +981,7 @@ struct t8_scheme element_get_shape (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_shape (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_shape (args...); }, element); }; /** Initialize the entries of an allocated element according to a @@ -981,8 +997,8 @@ struct t8_scheme const t8_linearidx_t id) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_set_linear_id (args...); }, element, - level, id); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_set_linear_id (args...); }, + element, level, id); }; /** Compute the linear id of a given element in a hypothetical uniform @@ -996,8 +1012,8 @@ struct t8_scheme element_get_linear_id (const t8_eclass_t tree_class, const t8_element_t *element, const int level) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_linear_id (args...); }, element, - level); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_linear_id (args...); }, + element, level); }; /** Compute the first descendant of a given element. @@ -1012,7 +1028,7 @@ struct t8_scheme const int level) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_first_descendant (args...); }, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_first_descendant (args...); }, element, desc, level); }; @@ -1028,7 +1044,7 @@ struct t8_scheme const int level) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_last_descendant (args...); }, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_last_descendant (args...); }, element, desc, level); }; @@ -1041,7 +1057,7 @@ struct t8_scheme element_construct_successor (const t8_eclass_t tree_class, const t8_element_t *element, t8_element_t *successor) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_construct_successor (args...); }, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_construct_successor (args...); }, element, successor); }; @@ -1061,8 +1077,8 @@ struct t8_scheme { return dispatch ( tree_class, - [] (const auto &scheme, auto... args) { return scheme.element_get_vertex_reference_coords (args...); }, element, - vertex, coords); + [] (const auto &scheme, const auto... args) { return scheme.element_get_vertex_reference_coords (args...); }, + element, vertex, coords); }; /** Convert points in the reference space of an element to points in the @@ -1080,7 +1096,7 @@ struct t8_scheme const size_t num_coords, double *out_coords) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_get_reference_coords (args...); }, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_get_reference_coords (args...); }, element, ref_coords, num_coords, out_coords); }; @@ -1100,7 +1116,8 @@ struct t8_scheme element_count_leaves (const t8_eclass_t tree_class, const t8_element_t *t, const int level) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_count_leaves (args...); }, t, level); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_count_leaves (args...); }, t, + level); }; /** Count how many leaf descendants of a given uniform level the root element will produce. @@ -1116,7 +1133,8 @@ struct t8_scheme count_leaves_from_root (const t8_eclass_t tree_class, const int level) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.count_leaves_from_root (args...); }, level); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.count_leaves_from_root (args...); }, + level); }; #if T8_ENABLE_DEBUG @@ -1141,7 +1159,7 @@ struct t8_scheme element_is_valid (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_is_valid (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_is_valid (args...); }, element); }; /** @@ -1155,7 +1173,8 @@ struct t8_scheme element_debug_print (const t8_eclass_t tree_class, const t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_debug_print (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_debug_print (args...); }, + element); }; #endif @@ -1171,7 +1190,7 @@ struct t8_scheme const int string_size) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_to_string (args...); }, element, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_to_string (args...); }, element, debug_string, string_size); }; @@ -1201,7 +1220,8 @@ struct t8_scheme element_new (const t8_eclass_t tree_class, const int length, t8_element_t **elements) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_new (args...); }, length, elements); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_new (args...); }, length, + elements); }; /** Initialize an array of allocated elements. @@ -1223,7 +1243,8 @@ struct t8_scheme element_init (const t8_eclass_t tree_class, const int length, t8_element_t *elements) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_init (args...); }, length, elements); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_init (args...); }, length, + elements); }; /** Deinitialize an array of allocated elements. @@ -1239,7 +1260,8 @@ struct t8_scheme element_deinit (const t8_eclass_t tree_class, const int length, t8_element_t *elements) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_deinit (args...); }, length, elements); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_deinit (args...); }, length, + elements); }; /** Deallocate an array of elements. @@ -1255,7 +1277,8 @@ struct t8_scheme element_destroy (const t8_eclass_t tree_class, const int length, t8_element_t **elements) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_destroy (args...); }, length, elements); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_destroy (args...); }, length, + elements); }; /** create the root element @@ -1266,7 +1289,7 @@ struct t8_scheme set_to_root (const t8_eclass_t tree_class, t8_element_t *element) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.set_to_root (args...); }, element); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.set_to_root (args...); }, element); }; /** Pack multiple elements into contiguous memory, so they can be sent via MPI. @@ -1283,8 +1306,8 @@ struct t8_scheme void *send_buffer, int buffer_size, int *position, sc_MPI_Comm comm) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_MPI_Pack (args...); }, elements, count, - send_buffer, buffer_size, position, comm); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_MPI_Pack (args...); }, elements, + count, send_buffer, buffer_size, position, comm); }; /** Determine an upper bound for the size of the packed message of \a count elements @@ -1297,8 +1320,8 @@ struct t8_scheme element_MPI_Pack_size (const t8_eclass_t tree_class, const unsigned int count, sc_MPI_Comm comm, int *pack_size) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_MPI_Pack_size (args...); }, count, comm, - pack_size); + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_MPI_Pack_size (args...); }, count, + comm, pack_size); }; /** Unpack multiple elements from contiguous memory that was received via MPI. @@ -1315,7 +1338,7 @@ struct t8_scheme t8_element_t **elements, const unsigned int count, sc_MPI_Comm comm) const { return dispatch ( - tree_class, [] (const auto &scheme, auto... args) { return scheme.element_MPI_Unpack (args...); }, recvbuf, + tree_class, [] (const auto &scheme, const auto... args) { return scheme.element_MPI_Unpack (args...); }, recvbuf, buffer_size, position, elements, count, comm); }; };