I'm testing the code below:
#define TO_STRING(X) #X
#define TEST(X, Y) namespace Y { constexpr std::string_view name{#X}, raw_value{TO_STRING(X)};\
constexpr bool defined {name!=raw_value}; constexpr bool has_value{raw_value!=""}; }
#define A
#define B 1
TEST(A, a);
TEST(B, b);
TEST(C, c);
int main()
{
if constexpr (a::defined && a::has_value) {
std::cout << a::name << " = " << a::raw_value << '\n';
} else {
std::cout << a::name << " not defined or have no value\n";
}
if constexpr (b::defined && b::has_value) {
std::cout << b::name << " = " << b::raw_value << '\n';
} else {
std::cout << b::name << " not defined or have no value\n";
}
if constexpr (c::defined && c::has_value) {
std::cout << c::name << " = " << c::raw_value << '\n';
} else {
std::cout << c::name << " not defined or have no value\n";
}
return 0;
}
Which produces the following output:
A not defined or have no value B = 1 C not defined or have no value
If I modify the TEST
adding a new function:
#define TEST(X, Y) namespace Y { constexpr std::string_view name{#X}, raw_value{TO_STRING(X)};\
constexpr bool defined {name!=raw_value}; constexpr bool has_value{raw_value!=""};\
constexpr auto value() { if constexpr (defined && has_value) return X; else return; } }
I was expecting the compiler to ignore the return X
statement while expanding TEST(C, c)
macro, it reports an error instead:
error: use of undeclared identifier 'C' TEST(C, c); ^
Referencing an undeclared identifier is absolutely ill formed but I was expecting the compiler to ignore it because I thought that if constexpr
untaken branches didn't need to be well formed. Is there a way to achieve this behaviour?
(bold mine)
This explains the behavior of
if constexpr
inside of templates. Since nothing else is said about it, its behavior outside of templates matches the behavior of the plainif
.Now, if you're thinking about making your function a template by adding an unused template parameter, it's not going to work so easily:
(bold mine)