2
votes

Est-il possible de générer des variables constexpr au moment de la compilation pour déboguer les métaprogrammes de modèle?

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.

Si j'utilise static_assert a> pour afficher restN ou toute autre variable constexpr , mon programme s'arrêtera au moment de la compilation au point d'assertion avec une sortie que je prescrit. De plus, je ne sais pas encore comment écrire quoi que ce soit en dehors d'une chaîne littérale avec static_assert () .

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;
}


1 commentaires

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.


3 Réponses :


1
votes

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;

Démo


2 commentaires

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.



1
votes

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


2 commentaires

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)



0
votes

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>(); 
}


0 commentaires