Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Stripping cast from macro

86 views
Skip to first unread message

mark

unread,
Oct 6, 2016, 10:25:46 AM10/6/16
to
I have C header files with a bunch of memory mapped register definitions
like:

#define REGISTER (*(volatile uint32_t *)0x42424242)

GCC used to allow this kind of thing in constexpr, but is now randomly
enforcing it as forbidden reinterpret_cast. I want to turn this into an
uintptr_t that can be used in a constexpr. The only possible way seems
to be some nasty macro that can somehow do the transformation.

Any ideas?

(Why is it impossible in this so-called Systems Programming Language to
define a pointer to a fixed location? Every compiler does it's own
non-standard thing.)

mark

unread,
Oct 6, 2016, 10:39:25 AM10/6/16
to
On 2016-10-06 16:25, mark wrote:
>
> #define REGISTER (*(volatile uint32_t *)0x42424242)

In case that isn't clear, I want to end up with:

constexpr uintptr_t register_addr = 0x42424242;

David Brown

unread,
Oct 6, 2016, 11:12:58 AM10/6/16
to
What happens when you write:

#define REGISTER (*(volatile uint32_t *)0x42424242)
constexpr uintptr_t register_addr = (uintptr_t) &REGISTER;

or

constexpr uintptr_t register_addr2 =
reinterpret_cast<uintptr_t>(&REGISTER);


Both seem to be accepted by gcc in my (very brief) tests.


mark

unread,
Oct 6, 2016, 11:19:13 AM10/6/16
to
That's what I mean by GCC randomly enforcing constexpr restrictions.
Some cases still work, others are broken. This is explicitly forbidden
by the standard and GCC is moving towards more enforcement.

David Brown

unread,
Oct 6, 2016, 4:21:28 PM10/6/16
to
Can you give an example of where this does not work? Exact code, and
gcc version and target?

As far as I can tell, this sort of code is perfectly legal - gcc does
not get to "break" it, and it certainly should not be doing so "randomly".

It may be undefined behaviour to access anything using the
register_addr2 pointer unless you first convert it back to a "volatile
uint32_t *" pointer. Converting pointers back and forth from integers
is mostly implementation-defined behaviour, with little guaranteed
behaviour except that you can convert the pointer to a uintptr_t (if
that type exists), then back again to the original pointer type, and
have the same value as you started with.

Barry Schwarz

unread,
Oct 6, 2016, 7:06:41 PM10/6/16
to
If it is a C header file, why are you compiling it as C++? Not all C
code is valid C++ code.
--
Remove del for email

David Brown

unread,
Oct 7, 2016, 3:04:04 AM10/7/16
to
(Please don't top-post)

On 07/10/16 01:06, Barry Schwarz wrote:
> If it is a C header file, why are you compiling it as C++? Not all C
> code is valid C++ code.
>

C header files are usually perfectly valid C++ code, once you have
wrapped any function declarations in extern "C". In this particular
case, my guess is that the headers in question are for a
microcontroller, and contain a list of definitions of hardware
registers. Such headers are almost always referred to as "C", but will
be compatible with C++.

mark

unread,
Oct 7, 2016, 8:01:42 AM10/7/16
to
On 2016-10-06 22:21, David Brown wrote:
>>
>> That's what I mean by GCC randomly enforcing constexpr restrictions.
>> Some cases still work, others are broken. This is explicitly forbidden
>> by the standard and GCC is moving towards more enforcement.
>
> Can you give an example of where this does not work? Exact code, and
> gcc version and target?

--------------------------------------------------------------
#include <stdint.h>

#define PORT (*(volatile uint32_t*)(0x42424242))

constexpr volatile uint32_t* test(volatile uint32_t& x) {
return &x;
}

constexpr volatile uint32_t* addr = test(PORT);
--------------------------------------------------------------

Works with 4.7 and 4.8, doesn't compile with GCC >= 4.9 on x64 and ARM.

While your example:
--------------------------------------------------------------
#define REGISTER (*(volatile uint32_t *)0x42424242)
constexpr uintptr_t register_addr = (uintptr_t) &REGISTER;
--------------------------------------------------------------

still compiles with GCC, it doesn't compile with Clang:

error: constexpr variable 'register_addr' must be initialized by a
constant expression
note: cast that performs the conversions of a reinterpret_cast is not
allowed in a constant expression

> As far as I can tell, this sort of code is perfectly legal - gcc does
> not get to "break" it, and it certainly should not be doing so
> "randomly".

C++14: 5.20 Constant expressions [expr.const]
<<<
A conditional-expression e is a core constant expression unless the
evaluation of e, following the rules of the abstract machine (1.9),
would evaluate one of the following expressions:
...
— (2.13) a reinterpret_cast
>>>

mark

unread,
Oct 7, 2016, 8:16:31 AM10/7/16
to
On 2016-10-07 09:03, David Brown wrote:
>
> On 07/10/16 01:06, Barry Schwarz wrote:
>> > If it is a C header file, why are you compiling it as C++?

How do I get compile time constants from a C translation unit into my
C++ headers? We are talking constexpr here, so the definition must be
available.

>> > Not all C code is valid C++ code.
>> >
> C header files are usually perfectly valid C++ code, once you have
> wrapped any function declarations in extern "C". In this particular
> case, my guess is that the headers in question are for a
> microcontroller, and contain a list of definitions of hardware
> registers. Such headers are almost always referred to as "C", but will
> be compatible with C++.

Yes.

These are 3rd party headers (used for C and C++) and there are literally
thousands of these definitions.

Richard

unread,
Oct 7, 2016, 1:05:18 PM10/7/16
to
[Please do not mail me a copy of your followup]

mark <ma...@invalid.invalid> spake the secret code
<nt83m6$u72$1...@dont-email.me> thusly:

>How do I get compile time constants from a C translation unit into my
>C++ headers?

C doesn't have the concept of a compile-time constant as a named
entity. That's why they are macros in a header. Macros are not
named entities understood by the compiler, they are expanded
into tokens by the preprocessor. As far as the compiler is
concerned, they are all just integral literals.

C++ has the concept of constant values (e.g. const int) and this
was generalized to constexpr expressions. In C++, all you need is
something like this in your header:

extern volatile uint32_t *const port_address;

...and in some translation unit you have:

volatile uint32_t *const port_address =
reinterpret_cast<volatile uint32_t*>(0x42424242);

You can't really get away from the reinterpret_cast here as there is
no way of directly expressing an address as a literal.

Therefore because reinterpret_cast<> foils constexpr, you can't have
this as constexpr either.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Tim Rentsch

unread,
Oct 9, 2016, 7:31:39 AM10/9/16
to
mark <ma...@invalid.invalid> writes:

> On 2016-10-06 22:21, David Brown wrote:
>
>>> That's what I mean by GCC randomly enforcing constexpr restrictions.
>>> Some cases still work, others are broken. This is explicitly forbidden
>>> by the standard and GCC is moving towards more enforcement.
>>
>> Can you give an example of where this does not work? Exact code, and
>> gcc version and target?
>
> [... gcc example ...]
>
> While your example:
> --------------------------------------------------------------
> #define REGISTER (*(volatile uint32_t *)0x42424242)
> constexpr uintptr_t register_addr = (uintptr_t) &REGISTER;
> --------------------------------------------------------------
>
> still compiles with GCC, it doesn't compile with Clang: [...]

If 'constexpr' is changed to 'const', is that also an error or is
it accepted? I find it hard to follow the rather circuitous
writing in the C++ standard in this area (among others).

>> As far as I can tell, this sort of code is perfectly legal - gcc does
>> not get to "break" it, and it certainly should not be doing so
>> "randomly".
>
> C++14: 5.20 Constant expressions [expr.const]
> <<<
> A conditional-expression e is a core constant expression unless the
> evaluation of e, following the rules of the abstract machine (1.9),
> would evaluate one of the following expressions:
> ...
> ? (2.13) a reinterpret_cast

This item is also present in C++11 (although of course other
things may have changed that also bear on the question).

Out of curiosity, what happens if you run your examples
with --std=c++11?

mark

unread,
Oct 9, 2016, 1:49:26 PM10/9/16
to
On 2016-10-09 13:31, Tim Rentsch wrote:
>> > While your example:
>> > --------------------------------------------------------------
>> > #define REGISTER (*(volatile uint32_t *)0x42424242)
>> > constexpr uintptr_t register_addr = (uintptr_t) &REGISTER;
>> > --------------------------------------------------------------
>> >
>> > still compiles with GCC, it doesn't compile with Clang: [...]
> If 'constexpr' is changed to 'const', is that also an error or is
> it accepted? I find it hard to follow the rather circuitous
> writing in the C++ standard in this area (among others).

const compiles. However, it does not result in compile time constant
value and is not usable as template parameter or in constexpr.

>> > C++14: 5.20 Constant expressions [expr.const]
>> > <<<
>> > A conditional-expression e is a core constant expression unless the
>> > evaluation of e, following the rules of the abstract machine (1.9),
>> > would evaluate one of the following expressions:
>> > ...
>> > ? (2.13) a reinterpret_cast
> This item is also present in C++11 (although of course other
> things may have changed that also bear on the question).
>
> Out of curiosity, what happens if you run your examples
> with --std=c++11?

Doesn't make a difference.

Richard

unread,
Oct 10, 2016, 12:43:20 PM10/10/16
to
[Please do not mail me a copy of your followup]

mark <ma...@invalid.invalid> spake the secret code
<ntdvuc$td5$1...@dont-email.me> thusly:

>const compiles. However, it does not result in compile time constant
>value

Please defined "compile time constant value" in specific terms.

For a simple constant address value I'd find it hard to believe that any
compiler (old or new) wouldn't turn this into a piece of fixed data in
the initialized data segment of the output image. If properly declared
and defined I'd find it hard to believe that many if not most compilers
would inline the value directly.

mark

unread,
Oct 10, 2016, 4:25:03 PM10/10/16
to
On 2016-10-10 18:43, Richard wrote:
>
>> >const compiles. However, it does not result in compile time constant
>> >value
> Please defined "compile time constant value" in specific terms.

---------------------------------------------------------------
#define REGISTER (*(volatile uint32_t *)0x42424242)
const uintptr_t register_addr = (uintptr_t) &REGISTER;

template<uintptr_t ptr> struct S {};
using S42 = S<register_addr>;
---------------------------------------------------------------
error: non-type template argument is not a constant expression

---------------------------------------------------------------
constexpr uintptr_t fn(uintptr_t v){ return (v % 128) / 4; }
constexpr size_t pin_number = fn(register_addr);
---------------------------------------------------------------
error: constexpr variable 'pin_number' must be initialized by a constant
expression

> For a simple constant address value I'd find it hard to believe that any
> compiler (old or new) wouldn't turn this into a piece of fixed data in
> the initialized data segment of the output image. If properly declared
> and defined I'd find it hard to believe that many if not most compilers
> would inline the value directly.

I'm not concerned about direct usages getting inlined. I'm concerned
about not being able to do template / constexpr metaprogramming.

Richard

unread,
Oct 10, 2016, 4:56:21 PM10/10/16
to
[Please do not mail me a copy of your followup]

mark <ma...@invalid.invalid> spake the secret code
<ntgte5$c1b$1...@dont-email.me> thusly:

>On 2016-10-10 18:43, Richard wrote:
>>
>>> >const compiles. However, it does not result in compile time constant
>>> >value
>> Please defined "compile time constant value" in specific terms.
>
>---------------------------------------------------------------
>#define REGISTER (*(volatile uint32_t *)0x42424242)
>const uintptr_t register_addr = (uintptr_t) &REGISTER;
>
>template<uintptr_t ptr> struct S {};
>using S42 = S<register_addr>;
>---------------------------------------------------------------
>error: non-type template argument is not a constant expression

Get rid of the C-style cast and the problem becomes obvious.

>I'm not concerned about direct usages getting inlined. I'm concerned
>about not being able to do template / constexpr metaprogramming.

What template metaprogramming? So far you've only talked about
contexpr functions.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>

mark

unread,
Oct 10, 2016, 6:40:07 PM10/10/16
to
On 2016-10-10 22:56, Richard wrote:
>> >---------------------------------------------------------------
>> >#define REGISTER (*(volatile uint32_t *)0x42424242)
>> >const uintptr_t register_addr = (uintptr_t) &REGISTER;
>> >
>> >template<uintptr_t ptr> struct S {};
>> >using S42 = S<register_addr>;
>> >---------------------------------------------------------------
>> >error: non-type template argument is not a constant expression
> Get rid of the C-style cast and the problem becomes obvious.

I know exactly that this is a reinterpret_cast, I have quoted the
standard myself, if you go up a few posts. I know that the standard
doesn't allow it and I'm looking for a workaround.

>> >I'm not concerned about direct usages getting inlined. I'm concerned
>> >about not being able to do template / constexpr metaprogramming.
> What template metaprogramming? So far you've only talked about
> contexpr functions.

I haven't talked about templates, because it's pretty much the same in
terms of restrictions. If its a constexpr value, it can be used as
template parameter (or array bounds or whatever). I have very
specifically said constexpr and not const.

Richard

unread,
Oct 10, 2016, 10:19:09 PM10/10/16
to
[Please do not mail me a copy of your followup]

mark <ma...@invalid.invalid> spake the secret code
<nth5bc$6m3$1...@dont-email.me> thusly:

>I haven't talked about templates, because it's pretty much the same in
>terms of restrictions. If its a constexpr value, it can be used as
>template parameter (or array bounds or whatever). I have very
>specifically said constexpr and not const.

My point is that we're hung up on one very specific details here that
can't be avoided due to the nature of reinterpret_cast<>. However, if
you stated the goal instead of the task, we might be able to come up
with an alternative solution.

There is no solution on this path. We can't suggest another path,
because we don't know where you're trying to go.

mark

unread,
Oct 11, 2016, 1:31:46 PM10/11/16
to
On 2016-10-11 04:19, Richard wrote:
> mark <ma...@invalid.invalid> spake the secret code
> <nth5bc$6m3$1...@dont-email.me> thusly:
>
>> I haven't talked about templates, because it's pretty much the same
>> in terms of restrictions. If its a constexpr value, it can be used
>> as template parameter (or array bounds or whatever). I have very
>> specifically said constexpr and not const.
>
> My point is that we're hung up on one very specific details here
> that can't be avoided due to the nature of reinterpret_cast<>.

I want to avoid the constant expression reinterpret_cast in the first
place. That's why this thread is titled 'Stripping cast from macro'.

> However, if you stated the goal instead of the task, we might be able
> to come up with an alternative solution.

The goal is:
#define REGISTER (*(volatile uint32_t *)0x42424242)
constexpr uintptr_t register_addr = SOME_MAGIC(REGISTER);

REGISTER has to end up as constant expression. These integers are used
for compile time calculations, array sizes, identities for static
template classes, initialization lists, for construction of parameter
packs, ...

> There is no solution on this path.

There is an ugly workaround. I can use a macro to turn REGISTER into a
string literal "(*(volatile uint32_t *)0x42424242)", which can be parsed
at compile time with a constexpr function. With C++11 it's a bit nasty,
with C++14 no problem. What makes this kludge somewhat viable is that it
is possible to get good error messages if the string has an unexpected
format and is not parsable.


Richard

unread,
Oct 11, 2016, 5:30:34 PM10/11/16
to
[Please do not mail me a copy of your followup]

mark <ma...@invalid.invalid> spake the secret code
<ntj7l7$8fq$1...@dont-email.me> thusly:

>The goal is:
>#define REGISTER (*(volatile uint32_t *)0x42424242)
>constexpr uintptr_t register_addr = SOME_MAGIC(REGISTER);

Sorry, but this is what I'm calling "the task". We're stuck on some
low-level details that aren't yielding fruit because we don't have
visibility into the scope of the larger problem.

The goal might be something like "I want to model this set of
hardware registers with the following structure...".

Just look at what you've written above. 0x42424242 is a magical,
made-up number, not a real-world example of what you're trying to do.
SOME_MAGIC is also some made-up thing that isn't contextualized into
the larger problem.

Help us to help you. Please don't just repeat the above snippet
because that isn't the end-game; it's a low-level detail that isn't
workable as it stands.

Paavo Helde

unread,
Oct 11, 2016, 7:05:26 PM10/11/16
to
On 11.10.2016 20:31, mark wrote:
>
> The goal is:
> #define REGISTER (*(volatile uint32_t *)0x42424242)
> constexpr uintptr_t register_addr = SOME_MAGIC(REGISTER);
>
> REGISTER has to end up as constant expression. These integers are used
> for compile time calculations, array sizes, identities for static
> template classes, initialization lists, for construction of parameter
> packs, ...

Not sure how you like this, but this is able to strip away the cast:

#include <stdint.h>

#define REGISTER (*(volatile uint32_t *)0x42424242)

#define EAT(x)
#define volatile ))EAT(
#define SOME_MAGIC(x) (EAT x

mark

unread,
Oct 12, 2016, 3:29:14 AM10/12/16
to
On 2016-10-12 01:05, Paavo Helde wrote:
>
> Not sure how you like this, but this is able to strip away the cast:
>
> #include <stdint.h>
>
> #define REGISTER (*(volatile uint32_t *)0x42424242)
>
> #define EAT(x)
> #define volatile ))EAT(
> #define SOME_MAGIC(x) (EAT x
>
> constexpr uintptr_t register_addr = SOME_MAGIC(REGISTER);

Thanks, very nice! This kind of neat kludge is what I had in mind when I
started the thread.

Richard

unread,
Oct 12, 2016, 1:20:03 PM10/12/16
to
[Please do not mail me a copy of your followup]

Paavo Helde <myfir...@osa.pri.ee> spake the secret code
<9qadnd2UQrm37WDK...@giganews.com> thusly:
It's undefined behavior to define keywords as macros.

mark

unread,
Oct 14, 2016, 6:11:35 AM10/14/16
to
Something that works with all versions of GCC, Clang and Intel C++ (that
have C++11 / constexpr support):


#define REGISTER (*(volatile uint32_t *)0x42424242)

#define FORCE_CONST(x) (__builtin_constant_p(x) ? (x) : (x))
#define FORCE_CONST_ADDR(x) FORCE_CONST(reinterpret_cast<uintptr_t>(&x))

constexpr uintptr_t test = FORCE_CONST_ADDR(REGISTER);

Tim Rentsch

unread,
Oct 14, 2016, 10:18:21 AM10/14/16
to
mark <ma...@invalid.invalid> writes:

> On 2016-10-09 13:31, Tim Rentsch wrote:
>>>> While your example:
>>>> --------------------------------------------------------------
>>>> #define REGISTER (*(volatile uint32_t *)0x42424242)
>>>> constexpr uintptr_t register_addr = (uintptr_t) &REGISTER;
>>>> --------------------------------------------------------------
>>>>
>>>> still compiles with GCC, it doesn't compile with Clang: [...]
>>
>> If 'constexpr' is changed to 'const', is that also an error or is
>> it accepted? I find it hard to follow the rather circuitous
>> writing in the C++ standard in this area (among others).
>
> const compiles. However, it does not result in compile time constant
> value and is not usable as template parameter or in constexpr.

I see. Basically the same as the difference between a constant
expression and an integer constant expression in C. That makes
the C++ rules consistent with the analogous rules in C, FWTIW.

>>>> C++14: 5.20 Constant expressions [expr.const]
>>>> <<<
>>>> A conditional-expression e is a core constant expression unless the
>>>> evaluation of e, following the rules of the abstract machine (1.9),
>>>> would evaluate one of the following expressions:
>>>> ...
>>>> ? (2.13) a reinterpret_cast
>>
>> This item is also present in C++11 (although of course other
>> things may have changed that also bear on the question).
>>
>> Out of curiosity, what happens if you run your examples
>> with --std=c++11?
>
> Doesn't make a difference.

Okay, thank you for the followup.

The limitation on "constant but not allowed to be constexpr"
expressions strikes me as a shortcoming of C++, considering
various implications for possible use cases. So if a vote
were being taken I would vote in favor of eliminating the
restriction in cases like this. Not that I think my vote
is going to be given much weight, but still... :)
0 new messages