Je débogue une métafonction qui itère sur un paramètre de modèle variadique et vérifie les paires ( Type
, Tag
) pour voir si chaque Type
est marqué avec le Tag
correspondant:
#include <cassert> #include <type_traits> #include <cstddef> struct fruit_tag {}; struct veggie_tag {}; template<typename T> struct tag; template<typename T, typename Tag> constexpr bool taggedType() { constexpr bool sameTypes = std::is_same<typename tag<T>::type, Tag>(); static_assert(sameTypes); return sameTypes; } template<typename Type, typename Tag, typename ... Rest> constexpr bool taggedTypes() { constexpr std::size_t restN = sizeof ...(Rest); static_assert(restN % 2 == 0, "Odd number of (Type, Tag) pairs."); constexpr bool pairDoesntMatch = ! taggedType<Type, Tag>(); if constexpr (pairDoesntMatch) return false; // Single pair, empty Rest, pair matches. if (restN == 0) return true; // Many pairs, test further. if (restN > 2) taggedTypes<Rest...>(); return true; } class Orange {}; template<> struct tag<Orange> { using type = fruit_tag; }; class Apple {}; template<> struct tag<Apple> { using type = fruit_tag; }; class Turnip{}; template<> struct tag<Turnip> { using type = veggie_tag; }; int main() { static_assert(taggedTypes<Turnip, veggie_tag, Orange, fruit_tag>()); };
Quelque chose ne va pas avec mon code, et je veux le déboguer.
Comment puis-je faire en sorte que le métaprogramme itère sur le paramètre de modèle variadique et en sortie les éléments dont j'ai besoin pour le débogage?
L'exemple complet:
template<typename Type, typename Tag, typename ... Rest> constexpr bool taggedTypes() { constexpr std::size_t restN = sizeof ...(Rest); static_assert(restN % 2 == 0, "Odd number of (Type, Tag) pairs."); constexpr bool pairDoesntMatch = ! taggedType<Type, Tag>(); if constexpr (pairDoesntMatch) return false; // Single pair, empty Rest, pair matches. if (restN == 0) return true; // More than two pairs, test further. if (restN > 2) taggedTypes<Rest...>(); return true; }
3 Réponses :
Quant à l'affichage du type au type de compilation pour le débogage, vous pouvez instancier un type non complet en utilisant la valeur:
template <typename Tuple, std::size_t ... Is> constexpr bool taggedTypes(std::index_sequence<Is...>) { return (std::is_same<typename tag<std::tuple_element_t<2 * Is, Tuple>>::type, std::tuple_element_t<2 * Is + 1, Tuple>>::value && ...); } template <typename ... Ts> constexpr bool taggedTypes() { constexpr std::size_t size = sizeof ...(Ts); //[[maybe_unused]]debug_odd<size> debug{}; static_assert(size % 2 == 0, "Odd number of (Type, Tag) pairs."); return taggedTypes<std::tuple<Ts...>>(std::make_index_sequence<size / 2>{}); }
puis:
constexpr int magic = 42; debug_int<magic>{}; // Compile error: invalid use of incomplete type 'struct debug_int<42>'
Démo simple
Démo sur votre cas
BTW, vos taggedTypes
peut être simplifiée à:
template <int> struct debug_int;
Cool, mais cela donne une erreur de compilation, qui s'arrête là. Je me demande s'il existe quelque chose comme l'impression au moment de la compilation, que je peux utiliser pour le débogage.
@tmaric: pas du standard, 0x5453 fournit un lien vers un patch pour que gcc le fasse.
C'est probablement plus d'efforts que vous ne l'espériez, mais il existe également un patch que vous pouvez appliquer à GCC pour activer une instruction static_print
qui fait ce que vous recherchez.
template<typename T, int s> struct test { static_print("The template ", ::test, " has been instantiated as ", test, ". By the way, s + 1 is ", s + 1); }; int main() { test<int, 3> y; return 0; }
La compilation du programme ci-dessus s'imprime (au moment de la compilation):
Le modèle de test a été instancié en tant que test
. Au fait, s + 1 vaut 4
L'effort pourrait porter ses fruits s'il accélère le débogage, mais devrais-je m'inquiéter de quelque manière que ce soit de la mise à jour de GCC?
@tmaric vous utiliseriez un gcc non corrigé pour les builds sans débogage. Appliquez les protections habituelles de «logiciels non approuvés» comme bon vous semble (par exemple, exécutez dans une machine virtuelle jetable)
J'ai fouillé un peu et j'ai trouvé une solution probablement moche qui n'arrête pas la compilation et ne nécessite pas de patch. J'utilise une métafonction qui provoque le signalement d'un avertissement du compilateur. Par exemple, avec gcc, -Wbool-compare
peut être utilisé comme ceci, pour afficher les résultats des calculs à la compilation:
$ mainmake 2>&1 | grep -Ev 'recursive|required|comparisons like|(0 < N < 100)' main.cpp: In function âconstexpr bool warning_print()â: main.cpp:4:19: warning: comparison of constant â100â with boolean expression is always true [-Wbool-compare] ~~~~~~^~~~~ main.cpp: In instantiation of âconstexpr bool warning_print() [with int N = 5]â: ~~^~~ main.cpp: In instantiation of âconstexpr bool warning_print() [with int N = 6]â: main.cpp: In instantiation of âconstexpr bool warning_print() [with int N = 7]â: main.cpp: In instantiation of âconstexpr bool warning_print() [with int N = 8]â: main.cpp: In instantiation of âconstexpr bool warning_print() [with int N = 9]â:
Cela donne (avec grep sous Linux ):
template<int N> constexpr bool warning_print() { return (0 < N < 100); } template<int N, int M> constexpr void iterate() { warning_print<N>(); if constexpr (N + 1 < M) iterate<N+1, M>(); return; } using namespace std; int main() { iterate<5, 10>(); }
Je n'ai pas lu ceci attentivement, donc ce n'est pas une réponse, mais cette page traite Technique d'Erwin Unruh pour générer des messages d'erreur avec des nombres premiers au moment de la compilation. La page traite également de ce qui prétend être une mise à jour C ++ 17 de la technique.