How do I create a âspacerâ in a C++ class memory structure?
Clash Royale CLAN TAG#URR8PPP
up vote
14
down vote
favorite
The issue
In a low level bare-metal embedded context, I would like to create a blank space in the memory, within a C++ structure and without any name, to forbid the user to access such memory location.
Right now, I have achieved to do so by putting an ugly uint32_t :96;
bitfield which will conveniently take place of three words, but it will raise a warning from GCC (Bitfield too large to fit in uint32_t, which is pretty legitimate).
While it works fine, it is not very clean when you want to distribute a library with several hundreds of those warnings...
How do I do that properly?
Why is there an issue in the first place?
The project I'm working on consist in defining the memory structure of different peripherals of a whole microcontroller line (STMicroelectronics STM32). To do so, the result is a class which contains an union of several structures which defines all registers, depending on the targeted microcontroller.
One simple example is the following, for a pretty simple peripheral: a General Purpose Input/Output (GPIO)
union
struct
GPIO_MAP0_MODER;
GPIO_MAP0_OTYPER;
GPIO_MAP0_OSPEEDR;
GPIO_MAP0_PUPDR;
GPIO_MAP0_IDR;
GPIO_MAP0_ODR;
GPIO_MAP0_BSRR;
GPIO_MAP0_LCKR;
GPIO_MAP0_AFR;
GPIO_MAP0_BRR;
GPIO_MAP0_ASCR;
;
struct
GPIO_MAP1_CRL;
GPIO_MAP1_CRH;
GPIO_MAP1_IDR;
GPIO_MAP1_ODR;
GPIO_MAP1_BSRR;
GPIO_MAP1_BRR;
GPIO_MAP1_LCKR;
uint32_t :32;
GPIO_MAP1_AFRL;
GPIO_MAP1_AFRH;
uint32_t :64;
;
struct
uint32_t :192;
GPIO_MAP2_BSRRL;
GPIO_MAP2_BSRRH;
uint32_t :160;
;
;
Where all GPIO_MAPx_YYY
is a macro, defined either as uint32_t :32
or the register type (a dedicated structure).
Here you see the uint32_t :192;
which works well, but trigger a warning.
I might have replaced it by several uint32_t :32;
(here 6), but I have some extreme cases where I have uint32_t :1344;
(42) (among others). So I would rather not add about one hundred lines on top of 8k others, even though the structure generation is scripted.
By the way, all posted code is working absolutely (and surprisingly) fine.
The exact warning message is something like this:width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' exceeds its type
(I just love how shady it is).
I would rather not solve this by simply removing the warning, but the use of
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-WTheRightFlag"
/* My code */
#pragma GCC diagnostic pop
May be a solution... If I find TheRightFlag
In the same way as pointed out in this thread, gcc/cp/class.c
hold this sad code part:
warning_at (DECL_SOURCE_LOCATION (field), 0,
"width of %qD exceeds its type", field);
Which tells us that there is no -Wxxx
flag to remove this warning...
c++ c memory-management low-level bare-metal
New contributor
 |Â
show 14 more comments
up vote
14
down vote
favorite
The issue
In a low level bare-metal embedded context, I would like to create a blank space in the memory, within a C++ structure and without any name, to forbid the user to access such memory location.
Right now, I have achieved to do so by putting an ugly uint32_t :96;
bitfield which will conveniently take place of three words, but it will raise a warning from GCC (Bitfield too large to fit in uint32_t, which is pretty legitimate).
While it works fine, it is not very clean when you want to distribute a library with several hundreds of those warnings...
How do I do that properly?
Why is there an issue in the first place?
The project I'm working on consist in defining the memory structure of different peripherals of a whole microcontroller line (STMicroelectronics STM32). To do so, the result is a class which contains an union of several structures which defines all registers, depending on the targeted microcontroller.
One simple example is the following, for a pretty simple peripheral: a General Purpose Input/Output (GPIO)
union
struct
GPIO_MAP0_MODER;
GPIO_MAP0_OTYPER;
GPIO_MAP0_OSPEEDR;
GPIO_MAP0_PUPDR;
GPIO_MAP0_IDR;
GPIO_MAP0_ODR;
GPIO_MAP0_BSRR;
GPIO_MAP0_LCKR;
GPIO_MAP0_AFR;
GPIO_MAP0_BRR;
GPIO_MAP0_ASCR;
;
struct
GPIO_MAP1_CRL;
GPIO_MAP1_CRH;
GPIO_MAP1_IDR;
GPIO_MAP1_ODR;
GPIO_MAP1_BSRR;
GPIO_MAP1_BRR;
GPIO_MAP1_LCKR;
uint32_t :32;
GPIO_MAP1_AFRL;
GPIO_MAP1_AFRH;
uint32_t :64;
;
struct
uint32_t :192;
GPIO_MAP2_BSRRL;
GPIO_MAP2_BSRRH;
uint32_t :160;
;
;
Where all GPIO_MAPx_YYY
is a macro, defined either as uint32_t :32
or the register type (a dedicated structure).
Here you see the uint32_t :192;
which works well, but trigger a warning.
I might have replaced it by several uint32_t :32;
(here 6), but I have some extreme cases where I have uint32_t :1344;
(42) (among others). So I would rather not add about one hundred lines on top of 8k others, even though the structure generation is scripted.
By the way, all posted code is working absolutely (and surprisingly) fine.
The exact warning message is something like this:width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' exceeds its type
(I just love how shady it is).
I would rather not solve this by simply removing the warning, but the use of
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-WTheRightFlag"
/* My code */
#pragma GCC diagnostic pop
May be a solution... If I find TheRightFlag
In the same way as pointed out in this thread, gcc/cp/class.c
hold this sad code part:
warning_at (DECL_SOURCE_LOCATION (field), 0,
"width of %qD exceeds its type", field);
Which tells us that there is no -Wxxx
flag to remove this warning...
c++ c memory-management low-level bare-metal
New contributor
8
have you considedchar unused[12];
and so on?
â M.M
8 hours ago
1
This is a well done question. Good job! I'd like to know the answer myself.
â Christopher Pisz
8 hours ago
2
I would just suppress the warning. [class.bit]/1 guarantees the behavior ofuint32_t :192;
.
â NathanOliver
8 hours ago
2
@NathanOliver I would gladly too, but it seems that this warning is not suppressible (Using GCC) or I didn't find how to do so. Moreover, it still is not a clean way to do (but it would be pretty satisfying) . I managed to find the correct "-W" flag but didn't manage to apply it only on my own files (I don't want the user to remove this kind of warnings for his work)
â J Faucher
8 hours ago
1
BTW you can write:42*32
instead of:1344
â M.M
8 hours ago
 |Â
show 14 more comments
up vote
14
down vote
favorite
up vote
14
down vote
favorite
The issue
In a low level bare-metal embedded context, I would like to create a blank space in the memory, within a C++ structure and without any name, to forbid the user to access such memory location.
Right now, I have achieved to do so by putting an ugly uint32_t :96;
bitfield which will conveniently take place of three words, but it will raise a warning from GCC (Bitfield too large to fit in uint32_t, which is pretty legitimate).
While it works fine, it is not very clean when you want to distribute a library with several hundreds of those warnings...
How do I do that properly?
Why is there an issue in the first place?
The project I'm working on consist in defining the memory structure of different peripherals of a whole microcontroller line (STMicroelectronics STM32). To do so, the result is a class which contains an union of several structures which defines all registers, depending on the targeted microcontroller.
One simple example is the following, for a pretty simple peripheral: a General Purpose Input/Output (GPIO)
union
struct
GPIO_MAP0_MODER;
GPIO_MAP0_OTYPER;
GPIO_MAP0_OSPEEDR;
GPIO_MAP0_PUPDR;
GPIO_MAP0_IDR;
GPIO_MAP0_ODR;
GPIO_MAP0_BSRR;
GPIO_MAP0_LCKR;
GPIO_MAP0_AFR;
GPIO_MAP0_BRR;
GPIO_MAP0_ASCR;
;
struct
GPIO_MAP1_CRL;
GPIO_MAP1_CRH;
GPIO_MAP1_IDR;
GPIO_MAP1_ODR;
GPIO_MAP1_BSRR;
GPIO_MAP1_BRR;
GPIO_MAP1_LCKR;
uint32_t :32;
GPIO_MAP1_AFRL;
GPIO_MAP1_AFRH;
uint32_t :64;
;
struct
uint32_t :192;
GPIO_MAP2_BSRRL;
GPIO_MAP2_BSRRH;
uint32_t :160;
;
;
Where all GPIO_MAPx_YYY
is a macro, defined either as uint32_t :32
or the register type (a dedicated structure).
Here you see the uint32_t :192;
which works well, but trigger a warning.
I might have replaced it by several uint32_t :32;
(here 6), but I have some extreme cases where I have uint32_t :1344;
(42) (among others). So I would rather not add about one hundred lines on top of 8k others, even though the structure generation is scripted.
By the way, all posted code is working absolutely (and surprisingly) fine.
The exact warning message is something like this:width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' exceeds its type
(I just love how shady it is).
I would rather not solve this by simply removing the warning, but the use of
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-WTheRightFlag"
/* My code */
#pragma GCC diagnostic pop
May be a solution... If I find TheRightFlag
In the same way as pointed out in this thread, gcc/cp/class.c
hold this sad code part:
warning_at (DECL_SOURCE_LOCATION (field), 0,
"width of %qD exceeds its type", field);
Which tells us that there is no -Wxxx
flag to remove this warning...
c++ c memory-management low-level bare-metal
New contributor
The issue
In a low level bare-metal embedded context, I would like to create a blank space in the memory, within a C++ structure and without any name, to forbid the user to access such memory location.
Right now, I have achieved to do so by putting an ugly uint32_t :96;
bitfield which will conveniently take place of three words, but it will raise a warning from GCC (Bitfield too large to fit in uint32_t, which is pretty legitimate).
While it works fine, it is not very clean when you want to distribute a library with several hundreds of those warnings...
How do I do that properly?
Why is there an issue in the first place?
The project I'm working on consist in defining the memory structure of different peripherals of a whole microcontroller line (STMicroelectronics STM32). To do so, the result is a class which contains an union of several structures which defines all registers, depending on the targeted microcontroller.
One simple example is the following, for a pretty simple peripheral: a General Purpose Input/Output (GPIO)
union
struct
GPIO_MAP0_MODER;
GPIO_MAP0_OTYPER;
GPIO_MAP0_OSPEEDR;
GPIO_MAP0_PUPDR;
GPIO_MAP0_IDR;
GPIO_MAP0_ODR;
GPIO_MAP0_BSRR;
GPIO_MAP0_LCKR;
GPIO_MAP0_AFR;
GPIO_MAP0_BRR;
GPIO_MAP0_ASCR;
;
struct
GPIO_MAP1_CRL;
GPIO_MAP1_CRH;
GPIO_MAP1_IDR;
GPIO_MAP1_ODR;
GPIO_MAP1_BSRR;
GPIO_MAP1_BRR;
GPIO_MAP1_LCKR;
uint32_t :32;
GPIO_MAP1_AFRL;
GPIO_MAP1_AFRH;
uint32_t :64;
;
struct
uint32_t :192;
GPIO_MAP2_BSRRL;
GPIO_MAP2_BSRRH;
uint32_t :160;
;
;
Where all GPIO_MAPx_YYY
is a macro, defined either as uint32_t :32
or the register type (a dedicated structure).
Here you see the uint32_t :192;
which works well, but trigger a warning.
I might have replaced it by several uint32_t :32;
(here 6), but I have some extreme cases where I have uint32_t :1344;
(42) (among others). So I would rather not add about one hundred lines on top of 8k others, even though the structure generation is scripted.
By the way, all posted code is working absolutely (and surprisingly) fine.
The exact warning message is something like this:width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' exceeds its type
(I just love how shady it is).
I would rather not solve this by simply removing the warning, but the use of
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-WTheRightFlag"
/* My code */
#pragma GCC diagnostic pop
May be a solution... If I find TheRightFlag
In the same way as pointed out in this thread, gcc/cp/class.c
hold this sad code part:
warning_at (DECL_SOURCE_LOCATION (field), 0,
"width of %qD exceeds its type", field);
Which tells us that there is no -Wxxx
flag to remove this warning...
c++ c memory-management low-level bare-metal
c++ c memory-management low-level bare-metal
New contributor
New contributor
edited 7 mins ago
Peter Mortensen
13.2k1983111
13.2k1983111
New contributor
asked 8 hours ago
J Faucher
713
713
New contributor
New contributor
8
have you considedchar unused[12];
and so on?
â M.M
8 hours ago
1
This is a well done question. Good job! I'd like to know the answer myself.
â Christopher Pisz
8 hours ago
2
I would just suppress the warning. [class.bit]/1 guarantees the behavior ofuint32_t :192;
.
â NathanOliver
8 hours ago
2
@NathanOliver I would gladly too, but it seems that this warning is not suppressible (Using GCC) or I didn't find how to do so. Moreover, it still is not a clean way to do (but it would be pretty satisfying) . I managed to find the correct "-W" flag but didn't manage to apply it only on my own files (I don't want the user to remove this kind of warnings for his work)
â J Faucher
8 hours ago
1
BTW you can write:42*32
instead of:1344
â M.M
8 hours ago
 |Â
show 14 more comments
8
have you considedchar unused[12];
and so on?
â M.M
8 hours ago
1
This is a well done question. Good job! I'd like to know the answer myself.
â Christopher Pisz
8 hours ago
2
I would just suppress the warning. [class.bit]/1 guarantees the behavior ofuint32_t :192;
.
â NathanOliver
8 hours ago
2
@NathanOliver I would gladly too, but it seems that this warning is not suppressible (Using GCC) or I didn't find how to do so. Moreover, it still is not a clean way to do (but it would be pretty satisfying) . I managed to find the correct "-W" flag but didn't manage to apply it only on my own files (I don't want the user to remove this kind of warnings for his work)
â J Faucher
8 hours ago
1
BTW you can write:42*32
instead of:1344
â M.M
8 hours ago
8
8
have you consided
char unused[12];
and so on?â M.M
8 hours ago
have you consided
char unused[12];
and so on?â M.M
8 hours ago
1
1
This is a well done question. Good job! I'd like to know the answer myself.
â Christopher Pisz
8 hours ago
This is a well done question. Good job! I'd like to know the answer myself.
â Christopher Pisz
8 hours ago
2
2
I would just suppress the warning. [class.bit]/1 guarantees the behavior of
uint32_t :192;
.â NathanOliver
8 hours ago
I would just suppress the warning. [class.bit]/1 guarantees the behavior of
uint32_t :192;
.â NathanOliver
8 hours ago
2
2
@NathanOliver I would gladly too, but it seems that this warning is not suppressible (Using GCC) or I didn't find how to do so. Moreover, it still is not a clean way to do (but it would be pretty satisfying) . I managed to find the correct "-W" flag but didn't manage to apply it only on my own files (I don't want the user to remove this kind of warnings for his work)
â J Faucher
8 hours ago
@NathanOliver I would gladly too, but it seems that this warning is not suppressible (Using GCC) or I didn't find how to do so. Moreover, it still is not a clean way to do (but it would be pretty satisfying) . I managed to find the correct "-W" flag but didn't manage to apply it only on my own files (I don't want the user to remove this kind of warnings for his work)
â J Faucher
8 hours ago
1
1
BTW you can write
:42*32
instead of :1344
â M.M
8 hours ago
BTW you can write
:42*32
instead of :1344
â M.M
8 hours ago
 |Â
show 14 more comments
4 Answers
4
active
oldest
votes
up vote
5
down vote
In the embedded systems arena, you can model hardware either by using a structure or by defining pointers to the register addresses.
Modeling by structure is not recommended because the compiler is allowed to add padding between members for alignment purposes (although many compilers for embedded systems have a pragma for packing the structure).
Example:
uint16_t * const UART1 = (uint16_t *)(0x40000);
const unsigned int UART_STATUS_OFFSET = 1U;
const unsigned int UART_TRANSMIT_REGISTER = 2U;
uint16_t * const UART1_STATUS_REGISTER = (UART1 + UART_STATUS_OFFSET);
uint16_t * const UART1_TRANSMIT_REGISTER = (UART1 + UART_TRANSMIT_REGISTER);
You could also use the array notation:
uint16_t status = UART1[UART_STATUS_OFFSET];
If you must use the structure, IMHO, the best method to skip addresses would be to define a member and not access it:
struct UART1
uint16_t status;
uint16_t reserved1; // Transmit register
uint16_t receive_register;
;
In one of our projects we have both constants and structs from different vendors (vendor 1 uses constants while vendor 2 uses structures).
Thanks for your answer. However, I choose to use a structure approach to ease the work of the user when he got an auto-complete feature (You only will have the right attributes displayed) and I don't want to "show" the user the reserved slots as pointed out in a comment of my first post.
â J Faucher
7 hours ago
You can still have that by making the above addressstatic
members of a structure assuming that autocomplete is able to show static members. If not, it can be inline member functions too.
â Phil1970
5 hours ago
@JFaucher I'm not an embedded systems person and haven't tested this, but wouldn't the autocomplete issue be resolved by declaring the reserved member private? (You can declare private members in a struct, and you can usepublic:
andprivate:
as many times as you want, to get the correct ordering of the fields.)
â Nathaniel
27 mins ago
add a comment |Â
up vote
5
down vote
How about a C++-ish way?
namespace GPIO
static volatile uint32_t &MAP0_MODER = *reinterpret_cast<uint32_t*>(0x4000);
static volatile uint32_t &MAP0_OTYPER = *reinterpret_cast<uint32_t*>(0x4004);
int main()
GPIO::MAP0_MODER = 42;
You get autocompletion because of the GPIO
namespace, and there is no need for dummy padding. Even, it is more clear what's going on, as you can see the address of each register, you don't have to rely on the compiler's padding behavior at all.
Even better any other comments I have made elsewhere. That should worth more than 1 vote.
â Phil1970
5 hours ago
This might possible optimize less well than a struct for access to multiple MMIO registers from the same function. With a pointer to the base address in a register, the compiler can use load/store instructions with immediate displacements, likeldr r0, [r4, #16]
, while compilers are more likely to miss that optimization with each address declared separately. GCC will probably load each GPIO address into a separate register. (From a literal pool, although some of them can be represented as rotated immediates in Thumb encoding.)
â Peter Cordes
4 hours ago
@PeterCordes: I've just checked this with clang, and it optimizes this case well. I haven't checked GCC, maybe it misses this optimization as you say.
â geza
4 hours ago
Turns out my worries were unfounded; ARM GCC does optimize this way, too. godbolt.org/z/ztB7hi. But note that you wantstatic volatile uint32_t &MAP0_MODER
, notinline
. Aninline
variable doesn't compile. (static
avoids having any static storage for the pointer, andvolatile
is exactly what you want for MMIO to avoid dead-store elimination or optimization of write / read-back.)
â Peter Cordes
3 hours ago
1
@PeterCordes: inline variables is a new C++17 feature. But you're right,static
does equally well for this case. Thanks for mentioningvolatile
, I'll add it to my answer (and change inline to static, so it works for pre C++17).
â geza
3 hours ago
 |Â
show 2 more comments
up vote
2
down vote
Use multiple adjacent anonymous bitfields. So instead of:
uint32_t :160;
for example, you'd have:
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
One for each register you want to be anonymous.
Thanks for the answer. I already considered that, however it would add over 200 lines on some of my files (uint32_t :1344;
is in the place) so I would rather not have to go this way...
â J Faucher
7 hours ago
add a comment |Â
up vote
2
down vote
To expand on Clifford's answer, you can always macro out the anonymous bitfields.
So instead of
uint32_t :160;
use
#define EMPTY_32_1
uint32_t :32
#define EMPTY_32_2
uint32_t :32; // I guess this also can be replaced with uint64_t :64
uint32_t :32
#define EMPTY_32_3
uint32_t :32;
uint32_t :32;
uint32_t :32
#define EMPTY_UINT32(N) EMPTY_32_ ## N
And then use it like
struct A
EMPTY_UINT32(3);
/* which resolves to EMPTY_32_3, which then resolves to real declarations */
Unfortunately, you'll need as many EMPTY_32_X
variants as many bytes you have :(
Still, it allows you to have single declarations in your struct.
1
Using Boost CPP macros, I think you can use recursion to avoid having to hand-create all the necessary macros.
â Peter Cordes
4 hours ago
You can cascade them (up to preprocessor recursion limit, but that's usually ample). So#define EMPTY_32_2 EMPTY_32_1; EMPTY_32_1
and#define EMPTY_32_3 EMPTY_32_2; EMPTY_32_1
etc.
â Miral
2 hours ago
add a comment |Â
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
5
down vote
In the embedded systems arena, you can model hardware either by using a structure or by defining pointers to the register addresses.
Modeling by structure is not recommended because the compiler is allowed to add padding between members for alignment purposes (although many compilers for embedded systems have a pragma for packing the structure).
Example:
uint16_t * const UART1 = (uint16_t *)(0x40000);
const unsigned int UART_STATUS_OFFSET = 1U;
const unsigned int UART_TRANSMIT_REGISTER = 2U;
uint16_t * const UART1_STATUS_REGISTER = (UART1 + UART_STATUS_OFFSET);
uint16_t * const UART1_TRANSMIT_REGISTER = (UART1 + UART_TRANSMIT_REGISTER);
You could also use the array notation:
uint16_t status = UART1[UART_STATUS_OFFSET];
If you must use the structure, IMHO, the best method to skip addresses would be to define a member and not access it:
struct UART1
uint16_t status;
uint16_t reserved1; // Transmit register
uint16_t receive_register;
;
In one of our projects we have both constants and structs from different vendors (vendor 1 uses constants while vendor 2 uses structures).
Thanks for your answer. However, I choose to use a structure approach to ease the work of the user when he got an auto-complete feature (You only will have the right attributes displayed) and I don't want to "show" the user the reserved slots as pointed out in a comment of my first post.
â J Faucher
7 hours ago
You can still have that by making the above addressstatic
members of a structure assuming that autocomplete is able to show static members. If not, it can be inline member functions too.
â Phil1970
5 hours ago
@JFaucher I'm not an embedded systems person and haven't tested this, but wouldn't the autocomplete issue be resolved by declaring the reserved member private? (You can declare private members in a struct, and you can usepublic:
andprivate:
as many times as you want, to get the correct ordering of the fields.)
â Nathaniel
27 mins ago
add a comment |Â
up vote
5
down vote
In the embedded systems arena, you can model hardware either by using a structure or by defining pointers to the register addresses.
Modeling by structure is not recommended because the compiler is allowed to add padding between members for alignment purposes (although many compilers for embedded systems have a pragma for packing the structure).
Example:
uint16_t * const UART1 = (uint16_t *)(0x40000);
const unsigned int UART_STATUS_OFFSET = 1U;
const unsigned int UART_TRANSMIT_REGISTER = 2U;
uint16_t * const UART1_STATUS_REGISTER = (UART1 + UART_STATUS_OFFSET);
uint16_t * const UART1_TRANSMIT_REGISTER = (UART1 + UART_TRANSMIT_REGISTER);
You could also use the array notation:
uint16_t status = UART1[UART_STATUS_OFFSET];
If you must use the structure, IMHO, the best method to skip addresses would be to define a member and not access it:
struct UART1
uint16_t status;
uint16_t reserved1; // Transmit register
uint16_t receive_register;
;
In one of our projects we have both constants and structs from different vendors (vendor 1 uses constants while vendor 2 uses structures).
Thanks for your answer. However, I choose to use a structure approach to ease the work of the user when he got an auto-complete feature (You only will have the right attributes displayed) and I don't want to "show" the user the reserved slots as pointed out in a comment of my first post.
â J Faucher
7 hours ago
You can still have that by making the above addressstatic
members of a structure assuming that autocomplete is able to show static members. If not, it can be inline member functions too.
â Phil1970
5 hours ago
@JFaucher I'm not an embedded systems person and haven't tested this, but wouldn't the autocomplete issue be resolved by declaring the reserved member private? (You can declare private members in a struct, and you can usepublic:
andprivate:
as many times as you want, to get the correct ordering of the fields.)
â Nathaniel
27 mins ago
add a comment |Â
up vote
5
down vote
up vote
5
down vote
In the embedded systems arena, you can model hardware either by using a structure or by defining pointers to the register addresses.
Modeling by structure is not recommended because the compiler is allowed to add padding between members for alignment purposes (although many compilers for embedded systems have a pragma for packing the structure).
Example:
uint16_t * const UART1 = (uint16_t *)(0x40000);
const unsigned int UART_STATUS_OFFSET = 1U;
const unsigned int UART_TRANSMIT_REGISTER = 2U;
uint16_t * const UART1_STATUS_REGISTER = (UART1 + UART_STATUS_OFFSET);
uint16_t * const UART1_TRANSMIT_REGISTER = (UART1 + UART_TRANSMIT_REGISTER);
You could also use the array notation:
uint16_t status = UART1[UART_STATUS_OFFSET];
If you must use the structure, IMHO, the best method to skip addresses would be to define a member and not access it:
struct UART1
uint16_t status;
uint16_t reserved1; // Transmit register
uint16_t receive_register;
;
In one of our projects we have both constants and structs from different vendors (vendor 1 uses constants while vendor 2 uses structures).
In the embedded systems arena, you can model hardware either by using a structure or by defining pointers to the register addresses.
Modeling by structure is not recommended because the compiler is allowed to add padding between members for alignment purposes (although many compilers for embedded systems have a pragma for packing the structure).
Example:
uint16_t * const UART1 = (uint16_t *)(0x40000);
const unsigned int UART_STATUS_OFFSET = 1U;
const unsigned int UART_TRANSMIT_REGISTER = 2U;
uint16_t * const UART1_STATUS_REGISTER = (UART1 + UART_STATUS_OFFSET);
uint16_t * const UART1_TRANSMIT_REGISTER = (UART1 + UART_TRANSMIT_REGISTER);
You could also use the array notation:
uint16_t status = UART1[UART_STATUS_OFFSET];
If you must use the structure, IMHO, the best method to skip addresses would be to define a member and not access it:
struct UART1
uint16_t status;
uint16_t reserved1; // Transmit register
uint16_t receive_register;
;
In one of our projects we have both constants and structs from different vendors (vendor 1 uses constants while vendor 2 uses structures).
answered 8 hours ago
Thomas Matthews
43.5k1168119
43.5k1168119
Thanks for your answer. However, I choose to use a structure approach to ease the work of the user when he got an auto-complete feature (You only will have the right attributes displayed) and I don't want to "show" the user the reserved slots as pointed out in a comment of my first post.
â J Faucher
7 hours ago
You can still have that by making the above addressstatic
members of a structure assuming that autocomplete is able to show static members. If not, it can be inline member functions too.
â Phil1970
5 hours ago
@JFaucher I'm not an embedded systems person and haven't tested this, but wouldn't the autocomplete issue be resolved by declaring the reserved member private? (You can declare private members in a struct, and you can usepublic:
andprivate:
as many times as you want, to get the correct ordering of the fields.)
â Nathaniel
27 mins ago
add a comment |Â
Thanks for your answer. However, I choose to use a structure approach to ease the work of the user when he got an auto-complete feature (You only will have the right attributes displayed) and I don't want to "show" the user the reserved slots as pointed out in a comment of my first post.
â J Faucher
7 hours ago
You can still have that by making the above addressstatic
members of a structure assuming that autocomplete is able to show static members. If not, it can be inline member functions too.
â Phil1970
5 hours ago
@JFaucher I'm not an embedded systems person and haven't tested this, but wouldn't the autocomplete issue be resolved by declaring the reserved member private? (You can declare private members in a struct, and you can usepublic:
andprivate:
as many times as you want, to get the correct ordering of the fields.)
â Nathaniel
27 mins ago
Thanks for your answer. However, I choose to use a structure approach to ease the work of the user when he got an auto-complete feature (You only will have the right attributes displayed) and I don't want to "show" the user the reserved slots as pointed out in a comment of my first post.
â J Faucher
7 hours ago
Thanks for your answer. However, I choose to use a structure approach to ease the work of the user when he got an auto-complete feature (You only will have the right attributes displayed) and I don't want to "show" the user the reserved slots as pointed out in a comment of my first post.
â J Faucher
7 hours ago
You can still have that by making the above address
static
members of a structure assuming that autocomplete is able to show static members. If not, it can be inline member functions too.â Phil1970
5 hours ago
You can still have that by making the above address
static
members of a structure assuming that autocomplete is able to show static members. If not, it can be inline member functions too.â Phil1970
5 hours ago
@JFaucher I'm not an embedded systems person and haven't tested this, but wouldn't the autocomplete issue be resolved by declaring the reserved member private? (You can declare private members in a struct, and you can use
public:
and private:
as many times as you want, to get the correct ordering of the fields.)â Nathaniel
27 mins ago
@JFaucher I'm not an embedded systems person and haven't tested this, but wouldn't the autocomplete issue be resolved by declaring the reserved member private? (You can declare private members in a struct, and you can use
public:
and private:
as many times as you want, to get the correct ordering of the fields.)â Nathaniel
27 mins ago
add a comment |Â
up vote
5
down vote
How about a C++-ish way?
namespace GPIO
static volatile uint32_t &MAP0_MODER = *reinterpret_cast<uint32_t*>(0x4000);
static volatile uint32_t &MAP0_OTYPER = *reinterpret_cast<uint32_t*>(0x4004);
int main()
GPIO::MAP0_MODER = 42;
You get autocompletion because of the GPIO
namespace, and there is no need for dummy padding. Even, it is more clear what's going on, as you can see the address of each register, you don't have to rely on the compiler's padding behavior at all.
Even better any other comments I have made elsewhere. That should worth more than 1 vote.
â Phil1970
5 hours ago
This might possible optimize less well than a struct for access to multiple MMIO registers from the same function. With a pointer to the base address in a register, the compiler can use load/store instructions with immediate displacements, likeldr r0, [r4, #16]
, while compilers are more likely to miss that optimization with each address declared separately. GCC will probably load each GPIO address into a separate register. (From a literal pool, although some of them can be represented as rotated immediates in Thumb encoding.)
â Peter Cordes
4 hours ago
@PeterCordes: I've just checked this with clang, and it optimizes this case well. I haven't checked GCC, maybe it misses this optimization as you say.
â geza
4 hours ago
Turns out my worries were unfounded; ARM GCC does optimize this way, too. godbolt.org/z/ztB7hi. But note that you wantstatic volatile uint32_t &MAP0_MODER
, notinline
. Aninline
variable doesn't compile. (static
avoids having any static storage for the pointer, andvolatile
is exactly what you want for MMIO to avoid dead-store elimination or optimization of write / read-back.)
â Peter Cordes
3 hours ago
1
@PeterCordes: inline variables is a new C++17 feature. But you're right,static
does equally well for this case. Thanks for mentioningvolatile
, I'll add it to my answer (and change inline to static, so it works for pre C++17).
â geza
3 hours ago
 |Â
show 2 more comments
up vote
5
down vote
How about a C++-ish way?
namespace GPIO
static volatile uint32_t &MAP0_MODER = *reinterpret_cast<uint32_t*>(0x4000);
static volatile uint32_t &MAP0_OTYPER = *reinterpret_cast<uint32_t*>(0x4004);
int main()
GPIO::MAP0_MODER = 42;
You get autocompletion because of the GPIO
namespace, and there is no need for dummy padding. Even, it is more clear what's going on, as you can see the address of each register, you don't have to rely on the compiler's padding behavior at all.
Even better any other comments I have made elsewhere. That should worth more than 1 vote.
â Phil1970
5 hours ago
This might possible optimize less well than a struct for access to multiple MMIO registers from the same function. With a pointer to the base address in a register, the compiler can use load/store instructions with immediate displacements, likeldr r0, [r4, #16]
, while compilers are more likely to miss that optimization with each address declared separately. GCC will probably load each GPIO address into a separate register. (From a literal pool, although some of them can be represented as rotated immediates in Thumb encoding.)
â Peter Cordes
4 hours ago
@PeterCordes: I've just checked this with clang, and it optimizes this case well. I haven't checked GCC, maybe it misses this optimization as you say.
â geza
4 hours ago
Turns out my worries were unfounded; ARM GCC does optimize this way, too. godbolt.org/z/ztB7hi. But note that you wantstatic volatile uint32_t &MAP0_MODER
, notinline
. Aninline
variable doesn't compile. (static
avoids having any static storage for the pointer, andvolatile
is exactly what you want for MMIO to avoid dead-store elimination or optimization of write / read-back.)
â Peter Cordes
3 hours ago
1
@PeterCordes: inline variables is a new C++17 feature. But you're right,static
does equally well for this case. Thanks for mentioningvolatile
, I'll add it to my answer (and change inline to static, so it works for pre C++17).
â geza
3 hours ago
 |Â
show 2 more comments
up vote
5
down vote
up vote
5
down vote
How about a C++-ish way?
namespace GPIO
static volatile uint32_t &MAP0_MODER = *reinterpret_cast<uint32_t*>(0x4000);
static volatile uint32_t &MAP0_OTYPER = *reinterpret_cast<uint32_t*>(0x4004);
int main()
GPIO::MAP0_MODER = 42;
You get autocompletion because of the GPIO
namespace, and there is no need for dummy padding. Even, it is more clear what's going on, as you can see the address of each register, you don't have to rely on the compiler's padding behavior at all.
How about a C++-ish way?
namespace GPIO
static volatile uint32_t &MAP0_MODER = *reinterpret_cast<uint32_t*>(0x4000);
static volatile uint32_t &MAP0_OTYPER = *reinterpret_cast<uint32_t*>(0x4004);
int main()
GPIO::MAP0_MODER = 42;
You get autocompletion because of the GPIO
namespace, and there is no need for dummy padding. Even, it is more clear what's going on, as you can see the address of each register, you don't have to rely on the compiler's padding behavior at all.
edited 3 hours ago
answered 5 hours ago
geza
10.7k22669
10.7k22669
Even better any other comments I have made elsewhere. That should worth more than 1 vote.
â Phil1970
5 hours ago
This might possible optimize less well than a struct for access to multiple MMIO registers from the same function. With a pointer to the base address in a register, the compiler can use load/store instructions with immediate displacements, likeldr r0, [r4, #16]
, while compilers are more likely to miss that optimization with each address declared separately. GCC will probably load each GPIO address into a separate register. (From a literal pool, although some of them can be represented as rotated immediates in Thumb encoding.)
â Peter Cordes
4 hours ago
@PeterCordes: I've just checked this with clang, and it optimizes this case well. I haven't checked GCC, maybe it misses this optimization as you say.
â geza
4 hours ago
Turns out my worries were unfounded; ARM GCC does optimize this way, too. godbolt.org/z/ztB7hi. But note that you wantstatic volatile uint32_t &MAP0_MODER
, notinline
. Aninline
variable doesn't compile. (static
avoids having any static storage for the pointer, andvolatile
is exactly what you want for MMIO to avoid dead-store elimination or optimization of write / read-back.)
â Peter Cordes
3 hours ago
1
@PeterCordes: inline variables is a new C++17 feature. But you're right,static
does equally well for this case. Thanks for mentioningvolatile
, I'll add it to my answer (and change inline to static, so it works for pre C++17).
â geza
3 hours ago
 |Â
show 2 more comments
Even better any other comments I have made elsewhere. That should worth more than 1 vote.
â Phil1970
5 hours ago
This might possible optimize less well than a struct for access to multiple MMIO registers from the same function. With a pointer to the base address in a register, the compiler can use load/store instructions with immediate displacements, likeldr r0, [r4, #16]
, while compilers are more likely to miss that optimization with each address declared separately. GCC will probably load each GPIO address into a separate register. (From a literal pool, although some of them can be represented as rotated immediates in Thumb encoding.)
â Peter Cordes
4 hours ago
@PeterCordes: I've just checked this with clang, and it optimizes this case well. I haven't checked GCC, maybe it misses this optimization as you say.
â geza
4 hours ago
Turns out my worries were unfounded; ARM GCC does optimize this way, too. godbolt.org/z/ztB7hi. But note that you wantstatic volatile uint32_t &MAP0_MODER
, notinline
. Aninline
variable doesn't compile. (static
avoids having any static storage for the pointer, andvolatile
is exactly what you want for MMIO to avoid dead-store elimination or optimization of write / read-back.)
â Peter Cordes
3 hours ago
1
@PeterCordes: inline variables is a new C++17 feature. But you're right,static
does equally well for this case. Thanks for mentioningvolatile
, I'll add it to my answer (and change inline to static, so it works for pre C++17).
â geza
3 hours ago
Even better any other comments I have made elsewhere. That should worth more than 1 vote.
â Phil1970
5 hours ago
Even better any other comments I have made elsewhere. That should worth more than 1 vote.
â Phil1970
5 hours ago
This might possible optimize less well than a struct for access to multiple MMIO registers from the same function. With a pointer to the base address in a register, the compiler can use load/store instructions with immediate displacements, like
ldr r0, [r4, #16]
, while compilers are more likely to miss that optimization with each address declared separately. GCC will probably load each GPIO address into a separate register. (From a literal pool, although some of them can be represented as rotated immediates in Thumb encoding.)â Peter Cordes
4 hours ago
This might possible optimize less well than a struct for access to multiple MMIO registers from the same function. With a pointer to the base address in a register, the compiler can use load/store instructions with immediate displacements, like
ldr r0, [r4, #16]
, while compilers are more likely to miss that optimization with each address declared separately. GCC will probably load each GPIO address into a separate register. (From a literal pool, although some of them can be represented as rotated immediates in Thumb encoding.)â Peter Cordes
4 hours ago
@PeterCordes: I've just checked this with clang, and it optimizes this case well. I haven't checked GCC, maybe it misses this optimization as you say.
â geza
4 hours ago
@PeterCordes: I've just checked this with clang, and it optimizes this case well. I haven't checked GCC, maybe it misses this optimization as you say.
â geza
4 hours ago
Turns out my worries were unfounded; ARM GCC does optimize this way, too. godbolt.org/z/ztB7hi. But note that you want
static volatile uint32_t &MAP0_MODER
, not inline
. An inline
variable doesn't compile. (static
avoids having any static storage for the pointer, and volatile
is exactly what you want for MMIO to avoid dead-store elimination or optimization of write / read-back.)â Peter Cordes
3 hours ago
Turns out my worries were unfounded; ARM GCC does optimize this way, too. godbolt.org/z/ztB7hi. But note that you want
static volatile uint32_t &MAP0_MODER
, not inline
. An inline
variable doesn't compile. (static
avoids having any static storage for the pointer, and volatile
is exactly what you want for MMIO to avoid dead-store elimination or optimization of write / read-back.)â Peter Cordes
3 hours ago
1
1
@PeterCordes: inline variables is a new C++17 feature. But you're right,
static
does equally well for this case. Thanks for mentioning volatile
, I'll add it to my answer (and change inline to static, so it works for pre C++17).â geza
3 hours ago
@PeterCordes: inline variables is a new C++17 feature. But you're right,
static
does equally well for this case. Thanks for mentioning volatile
, I'll add it to my answer (and change inline to static, so it works for pre C++17).â geza
3 hours ago
 |Â
show 2 more comments
up vote
2
down vote
Use multiple adjacent anonymous bitfields. So instead of:
uint32_t :160;
for example, you'd have:
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
One for each register you want to be anonymous.
Thanks for the answer. I already considered that, however it would add over 200 lines on some of my files (uint32_t :1344;
is in the place) so I would rather not have to go this way...
â J Faucher
7 hours ago
add a comment |Â
up vote
2
down vote
Use multiple adjacent anonymous bitfields. So instead of:
uint32_t :160;
for example, you'd have:
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
One for each register you want to be anonymous.
Thanks for the answer. I already considered that, however it would add over 200 lines on some of my files (uint32_t :1344;
is in the place) so I would rather not have to go this way...
â J Faucher
7 hours ago
add a comment |Â
up vote
2
down vote
up vote
2
down vote
Use multiple adjacent anonymous bitfields. So instead of:
uint32_t :160;
for example, you'd have:
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
One for each register you want to be anonymous.
Use multiple adjacent anonymous bitfields. So instead of:
uint32_t :160;
for example, you'd have:
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
One for each register you want to be anonymous.
answered 7 hours ago
Clifford
57k857121
57k857121
Thanks for the answer. I already considered that, however it would add over 200 lines on some of my files (uint32_t :1344;
is in the place) so I would rather not have to go this way...
â J Faucher
7 hours ago
add a comment |Â
Thanks for the answer. I already considered that, however it would add over 200 lines on some of my files (uint32_t :1344;
is in the place) so I would rather not have to go this way...
â J Faucher
7 hours ago
Thanks for the answer. I already considered that, however it would add over 200 lines on some of my files (
uint32_t :1344;
is in the place) so I would rather not have to go this way...â J Faucher
7 hours ago
Thanks for the answer. I already considered that, however it would add over 200 lines on some of my files (
uint32_t :1344;
is in the place) so I would rather not have to go this way...â J Faucher
7 hours ago
add a comment |Â
up vote
2
down vote
To expand on Clifford's answer, you can always macro out the anonymous bitfields.
So instead of
uint32_t :160;
use
#define EMPTY_32_1
uint32_t :32
#define EMPTY_32_2
uint32_t :32; // I guess this also can be replaced with uint64_t :64
uint32_t :32
#define EMPTY_32_3
uint32_t :32;
uint32_t :32;
uint32_t :32
#define EMPTY_UINT32(N) EMPTY_32_ ## N
And then use it like
struct A
EMPTY_UINT32(3);
/* which resolves to EMPTY_32_3, which then resolves to real declarations */
Unfortunately, you'll need as many EMPTY_32_X
variants as many bytes you have :(
Still, it allows you to have single declarations in your struct.
1
Using Boost CPP macros, I think you can use recursion to avoid having to hand-create all the necessary macros.
â Peter Cordes
4 hours ago
You can cascade them (up to preprocessor recursion limit, but that's usually ample). So#define EMPTY_32_2 EMPTY_32_1; EMPTY_32_1
and#define EMPTY_32_3 EMPTY_32_2; EMPTY_32_1
etc.
â Miral
2 hours ago
add a comment |Â
up vote
2
down vote
To expand on Clifford's answer, you can always macro out the anonymous bitfields.
So instead of
uint32_t :160;
use
#define EMPTY_32_1
uint32_t :32
#define EMPTY_32_2
uint32_t :32; // I guess this also can be replaced with uint64_t :64
uint32_t :32
#define EMPTY_32_3
uint32_t :32;
uint32_t :32;
uint32_t :32
#define EMPTY_UINT32(N) EMPTY_32_ ## N
And then use it like
struct A
EMPTY_UINT32(3);
/* which resolves to EMPTY_32_3, which then resolves to real declarations */
Unfortunately, you'll need as many EMPTY_32_X
variants as many bytes you have :(
Still, it allows you to have single declarations in your struct.
1
Using Boost CPP macros, I think you can use recursion to avoid having to hand-create all the necessary macros.
â Peter Cordes
4 hours ago
You can cascade them (up to preprocessor recursion limit, but that's usually ample). So#define EMPTY_32_2 EMPTY_32_1; EMPTY_32_1
and#define EMPTY_32_3 EMPTY_32_2; EMPTY_32_1
etc.
â Miral
2 hours ago
add a comment |Â
up vote
2
down vote
up vote
2
down vote
To expand on Clifford's answer, you can always macro out the anonymous bitfields.
So instead of
uint32_t :160;
use
#define EMPTY_32_1
uint32_t :32
#define EMPTY_32_2
uint32_t :32; // I guess this also can be replaced with uint64_t :64
uint32_t :32
#define EMPTY_32_3
uint32_t :32;
uint32_t :32;
uint32_t :32
#define EMPTY_UINT32(N) EMPTY_32_ ## N
And then use it like
struct A
EMPTY_UINT32(3);
/* which resolves to EMPTY_32_3, which then resolves to real declarations */
Unfortunately, you'll need as many EMPTY_32_X
variants as many bytes you have :(
Still, it allows you to have single declarations in your struct.
To expand on Clifford's answer, you can always macro out the anonymous bitfields.
So instead of
uint32_t :160;
use
#define EMPTY_32_1
uint32_t :32
#define EMPTY_32_2
uint32_t :32; // I guess this also can be replaced with uint64_t :64
uint32_t :32
#define EMPTY_32_3
uint32_t :32;
uint32_t :32;
uint32_t :32
#define EMPTY_UINT32(N) EMPTY_32_ ## N
And then use it like
struct A
EMPTY_UINT32(3);
/* which resolves to EMPTY_32_3, which then resolves to real declarations */
Unfortunately, you'll need as many EMPTY_32_X
variants as many bytes you have :(
Still, it allows you to have single declarations in your struct.
edited 3 hours ago
answered 5 hours ago
Adam Kotwasinski
1,664521
1,664521
1
Using Boost CPP macros, I think you can use recursion to avoid having to hand-create all the necessary macros.
â Peter Cordes
4 hours ago
You can cascade them (up to preprocessor recursion limit, but that's usually ample). So#define EMPTY_32_2 EMPTY_32_1; EMPTY_32_1
and#define EMPTY_32_3 EMPTY_32_2; EMPTY_32_1
etc.
â Miral
2 hours ago
add a comment |Â
1
Using Boost CPP macros, I think you can use recursion to avoid having to hand-create all the necessary macros.
â Peter Cordes
4 hours ago
You can cascade them (up to preprocessor recursion limit, but that's usually ample). So#define EMPTY_32_2 EMPTY_32_1; EMPTY_32_1
and#define EMPTY_32_3 EMPTY_32_2; EMPTY_32_1
etc.
â Miral
2 hours ago
1
1
Using Boost CPP macros, I think you can use recursion to avoid having to hand-create all the necessary macros.
â Peter Cordes
4 hours ago
Using Boost CPP macros, I think you can use recursion to avoid having to hand-create all the necessary macros.
â Peter Cordes
4 hours ago
You can cascade them (up to preprocessor recursion limit, but that's usually ample). So
#define EMPTY_32_2 EMPTY_32_1; EMPTY_32_1
and #define EMPTY_32_3 EMPTY_32_2; EMPTY_32_1
etc.â Miral
2 hours ago
You can cascade them (up to preprocessor recursion limit, but that's usually ample). So
#define EMPTY_32_2 EMPTY_32_1; EMPTY_32_1
and #define EMPTY_32_3 EMPTY_32_2; EMPTY_32_1
etc.â Miral
2 hours ago
add a comment |Â
J Faucher is a new contributor. Be nice, and check out our Code of Conduct.
J Faucher is a new contributor. Be nice, and check out our Code of Conduct.
J Faucher is a new contributor. Be nice, and check out our Code of Conduct.
J Faucher is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53109888%2fhow-do-i-create-a-spacer-in-a-c-class-memory-structure%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
8
have you consided
char unused[12];
and so on?â M.M
8 hours ago
1
This is a well done question. Good job! I'd like to know the answer myself.
â Christopher Pisz
8 hours ago
2
I would just suppress the warning. [class.bit]/1 guarantees the behavior of
uint32_t :192;
.â NathanOliver
8 hours ago
2
@NathanOliver I would gladly too, but it seems that this warning is not suppressible (Using GCC) or I didn't find how to do so. Moreover, it still is not a clean way to do (but it would be pretty satisfying) . I managed to find the correct "-W" flag but didn't manage to apply it only on my own files (I don't want the user to remove this kind of warnings for his work)
â J Faucher
8 hours ago
1
BTW you can write
:42*32
instead of:1344
â M.M
8 hours ago