einpoklum
einpoklum

Reputation: 131976

How to make my C++ compiler emit a constexpr value (e.g. in warning?)

(This is sort of an XY problem, but bear with me.)

I'm getting this compilation warning about a shift amount being too large. Now, to diagnose this, I would like my compiler to somehow emit the constexpr value which is used as a shift amount.

The way I've done it so far is try to instantiate a type with a numeric parameter which I know I can place out of range, then add the constexpr value I wanted and get an error which shows me the sum. But that's an ugly hack. Is there a way to get constexpr values (hopefully not only integers) to be emitted to the standard error stream? e.g. together with some explanatory text or a warning message?

I'm asking about GCC 6.x and later and clang 4.x and later.

Upvotes: 6

Views: 973

Answers (3)

user743382
user743382

Reputation:

GCC displays <<'s operands when it issues an error message for overflow in a constant expression. It does not display <<'s operands when it only issues a warning message, when the result is not used as a constant expression. You can make use of this by adding an otherwise pointless constant.

template <int> constexpr int f() { return 1; }
template <int> constexpr int g() { return 40; }
template <int I> constexpr int h() { return f<I>() << g<I>(); }
int main() { h<1234>(); }

This causes a warning without information about the problematic value: warning: left shift count >= width of type [-Wshift-count-overflow].

template <int> constexpr int f() { return 1; }
template <int> constexpr int g() { return 40; }
template <int I> constexpr int h() { constexpr int i = f<I>() << g<I>(); return f<I>() << g<I>(); }
int main() { h<1234>(); }

This causes an error with information about the problematic value (along with some more warnings): error: right operand of shift expression ‘(1 << 40)’ is >= than the precision of the left operand.

If only the second operand is a constant expression, it's still fine, for this particular warning it suffices to turn the left operand into a constant 1.

Upvotes: 1

einpoklum
einpoklum

Reputation: 131976

This is super-ugly, but produces both the name of the expression and its value in a discernable, though terrible, format:

constexpr int I = 8 % 3;

#define CONCATENATE( s1, s2 )               s1 ## s2
#define EXPAND_THEN_CONCATENATE( s1, s2 )   CONCATENATE( s1, s2 )

template<int i>
class The_expression_named_in_the_previous_error_has_value{ static_assert(i != i, ""); };


#define ERROR_PRINT(_expr) \
EXPAND_THEN_CONCATENATE(In_the_next_error_you_will_find_the_value_of_the_expression__, _expr); \
The_expression_named_in_the_previous_error_has_value<I>();

int main() {
    ERROR_PRINT(I);
}

This produces (with GCC 6):

main.cpp: In function ‘int main()’:
main.cpp:11:25: error: ‘In_the_next_error_you_will_find_the_value_of_the_expression__I’ was not declared in this scope
 EXPAND_THEN_CONCATENATE(In_the_next_error_you_will_find_the_value_of_the_expression__, _expr); \
                         ^
main.cpp:3:45: note: in definition of macro ‘CONCATENATE’
 #define CONCATENATE( s1, s2 )               s1 ## s2
                                             ^
main.cpp:11:1: note: in expansion of macro ‘EXPAND_THEN_CONCATENATE’
 EXPAND_THEN_CONCATENATE(In_the_next_error_you_will_find_the_value_of_the_expression__, _expr); \
 ^
main.cpp:15:5: note: in expansion of macro ‘ERROR_PRINT’
     ERROR_PRINT(I);
     ^
main.cpp: In instantiation of ‘class The_expression_named_in_the_previous_error_has_value<2>’:
main.cpp:15:5:   required from here
main.cpp:7:61: error: static assertion failed: 
 class The_expression_named_in_the_previous_error_has_value{ static_assert(i != i, ""); };

But I'm sure this can be massively improved upon with some constexpr-string-trickery.

Upvotes: 1

Sneftel
Sneftel

Reputation: 41503

Well, the obvious approach is similar to what you said -- make the compiler mention the value while emitting a diagnostic.

constexpr int I = 8 % 3;

template<int i>
class TheValueYouWantIs { static_assert(i != i); };


int main() {
    TheValueYouWantIs<I>();
}

Thus:

prog.cpp: In instantiation of ‘class TheValueYouWantIs<2>’:
prog.cpp:8:27:   required from here
[...less informative stuff...]

Warnings are obviously more compiler-dependent, but should be easily possible. This sort of thing won't help you with char arrays, though. Not a complete solution.

Upvotes: 1

Related Questions