How to print result of a compile-time calculation in C++?
Asked Answered
O

6

23

I've wrote several constexpr functions and use them in static_asserts to control some resource limits. But I'd like to not only enforce compile-time predicate but also to see the actual values calculated during normal compilation process or at least when assertion fails.

There are ways to print string messages during compilation, but what's about printing results of constexpr computations?

Outspeak answered 4/3, 2015 at 10:51 Comment(3)
Shame static_assert's message argument can't do this.Sula
you could make use of regular compiler diagnostics by forcing an incomplete type on condition failure, which should lead to template arguments being visible in the compiler output, like hereHelle
I can do this with VC++ (see my answer). Perhaps someone could try to see if the same technique can be used in gccTraci
B
11

Here is some code that exploits gcc's diagnostic messages to print values of interest after an assert message. To find the values of interest, you just need to search the error string for 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>();
}

Here is the error that it got me:

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; <b>T x = 64l</b>; 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; <b>T x = 11</b>; F = f() [with int X = 4]::__lambda0]’, declared using local type ‘f() [with int X = 4]::__lambda0’, is used but never defined [-fpermissive]

Note that bolded parts

Bassinet answered 4/3, 2015 at 12:45 Comment(1)
That works, thanks! Any idea how to emit a warning message so that compilation continues?Outspeak
A
8

This is based on the solution posted by @tohava:

If you need to print constexpr values without static_assert(), this works for the GCC compiler with the -Wunused flag:

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

Output:

$ 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;
       ^~~~~~

The important part is the val = 13.

Play with this example online at https://godbolt.org/z/Cdb-At

Unfortunately, the Clang compiler doesn't include backtrace of the warning message, so it doesn't output the value.

Agnosia answered 13/11, 2019 at 9:50 Comment(1)
Note that on gcc 10.2 I had to adjust the signature of the dummy static_print function slightly to make it return a (dummy) int. Otherwise I was running into a different compile error: specializing member '::static_print<...>' requires 'template<>' syntaxArtair
T
3

My VC++ code that print, during compilation, the value of as many compile time constants as you like (e.g. sizeof structures) and continue compiling without error:

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

Sample output produced by VC++2010. The target value is the first function template parameter value (0x18 and 0xd8 in the example) which VC++ oddly chose to output as hexadecimal value!!

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 ==========

The major technique used here is that a during each function template instantiation, the #pragma message() directive is invoked once. The Microsoft specific macro __FUNCSIG__ can display the signature of the containing function and thus the integer value used in each specific instantiation of the function template. Giving COUNTER as the second template parameter is to make sure 2 integers of the same value are still considered different. The 1 in the #pragma directive is of no use here but can be used as an identifier in case we have more than 1 such directive and the output window is full of messy messages.

Traci answered 25/5, 2017 at 14:59 Comment(0)
G
3

There's an answer by @je4d (https://mcmap.net/q/533318/-std-cout-equivalent-at-compile-time-or-static_assert-stringification-of-compile-time-constant-values-in-c-11),
which works for msvc, gcc, clang, and lots of compilers on godbolt except for the ancient 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*/

It prints the value in the error message:

:4244:1: error: incomplete type 'value_of_PI_INT_is<4242>' used in nested name specifier

Goldbrick answered 5/6, 2020 at 11:42 Comment(0)
H
1

With most recent GCC compilers (g++, in 2020 GCC 9 at least) on at least Linux systems (and probably others) you could develop your GCC plugin adding your __builtin_compile_time_print to the existing GCC builtin functions. Or add your own #pragma or _Pragma having a similar side effect.

I started working (with European H2020 funding from CHARIOT) on the Clips-rules-gcc project. Probably in 2 or 3 months you might be able to use it to do what you want. Please stay tuned.

See also my obsolete GCC MELT project, which could have enabled you to do what you want with some obsolete 4.6 GCC compiler.

Of course you could patch Clang/GCC to do similar things.

Hollinger answered 28/2, 2020 at 8:36 Comment(1)
Any update on this project ?Artair
O
0

I have noticed that most available answers introduce a function to do that. In light of these answers, I wrote my own version, which can be directly used anywhere a static_assert can be put.

template <int P, bool v>
struct Checker {
    static constexpr bool value = v;
};

static_assert(Checker<sizeof(XXX), false>::value);

In my code, P is something you want to print. In my case, it is sizeof(XXX). v is a bool which should always be set to false to fire the static_assert.

Oviparous answered 18/2, 2024 at 3:37 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.