GCC constexpr allows add but not bitwise-or with address - c++

Consider this code:
#include <cstdint>
static int x = 0;
const uintptr_t arithmetic()
{
static constexpr uintptr_t result = ((uintptr_t)&x) + 1u;
return result;
}
const uintptr_t bitwise()
{
static constexpr uintptr_t result = ((uintptr_t)&x) | 1u;
return result;
}
GCC (all versions 4-9) compiles arithmetic() just fine, but rejects bitwise():
<source>: In function 'const uintptr_t bitwise()':
<source>:13:57: error: '(((uintptr_t)(& x)) | 1)' is not a constant expression
13 | static constexpr uintptr_t result = ((uintptr_t)&x) | 1u;
| ~~~~~~~~~~~~~~~~^~~~
Why? Note that bitwise-or works fine in other constexpr use cases, but not this one.
Demo: https://godbolt.org/z/x5jbuU

You can’t use reinterpret_cast (or a C-style cast that performs one) in a constant expression at all. GCC either has a bug enforcing that, or is trying to be helpful to support some practical use case where + but not | is relevant.

Related

constexpr uintptr_t : expression must have constant value

I just learned about constexpr in C++ and intended to use it in a project.
But I already stamble on a compilor error : expression must have constant value with this code :
constexpr uintptr_t addr1 = 0x00400000;
constexpr uintptr_t addr2 = addr1 + 0x0346DC48;
As uintptr_t is just a typedef unsigned int uintptr_t, I tried with unsigned int instead and didn't get the error anymore :
constexpr unsigned int addr1 = 0x00400000;
constexpr uintptr_t addr2 = addr1 + 0x0346DC48;
Anyone knows why and is willing to explain it ?
Edit1 : This is for 32 bit build
Edit2 : Adding an import like windows.h, iostream or stdlib.h solves the issue (I tested many includes and they all solved the issue). But I don't understand why.

Ternary operator evaluation rules in templates different?

Given this innocent snippet:
#include <cstdint>
template <unsigned int n> constexpr uint64_t bit = (1ull << n);
template <unsigned int n> constexpr uint64_t mask = (n == 64) ? ~0ull : bit<n> - 1;
namespace this_works_fine
{
template <unsigned int n> constexpr uint64_t bit = (1ull << n);
template <unsigned int n> constexpr uint64_t mask = []() constexpr { if constexpr (n == 64) return ~0ull; else return bit<n> - 1; }();
}
int main()
{
auto a = mask<64>;
(void)a;
}
... I expected that to "just work, zero errors, zero warnings". It's quite clear and simple and there's not much room for doing something wrong. The only thing to be aware of is that shifting more than an integer's width is UB (happens for N == 64), but that is being explicitly taken care of. It'll probably produce a warning/error for values larger than 64, but that's fine, no need for an explicit error check.
The conditional operator only evaluates either the second or the third operand based on the first operand's evaluation. So as long as the code altogether is in principle syntactically correct, we're good to go.
Now, GCC (9.1.0) tells me the following:
g++.exe -Wall -fexceptions -O2 --std=c++17 -c main.cpp -o obj\main.o
g++.exe -o lib\gcc-bug.exe obj\main.o -s
main.cpp: In instantiation of 'constexpr const uint64_t bit<64>':
main.cpp:4:73: required from 'constexpr const uint64_t mask<64>'
main.cpp:14:12: required from here
main.cpp:3:59: error: right operand of shift expression '(1 << 64)' is >= than the precision of the left operand [-fpermissive]
3 | template <unsigned int n> constexpr uint64_t bit = (1ull << n);
| ~~~~~~^~~~~
The exact same thing rewritten with if constexpr() instead compiles (and, of course, works) without any trouble. No error, no warning. No surprise. Why wouldn't it work!
While I was about to submit a bug report to GCC which is "obviously broken", it occurred to me that I might first check with version 9.2 (which isn't available for MinGW yet) as well as trunk on Godbolt, and while we're at it with Clang as well since that's just one more click.
Unsurprisingly, the other GCC versions produce the same error, but much to my surprise, Clang doesn't compile it either. It claims that (1ull << n) is not a constant expression. Which is another story, but equally stunning.
So I'm a bit unsettled there. It seems like I am not understanding the rules of the conditional operator correctly? Is there any special exception for templates or template variables where it evaluates differently?
When you are using the if constexpr then this part of the code
else return bit<n> - 1;
is not instantiated when n is equal to 64.
From the C++ Standard (9.4.1 The if statement)
2 If the if statement is of the form if constexpr, the value of the
condition shall be a contextually converted constant expression of
type bool (8.6); this form is called a constexpr if statement. If the
value of the converted condition is false, the first substatement is a
discarded statement, otherwise the second substatement, if present, is
a discarded statement. During the instantiation of an enclosing
templated entity (Clause 17), if the condition is not
value-dependent after its instantiation, the discarded substatement
(if any) is not instantiated.
Opposite to this code all parts of the code
template <unsigned int n> constexpr uint64_t mask = (n == 64) ? ~0ull : bit<n> - 1;
are instantiated. So the compiler issues an error.
Just try the following semantically equivalent code and you will get the same error.
#include <cstdint>
template <unsigned int n> constexpr uint64_t bit = (1ull << n);
template uint64_t bit<64>;
int main()
{
}

GCC and -Wconversion

Let's compile the following program:
int main()
{
uint16_t data = 0;
data |= uint16_t(std::round(3.14f));
return 0;
}
with g++ -Wconversion prog.cpp
We'll get warning: conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value, but I can't see implicit conversions here.
This kind of warnings should be muted by explicit casts, for example:
double d = 3.14;
float foo1 = d; // Warning
float foo2 = float(d); // No warning
float foo2 = static_cast<float>(d); // No warning
Is GCC right here or it's a bug?
Note that my snippet is minimal. For example, warning disappears in following cases:
remove the f suffix from 3.14, i.e. make it double
use assignment instead of |=
remove std::round
cache rounding result: const auto r = uint16_t(std::round(3.14f));, then or-assign it to data.
Is GCC right here or it's a bug?
Since the behavior does not match the expectation, I'd call it a bug.
From https://godbolt.org/z/aSj--7, it seems that in GCC's eye, data |= uint16_t(std::round(3.14f)) is translated as
(void) (data = TARGET_EXPR <D.2364, (uint16_t) round (3.1400001049041748046875e+0)>;, data | NON_LVALUE_EXPR <D.2364>;)
(TARGET_EXPR represents a temporary object. D.2364 is an internal variable name.)
Translate GCC's internal language back to C++, and we'll get
data = (temp = (uint16_t) round (3.14e+0), data | temp)
Since the LHS of the comma expression does not affect the RHS, this should be as safe as data = data | temp. However, GCC warns at the former but not at the latter, which is very unlikely to be intentional. Thus I believe that it is an oversight of GCC maintainers.
The warning is bogus.
According to [over.built]/22:
For every triple (L, VQ, R), where L is an integral type, VQ is either volatile or empty, and R is a promoted integral type, there exist candidate operator functions of the form ...
VQ L& operator|=(VQ L&, R);
So we get a built-in unsigned short operator |=(unsigned short&, unsigned int);
There are no implicit conversions in the given expression
uint16_t data = 0;
data |= uint16_t(std::round(3.14f));

c++ auto type signed to/from unsigned conversion

I want to write a function that performs bit-wise operations on a parameter that is auto type.
The type passed in might be unsigned int or int type (with differing widths).
I only want to perform bit-wise operations on unsigned types.
I need an operator that returns the unsigned version of the original data type. In the function example below the "operator" unsigned_type would give me the data type that value has but insure it is unsigned.
int -> unsigned int
int16_t -> uint16_t
uint16_t -> uint16_t
Function example:
auto bit_shifting_and_mask(auto value) -> decltype(value)
{
unsigned_type(value) unsigned_value = static_cast<unsigned_type(value)>(value);
unsigned_value >>= 8u; // Contrived bit manipulation
unsigned_value &= 0xABCDu; // here ...
return static_cast<decltype(value)>(unsigned_value);
}
Is there some means to perform the operation unsigned_type against a data type obtained from decltype?
Thanks.
C++11 has a std::make_unsigned utility in <type_traits>:
auto bit_shifting_and_mask(auto value) -> decltype(value)
{
auto unsigned_value = static_cast<std::make_unsigned<decltype(value)>::type>(value);
unsigned_value >>= 8u; // Contrived bit manipulation
unsigned_value &= 0xABCDu; // here ...
return static_cast<decltype(value)>(unsigned_value);
}
With C++14, you can simplify further by using std::make_unsigned_t instead of std::make_unsigned::type.
make_unsigned, as Jarod42 said.
auto bit_shifting_and_mask(auto value) -> decltype(value)
This is not the way you want to make this function type-dependent. Use template, unless this function is lambda.
This does not require features not (yet) in the standard. It compiles under VS 2017 with VC++17 enabled.
#include <type_traits>
template<typename T>
auto bit_shifting_and_mask(T value) {
static_assert(std::is_integral_v<T>,
"bit_shifting_and_mask(value): value is not integral type");
using unsgn =std::make_unsigned_t<T>;
auto unsigned_value = static_cast<unsgn>(value);
unsigned_value >>= 8u; // Contrived bit manipulation
unsigned_value &= 0xABCDu; // here ...
return unsigned_value;
}

constexpr and endianness

A common question that comes up from time to time in the world of C++ programming is compile-time determination of endianness. Usually this is done with barely portable #ifdefs. But does the C++11 constexpr keyword along with template specialization offer us a better solution to this?
Would it be legal C++11 to do something like:
constexpr bool little_endian()
{
const static unsigned num = 0xAABBCCDD;
return reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD;
}
And then specialize a template for both endian types:
template <bool LittleEndian>
struct Foo
{
// .... specialization for little endian
};
template <>
struct Foo<false>
{
// .... specialization for big endian
};
And then do:
Foo<little_endian()>::do_something();
New answer (C++20)
c++20 has introduced a new standard library header <bit>.
Among other things it provides a clean, portable way to check the endianness.
Since my old method relies on some questionable techniques, I suggest anyone who uses it to switch to the check provided by the standard library.
Here's an adapter which allows to use the new way of checking endianness without having to update the code that relies on the interface of my old class:
#include <bit>
class Endian
{
public:
Endian() = delete;
static constexpr bool little = std::endian::native == std::endian::little;
static constexpr bool big = std::endian::native == std::endian::big;
static constexpr bool middle = !little && !big;
};
Old answer
I was able to write this:
#include <cstdint>
class Endian
{
private:
static constexpr uint32_t uint32_ = 0x01020304;
static constexpr uint8_t magic_ = (const uint8_t&)uint32_;
public:
static constexpr bool little = magic_ == 0x04;
static constexpr bool middle = magic_ == 0x02;
static constexpr bool big = magic_ == 0x01;
static_assert(little || middle || big, "Cannot determine endianness!");
private:
Endian() = delete;
};
I've tested it with g++ and it compiles without warnings. It gives a correct result on x64.
If you have any big-endian or middle-endian proccesor, please, confirm that this works for you in a comment.
It is not possible to determine endianness at compile time using constexpr (before C++20). reinterpret_cast is explicitly forbidden by [expr.const]p2, as is iain's suggestion of reading from a non-active member of a union. Casting to a different reference type is also forbidden, as such a cast is interpreted as a reinterpret_cast.
Update:
This is now possible in C++20. One way (live):
#include <bit>
template<std::integral T>
constexpr bool is_little_endian() {
for (unsigned bit = 0; bit != sizeof(T) * CHAR_BIT; ++bit) {
unsigned char data[sizeof(T)] = {};
// In little-endian, bit i of the raw bytes ...
data[bit / CHAR_BIT] = 1 << (bit % CHAR_BIT);
// ... corresponds to bit i of the value.
if (std::bit_cast<T>(data) != T(1) << bit)
return false;
}
return true;
}
static_assert(is_little_endian<int>());
(Note that C++20 guarantees two's complement integers -- with an unspecified bit order -- so we just need to check that every bit of the data maps to the expected place in the integer.)
But if you have a C++20 standard library, you can also just ask it:
#include <type_traits>
constexpr bool is_little_endian = std::endian::native == std::endian::little;
Assuming N2116 is the wording that gets incorporated, then your example is ill-formed (notice that there is no concept of "legal/illegal" in C++). The proposed text for [decl.constexpr]/3 says
its function-body shall be a compound-statement of the form
{ return expression; }
where expression is a potential constant expression (5.19);
Your function violates the requirement in that it also declares a local variable.
Edit: This restriction could be overcome by moving num outside of the function. The function still wouldn't be well-formed, then, because expression needs to be a potential constant expression, which is defined as
An expression is a potential constant expression if it is a constant
expression when all occurrences of function parameters are replaced
by arbitrary constant expressions of the appropriate type.
IOW, reinterpret_cast<const unsigned char*> (&num)[0] == 0xDD would have to be a constant expression. However, it is not: &num would be a address constant-expression (5.19/4). Accessing the value of such a pointer is, however, not allowed for a constant expression:
The subscripting operator [] and the class member access . and
operators, the & and * unary operators, and pointer casts (except dynamic_casts, 5.2.7) can be used in the creation of an
address constant expression, but the value of an object shall not be accessed by the use of these operators.
Edit: The above text is from C++98. Apparently, C++0x is more permissive what it allows for constant expressions. The expression involves an lvalue-to-rvalue conversion of the array reference, which is banned from constant expressions unless
it is applied to an lvalue of effective integral type that refers
to a non-volatile const variable or static data member initialized
with constant expressions
It's not clear to me whether (&num)[0] "refers to" a const variable, or whether only a literal num "refers to" such a variable. If (&num)[0] refers to that variable, it is then unclear whether reinterpret_cast<const unsigned char*> (&num)[0] still "refers to" num.
There is std::endian in the upcoming C++20.
#include <bit>
constexpr bool little_endian() noexcept
{
return std::endian::native == std::endian::little;
}
My first post. Just wanted to share some code that I'm using.
//Some handy defines magic, thanks overflow
#define IS_LITTLE_ENDIAN ('ABCD'==0x41424344UL) //41 42 43 44 = 'ABCD' hex ASCII code
#define IS_BIG_ENDIAN ('ABCD'==0x44434241UL) //44 43 42 41 = 'DCBA' hex ASCII code
#define IS_UNKNOWN_ENDIAN (IS_LITTLE_ENDIAN == IS_BIG_ENDIAN)
//Next in code...
struct Quad
{
union
{
#if IS_LITTLE_ENDIAN
struct { std::uint8_t b0, b1, b2, b3; };
#elif IS_BIG_ENDIAN
struct { std::uint8_t b3, b2, b1, b0; };
#elif IS_UNKNOWN_ENDIAN
#error "Endianness not implemented!"
#endif
std::uint32_t dword;
};
};
Constexpr version:
namespace Endian
{
namespace Impl //Private
{
//41 42 43 44 = 'ABCD' hex ASCII code
static constexpr std::uint32_t LITTLE_{ 0x41424344u };
//44 43 42 41 = 'DCBA' hex ASCII code
static constexpr std::uint32_t BIG_{ 0x44434241u };
//Converts chars to uint32 on current platform
static constexpr std::uint32_t NATIVE_{ 'ABCD' };
}
//Public
enum class Type : size_t { UNKNOWN, LITTLE, BIG };
//Compare
static constexpr bool IS_LITTLE = Impl::NATIVE_ == Impl::LITTLE_;
static constexpr bool IS_BIG = Impl::NATIVE_ == Impl::BIG_;
static constexpr bool IS_UNKNOWN = IS_LITTLE == IS_BIG;
//Endian type on current platform
static constexpr Type NATIVE_TYPE = IS_LITTLE ? Type::LITTLE : IS_BIG ? Type::BIG : Type::UNKNOWN;
//Uncomment for test.
//static_assert(!IS_LITTLE, "This platform has little endian.");
//static_assert(!IS_BIG, "This platform has big endian.");
//static_assert(!IS_UNKNOWN, "Error: Unsupported endian!");
}
That is a very interesting question.
I am not Language Lawyer, but you might be able to replace the reinterpret_cast with a union.
const union {
int int_value;
char char_value[4];
} Endian = { 0xAABBCCDD };
constexpr bool little_endian()
{
return Endian[0] == 0xDD;
}
This may seem like cheating, but you can always include endian.h... BYTE_ORDER == BIG_ENDIAN is a valid constexpr...
Here is a simple C++11 compliant version, inspired by #no-name answer:
constexpr bool is_system_little_endian(int value = 1) {
return static_cast<const unsigned char&>(value) == 1;
}
Using a default value to crank everything on one line is to meet C++11 requirements on constexpr functions: they must only contain a single return statement.
The good thing with doing it (and testing it!) in a constexpr context is that it makes sure that there is no undefined behavior in the code.
On compiler explorer here.
If your goal is to insure that the compiler optimizes little_endian() into a constant true or false at compile-time, without any of its contents winding up in the executable or being executed at runtime, and only generating code from the "correct" one of your two Foo templates, I fear you're in for a disappointment.
I also am not a language lawyer, but it looks to me like constexpr is like inline or register: a keyword that alerts the compiler writer to the presence of a potential optimization. Then it's up to the compiler writer whether or not to take advantage of that. Language specs typically mandate behaviors, not optimizations.
Also, have you actually tried this on a variety of C++0x complaint compilers to see what happens? I would guess most of them would choke on your dual templates, since they won't be able to figure out which one to use if invoked with false.