4

I'm trying to write defensive code and put static_assert<> to ensure a structure's member has a specific offset to satisfy some hardware requirements

MISRA C++ Rule 18-2-1 says "The macro offsetof shall not be used", so we've 'undef'd offsetof.

We've provided some template things, but they all fail when used in static_assert<>

I've been unable to find something that works with C++11 static_assert<> across compilers.

struct Commands
{
    uint32_t command[4];
};

struct DMA_Bundle
{
    struct header_
    {
        uint32_t num_of_elems;
        uint8_t reserved[60];
    } header;
    Commands array[16];
};

I'm trying to assure that array is 64 bytes from the beginning.

static_assert(offsetof(DMA_Bundle,array)==64,"zoinks");

Does works, but MISRA says but I can't use that. (and I can't argue with our functional safety people :/)

I've tried the following, and generally they don't work:

static_assert(offsetofarray()==64,"bar");
static_assert(myoffsetof(DMA_Bundle::array)==64,"double_zoinks");

The explicit offsetofarray() constexpr function does work under GCC 7.5.0, but it fails under later GCC and Clang (which is what our embedded processor tools use). It fails with 'non constant condition for static_assert'.

The other seems to complain of "invalid use of non-static data member 'DMA_Bundle::array'"

And, for what it's worth, I'm limited to C++11.

Russ Schultz
  • 2,545
  • 20
  • 22
  • So why not just assert that the size of the header is 64 bytes? You already have that part neatly compartmentalized. – Taekahn Apr 20 '22 at 04:03
  • @Taekahn just trying to be defensive and ensure nothing else changes in the future or somebody adds another variable. – Russ Schultz Apr 20 '22 at 04:28
  • You could always write a test that fails if the array isn’t where you expect. Write 64 bytes of data to the object then access the array through normal means and see if you have the correct data at the start of the array – Taekahn Apr 20 '22 at 04:30
  • You could try the [hacky implementations](https://en.m.wikipedia.org/wiki/Offsetof). Although they have undefined behavior, maybe they do work in your case. – Sedenion Apr 20 '22 at 06:04
  • 2
    "and I can't argue with our functional safety people" They can't be the ones making the calls for when to do deviations. It should rather be a decision made by one or several of the most seasoned C programmers in the company. Otherwise you are _creating_ hazards instead of avoiding them. – Lundin Apr 20 '22 at 10:29
  • Anyway, a work-around might be `static_assert(sizeof (DMA_Bundle::header_) == 64, "yikes");` This takes possible trailing padding of the struct in account so it ought to be the very same thing as the `offsetof` macro. – Lundin Apr 20 '22 at 10:59

1 Answers1

5

Some background:

And, for what it's worth, I'm limited to C++11

And here is your first problem...

MISRA C++:2008 only allows the use of C++:2003 - anything after this is out of scope.

MISRA C++ Rule 18-2-1 says "The macro offsetof shall not be used", so we've 'undef'd offsetof.

Use of #undef is a violation of required Rule 16-0-3 (and it is unnecessary to #undef offsetof)


Now to address the main point:

Rule 18-2-1 is a required Rule which seeks to protect against undefined behaviour when the types of the operands are incompatible.

Does works, but MISRA says but I can't use that. (and I can't argue with our functional safety people :/)

The problem with many clip-board monitors is they can tick boxes, but adopt a "MISRA says no" attitude without understanding what MISRA actually says...

My suggestion, especially if only being used when associated with static_assert() is to raise a deviation (see section 4.3.2 and the extended guidance in MISRA Compliance).

A deviation is a perfectly legitimate approach, where a Rule may be causing an inconvenience, but the undefined behaviour is not applicable.


ETA (noting discussion with Lundin about C): In MISRA C:2004, there was an equivalent guideline (Rule 20.6) which likewise imposed a blanket restriction on offsetof - in MISRA C:2012 this blanket restriction was dropped, with the undefined behaviour captured by the general Rule 1.3

In many respects, this makes justification of a MISRA C++ deviation easier - as the rationale would be matching the MISRA C decision.

See profile for affiliation

Andrew
  • 2,046
  • 1
  • 24
  • 37
  • exactly, instead of the cure that kills the patient. – AndersK Apr 20 '22 at 06:19
  • 1
    This rule was dropped in MISRA C:2012 so maybe make a permanent deviation. From what I can tell from the rationale, the main reason is that `offsetof` is unreliable when using bit-fields (C17 7.19/3). The correct and sound solution to that problem is to ban the root cause, namely the use of bit-fields in safety-related software. – Lundin Apr 20 '22 at 10:40
  • @Lundin - I've deleted that comment, and updated my answer more fully. – Andrew Apr 21 '22 at 06:19