如何在C ++中打印编译时计算的结果?

时间:2015-03-04 10:51:43

标签: c++ c++11 gcc constexpr static-assert

我已经编写了几个constexpr函数,并在static_asserts中使用它们来控制一些资源限制。但我不仅要强制执行编译时谓词,还要查看在正常编译过程中计算的实际值,或者至少在断言失败时。

有很多方法可以在编译期间打印字符串消息,但有关打印constexpr计算结果的方法是什么?

5 个答案:

答案 0 :(得分:6)

这是一些代码,利用gcc的诊断消息在断言消息后打印感兴趣的值。要查找感兴趣的值,您只需要搜索T x =的错误字符串:

#include <string>

template <class T, T x, class F>
void transparent(F f) { f(); }


template <bool B>
constexpr void my_assert() { 
    static_assert(B, "oh no");
}

template <int X>
void f() {
    transparent<int, X+7>([]{
        transparent<long, X*X*X>([]{
            my_assert<X+10==-89>(); });});
}

int main() {
//    f<3>();
    f<4>();
//    f<-99>();
}

这是我遇到的错误:

g++ h.cpp -std=c++11 h.cpp: In instantiation of ‘constexpr void my_assert() [with bool B = false]’: h.cpp:16:34: required from ‘f() [with int X = 4]::__lambda0::__lambda1’ h.cpp:15:35: required from ‘struct f() [with int X = 4]::__lambda0::__lambda1’ h.cpp:16:38: required from ‘f() [with int X = 4]::__lambda0’ h.cpp:14:28: required from ‘struct f() [with int X = 4]::__lambda0’ h.cpp:16:41: required from ‘void f() [with int X = 4]’ h.cpp:21:10: required from here h.cpp:9:5: error: static assertion failed: oh no static_assert(B, "oh no"); ^ h.cpp:4:6: error: ‘void transparent(F) [with T = long int; T x = 64l; F = f() [with int X = 4]::__lambda0::__lambda1]’, declared using local type ‘f() [with int X = 4]::__lambda0::__lambda1’, is used but never defined [-fpermissive] void transparent(F f) { f(); } ^ h.cpp:4:6: error: ‘void transparent(F) [with T = int; T x = 11; F = f() [with int X = 4]::__lambda0]’, declared using local type ‘f() [with int X = 4]::__lambda0’, is used but never defined [-fpermissive]

请注意粗体部分

答案 1 :(得分:1)

在至少Linux系统(可能还有其他)上,使用最新的GCC编译器(至少g++,2020年GCC 9),您可以开发自己的GCC plugin __builtin_compile_time_print到现有的GCC内置函数。或添加自己的#pragma_Pragma具有类似的副作用。

我开始(在CHARIOT的欧洲H2020资助下)从事Clips-rules-gcc项目。大概在2或3个月内,您就可以使用它来完成您想要的事情。请继续关注。

另请参见我过时的GCC MELT项目,该项目可能使您能够使用某些过时的4.6 GCC编译器来完成所需的工作。

您当然可以修补Clang/GCC来做类似的事情。

答案 2 :(得分:1)

@ je4d(https://stackoverflow.com/a/13465643/3353857)给出了答案,
该工具适用于godbolt上的msvc,gcc,clang和许多编译器,但较早的gcc 4.1.2

#define strcat_(x, y) x ## y
#define strcat(x, y) strcat_(x, y)
#define PRINT_VALUE(x) \
    template <int> \
    struct strcat(strcat(value_of_, x), _is); \
    static_assert(strcat(strcat(value_of_, x), _is)<x>::x, "");


#line 4242
constexpr int PI_INT = __LINE__;  /*set the value*/

PRINT_VALUE(PI_INT) /*print it*/

它将在错误消息中打印该值:

:4244:1:错误:类型'value_of_PI_INT_is << strong> 4242 >'不完整 用于嵌套名称说明符

答案 3 :(得分:0)

我的VC ++代码在编译期间打印出任意数量的编译时常量(例如sizeof结构)并继续编译而不会出现错误:

// cpptest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
//#define VALUE_TO_STRING2(x) #x
//#define VALUE_TO_STRING(x) VALUE_TO_STRING2(x)


#define TO_STRING(x) #x
#define FUNC_TEMPLATE_MSG(x,y) "[" x "]""["TO_STRING(y)"]"

template<unsigned int N,unsigned int M> 
int printN()
{ 
#pragma message(FUNC_TEMPLATE_MSG(__FUNCSIG__ ,1))

    return 0;
};



struct X {
    char a[20];
    int b;
};
struct Y {
    char a[210];
    int b;
};

int _tmain(int argc, _TCHAR* argv[])
{
printN<sizeof(X),__COUNTER__>();
printN<sizeof(Y),__COUNTER__>();
//..as many compile time constants as you like
}

VC ++ 2010生成的示例输出。目标值是第一个函数模板参数值(示例中为0x18和0xd8),VC ++奇怪地选择输出为十六进制值!!

1>------ Build started: Project: cpptest, Configuration: Release Win32 ------
1>  cpptest.cpp
1>  [int __cdecl printN<0x18,0x0>(void)][1]
1>  [int __cdecl printN<0xd8,0x1>(void)][1]
1>  Generating code
1>  Finished generating code
1>  cpptest.vcxproj -> c:\work\cpptest\Release\cpptest.exe
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

这里使用的主要技术是在每个函数模板实例化期间,#pragma message()指令被调用一次。 Microsoft特定宏__FUNCSIG__可以显示包含函数的签名,从而显示在函数模板的每个特定实例中使用的整数值。给COUNTER作为第二个模板参数是为了确保相同值的2个整数仍然被认为是不同的。 #pragma指令中的1在这里没有用,但是如果我们有超过1个这样的指令并且输出窗口充满了凌乱的消息,它可以用作标识符。

答案 4 :(得分:0)

这基于solution posted by @tohava

如果您需要在不使用static_assert()的情况下打印constexpr值,则适用于带有-Wunused标志的GCC编译器:

// before c++17:
template <typename T, T val>
constexpr void static_print() {
    #if !defined(__GNUC__) || defined(__clang__)
        int static_print_is_implemented_only_for_gcc = 0;
    #else
        int unused = 0;
    #endif
};

// for c++17 and higher:
template <auto val>
constexpr void static_print() {
    #if !defined(__GNUC__) || defined(__clang__)
        int static_print_is_implemented_only_for_gcc = 0;
    #else
        int unused = 0;
    #endif
};

int main() {
    constexpr int i = 13;
    // for c++17 and higher:
    static_print<i>();
    // before c++17:
    static_print<int, i>();
}

输出:

$ g++ -std=c++17 main.cpp -Wall && ./a
main.cpp: In instantiation of 'constexpr void static_print() [with auto val = 13]':
main.cpp:23:21:   required from here
main.cpp:17:7: warning: unused variable 'unused' [-Wunused-variable]
   int unused = 0;
       ^~~~~~
main.cpp: In instantiation of 'constexpr void static_print() [with T = int; T val = 13]':
main.cpp:24:26:   required from here
main.cpp:8:7: warning: unused variable 'unused' [-Wunused-variable]
   int unused = 0;
       ^~~~~~

重要的部分是val = 13

通过https://godbolt.org/z/Cdb-At

在线在线播放此示例

不幸的是,Clang编译器不包含警告消息的回溯,因此它不会输出该值。