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

C++ 如何在C+中打印编译时计算的结果+;?,c++,c++11,gcc,constexpr,static-assert,C++,C++11,Gcc,Constexpr,Static Assert,我已经编写了几个constexpr函数,并在静态_断言中使用它们来控制一些资源限制。但我不仅希望强制执行编译时谓词,还希望看到在正常编译过程中或至少在断言失败时计算的实际值 编译过程中可以打印字符串消息,但是打印constexpr计算结果有什么用呢?下面是一些代码,它利用gcc的诊断消息在断言消息之后打印感兴趣的值。要查找感兴趣的值,只需在错误字符串中搜索tx=: #include <string> template <class T, T x, class F> vo

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


编译过程中可以打印字符串消息,但是打印constexpr计算结果有什么用呢?

下面是一些代码,它利用gcc的诊断消息在断言消息之后打印感兴趣的值。要查找感兴趣的值,只需在错误字符串中搜索
tx=

#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>();
}
#包括
模板
void transparent(F){F();}
模板
constexpr void my_assert(){
静态断言(B,“哦,不”);
}
模板
void f(){
透明([]{
透明([]{
我的_assert();});});
}
int main(){
//f();
f();
//f();
}
以下是它带给我的错误:

g++h.cpp-std=c++11
h、 cpp:在“constexpr void my_assert()[with bool B=false]”的实例化中:
h、 cpp:16:34:从'f()[整数X=4]中必需::_lambda0::_lambda1'
h、 cpp:15:35:在“结构f()[具有int X=4]:::_lambda0::_lambda1”中为必填项
h、 cpp:16:38:从'f()[整数X=4]中需要:\uu lambda0'
h、 cpp:14:28:从'struct f()
h、 cpp:16:41:从“void f()[整数X=4]开始需要”
h、 cpp:21:10:此处为必填项
h、 cpp:9:5:错误:静态断言失败:哦,不
静态断言(B,“哦,不”);
^
h、 cpp:4:6:错误:“void transparent(F)[with T=long int;tx=64l;F=F()[with int x=4]:::::_lambda0::_lambda1]”,使用局部类型“F()[with int x=4]::_lambda0::_lambda1]声明,但从未定义[-fpermissive]
void transparent(F){F();}
^
h、 cpp:4:6:错误:“void transparent(F)[with T=int;tx=11;F=F()[with int x=4]:::_lambda0]”,使用本地类型“F()[with int x=4]::::_lambda0]声明,但从未定义[-fpermissive]

请注意,粗体部分

我的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
}
//cpptest.cpp:定义控制台应用程序的入口点。
//
#包括“stdafx.h”
//#定义值_至_STRING2(x)#x
//#定义值\u到\u字符串(x)值\u到\u字符串2(x)
#定义为_字符串(x)#x
#定义函数模板消息(x,y)“[“x”]”[“到字符串(y)””
模板
int printN()
{ 
#pragma消息(FUNC_TEMPLATE_MSG(uu FUNCSIG_uu,1))
返回0;
};
结构X{
chara[20];
int b;
};
结构{
chara[210];
int b;
};
int _tmain(int argc,_TCHAR*argv[]
{
printN();
printN();
//…任意多个编译时常量
}
VC++2010生成的示例输出。目标值是VC++奇怪地选择输出为十六进制值的第一个函数模板参数值(示例中为0x18和0xd8)

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 ==========
1>----构建已启动:项目:cpptest,配置:发布Win32------
1> cpptest.cpp
1> [int\uu cdecl打印(无效)][1]
1> [int\uu cdecl打印(无效)][1]
1> 生成代码
1> 已完成生成代码
1> cpptest.vcxproj->c:\work\cpptest\Release\cpptest.exe
=======生成:1成功,0失败,0最新,0跳过==========
这里使用的主要技术是,在每个函数模板实例化期间,调用一次
#pragma message()
指令。Microsoft特定宏
\uuuu FUNCSIG\uuuu
可以显示包含函数的签名,从而显示函数模板的每个特定实例化中使用的整数值。将
计数器
作为第二个模板参数是为了确保相同值的2个整数仍然被认为是不同的。
#pragma
指令中的1在这里没有用处,但可以用作标识符,以防我们有超过1个这样的指令,并且输出窗口充满杂乱的消息。

这是基于:

如果需要打印不带
static_assert()
的constepr值,这适用于带有
-Wunused
标志的GCC编译器:

//在c++17之前:
模板
constexpr void static_print(){
#如果!已定义(uu GNUC_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
int static\u print\u仅在\u gcc=0时实现;
#否则
int=0;
#恩迪夫
};
//对于c++17及更高版本:
模板

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

对于最新的编译器(
g++
,至少在2020年),至少在Linux系统上(可能还有其他系统上)您可以将
\uuuuuuBuiltin\uCompile\uTime\uPrint
添加到现有的GCC内置函数中。或者添加您自己的具有类似副作用的
#pragma
\u pragma

我开始(利用欧洲H2020基金)参与该项目。可能在2到3个月内,你就可以用它来做你想做的事情。请继续收看

另请参阅我的过时项目,它可以让您使用一些过时的4.6 GCC编译器做您想做的事情


当然你也可以做类似的事情。

有一个@je4d()的答案,
它适用于msvc、gcc、clang以及除古代GCC4.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*/
#定义strcat(x,y)x##y
#定义strcat(x,y)strcat(x,y)
#定义打印值(x)\
模板\
结构strcat(strcat(x的值),u为\
静态断言(strcat(strcat(x的值)::x,“”;
#第4242行
constexpr int PI_int=_线_;/*设置值*/
打印值(PI_INT)/*打印它*/
它将打印错误消息中的值:

:4244:1:错误:不完整的类型'value\u of_PI\u INT\u is' 在嵌套名称说明符中使用


Shame
static\u assert
的消息参数ca
#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*/