【问题标题】:Is it possible to output constexpr variables at compile time to debug template metaprograms?是否可以在编译时输出 constexpr 变量来调试模板元程序?
【发布时间】:2019-01-09 21:09:01
【问题描述】:

我正在调试一个元函数,它遍历可变参数模板参数并检查对(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>()); 
};

【问题讨论】:

  • 我没有仔细阅读这篇文章,所以这不是一个答案,但this page 讨论了 Erwin Unruh 在编译时使用素数生成错误消息的技术。该页面还讨论了该技术的 C++17 更新。

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


【解决方案1】:

至于在编译类型时显示类型以进行调试,您可以使用该值实例化一个不完整的类型:

template <int> struct debug_int;

然后:

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

Simple Demo
Demo on your case

顺便说一句,您的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>{});
}

Demo

【讨论】:

  • 酷,但这会产生一个编译错误,并停在那里。我想知道是否有类似编译时打印的东西,可以用于调试。
  • @tmaric: 不是标准的,0x5453 为 gcc 提供了补丁链接。
【解决方案2】:

这可能比您希望的要多,但您也可以向 GCC 申请 patch 以启用满足您要求的 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&lt;int, 3&gt;. By the way, s + 1 is 4

【讨论】:

  • 如果加快调试速度,努力可能会得到回报,但我应该担心修补 GCC 吗?
  • @tmaric 您将使用未修补的 gcc 进行非调试构建。应用您认为合适的常规“不受信任的软件”保护措施(例如在一次性 VM 中运行)
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 2020-03-06
    • 2022-01-19
    • 2011-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多