To make a preorder traversal of a nested pack of types (i.e. a tree of types), and then carry out an action at each leaf, I've worked out the algorithm already (and tested to work correctly):
template <typename, typename> struct Visit;
template <typename, typename, bool> struct VisitHelper;
template <template <typename...> class P1, template <typename...> class P2, typename First, typename... Rest, typename... Visited>
struct Visit<P1<First, Rest...>, P2<Visited...>> :
VisitHelper<P1<First, Rest...>, P2<Visited...>, HasChildren<First>::value> {};
template <template <typename...> class P1, template <typename...> class P2, typename... Visited>
struct Visit<P1<>, P2<Visited...>> { // End of recursion. Every leaf in the tree visited.
using type = P2<Visited...>;
};
// Here First has children, so visit its children, after which visit Rest...
template <template <typename...> class P1, template <typename...> class P2, typename First, typename... Rest, typename... Visited>
struct VisitHelper<P1<First, Rest...>, P2<Visited...>, true> :
Visit<typename Merge<First, P2<Rest...>>::type, P1<Visited...>> {};
// Here First has no children, so the "leaf action" is carried out. In this case, it is appended to P<Visited...> as a simple example.
template <template <typename...> class P1, template <typename...> class P2, typename First, typename... Rest, typename... Visited>
struct VisitHelper<P1<First, Rest...>, P2<Visited...>, false> :
Visit<P1<Rest...>, typename Append<First, P2<Visited...>>::type> {};
The helper functions are self-explanatory. For completeness, here they are:
template <typename T>
struct HasChildren : std::false_type {};
template <template <typename...> class P, typename... Types>
struct HasChildren<P<Types...>> : std::true_type {};
template <typename, typename> struct Merge;
template <template <typename...> class P1, template <typename...> class P2, typename... Ts, typename... Us>
struct Merge<P1<Ts...>, P2<Us...>> {
using type = P1<Ts..., Us...>;
};
template <typename, typename> struct Append;
template <typename T, template <typename...> class P, typename... Ts>
struct Append<T, P<Ts...>> {
using type = P<Ts..., T>;
};
My "leaf action" here is simply storing the type of each node visited, as my test shows:
template <typename...> struct Pack {};
template <typename...> struct Group {};
template <typename...> struct Wrap {};
struct Object {};
int main() {
std::cout << std::boolalpha << std::is_same<
Visit<Pack<Wrap<int, Object, double>, bool, Group<char, Pack<int, double, Pack<char, Wrap<char, long, short>, int, Object>, char>, double>, long>, Pack<>>::type,
Pack<int, Object, double, bool, char, int, double, char, char, long, short, int, Object, char, double, long>
>::value << std::endl; // true
}
But my problem now is: What if there is to be an action carried out at each non-leaf, and such "non-leaf action" is carried out AFTER the children are visited? How to remember the non-leaf?
Here is my motivation for this. Consider this loop (which is carried out during run-time):
template <int N>
void Graph<N>::topologicalSortHelper (int v, std::array<bool, N>& visited, std::stack<int>& stack) {
visited[v] = true; // Mark the current node as visited.
for (int x : adjacentVertices[v]) // Repeat for all the vertices adjacent to this vertex.
if (!visited[x])
topologicalSortHelper (x, visited, stack);
stack.push(v); // Push current vertex to 'stack' which stores the result.
}
There are 2 "non-leaf actions" here. visited[v] = true;
is carried out before visiting the children, so that is no problem (just make the change in the inheritance), but the real problem is stack.push(v);
. How to remember v
after all the children have been visited?
So more generally, how to modify my code above so that an action at a non-leaf can be carried out after the children have been visited (all done during compile-time).
Aucun commentaire:
Enregistrer un commentaire