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

Is it possible to output constexpr variables at compile time to debug template metaprograms?

本文关键字:调试 程序 变量 constexpr 编译 输出 是否      更新时间:2023-10-16

我正在调试一个元函数,该函数迭代可变参数并检查对(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>()); 
};

至于在编译类型处显示类型以进行调试,您可以使用以下值实例化非完整类型:

template <int> struct debug_int;

然后:

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

简单演示
演示您的案例

顺便说一句,您的taggedTypes方法可以简化为:

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

演示

这可能比您希望的要多,但是您还可以将一个补丁应用于GCC,以启用执行您正在寻找的static_print语句。

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

上述程序打印出来(在编译时): The template test has been instantiated as test<int, 3>. By the way, s + 1 is 4

我挖了

一下,发现了一个可能很丑陋的解决方案,它不会停止编译,也不需要补丁。我正在使用一个元函数,它会导致编译器警告标记。例如,对于 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]’: