是否可以在编译时输出constexpr变量来调试模板元程序?

时间:2019-01-09 21:09:01

标签: c++ debugging c++17 variadic-templates template-meta-programming

我正在调试一个遍历可变参数模板参数并检查对(TypeTag)的元函数,以查看每个Type是否被相应的Tag标记:

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

我的代码出了点问题,我想调试它。

如果我使用static_assert输出restN或任何其他constexpr变量,我的程序将在编译时在断言时使用我指定的输出中断。另外,我还不清楚如何用static_assert()写下字符串文字以外的任何内容。

如何使元程序遍历可变参数模板参数并输出调试所需的内容?

完整的示例:

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

3 个答案:

答案 0 :(得分:1)

As for displaying type at compile type for debugging, you might instantiate a non complete type using the value:

template <int> struct debug_int;

and then:

constexpr int magic = 42;
debug_int<magic>{}; // Compile error: invalid use of incomplete type 'struct debug_int<42>'

Simple Demo
Demo on your case

BTW, your taggedTypes method can be simplified to:

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

Demo

答案 1 :(得分:1)

This is probably more effort than you were hoping for, but there is also a patch that you can apply to GCC to enable a static_print statement that does what you are looking for.

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

Compiling the above program prints out (at compile time): The template test has been instantiated as test<int, 3>. By the way, s + 1 is 4

答案 2 :(得分:0)

我仔细研究了一下,发现了一个可能丑陋的解决方案,该解决方案不会停止编译并且不需要补丁。我正在使用一个引起编译器警告标记的元函数。例如,对于gcc,-Wbool-compare可以这样使用,以输出编译时计算的结果:

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

这给出了(在Linux上使用grep):

$ 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]’: