1

I'm always trying to reduce code verbosity and this question is about that. I think I need a standards expert to explain why my attempt doesn't work, I've done my best to figure it out but have failed.

The goal is to better compose unions. In standards documents such as USB-C PD controller specifications the registers are set out in distinct 8 bit sections.

Universal Serial Bus Type-C TM Port Controller Interface Specification

When coding to meet the above standard I prefer to set out data in an identical fashion and then put abstractions on top of the standard.

For example, there is a register called ALERT_H and ALERT_L, these I represent as two single byte bitfield structures (a common practice in embedded).

When creating a simplifying abstraction I want to combine the two structures into an equivalent structure with a union so that I can check if any bits at all are set in a single if statement (if (Flags) sort of thing).

An Example:

#include <cstdint>

struct ALERT_L
{
    uint8_t CC_STATUS : 1;
    uint8_t POWER_STATUS : 1;
    uint8_t RX_SOP_MSG_STATUS : 1;
    uint8_t RX_HARD_RESET : 1;
    uint8_t TX_FAIL : 1;
    uint8_t TX_DISCARD : 1;
    uint8_t TX_SUCCESS : 1;
    uint8_t const ALARM_VBUS_VOLTAGE_H : 1;
};
struct ALERT_H
{
    uint8_t const ALARM_VBUS_VOLTAGE_L : 1;
    uint8_t FAULT : 1;
    uint8_t RXBUF_OVFLOW : 1;
    uint8_t const VBUS_SINK_DISCNT : 1;
    uint8_t Reserved4_7 : 1;
};
/**
  * Helper to group the alert bits into a single struct
  */
union ALERT
{
    struct : ALERT_H, ALERT_L
    {

    };
    uint16_t Value;
};
static_assert(sizeof(ALERT) == sizeof(uint16_t));
static_assert(sizeof(ALERT) == sizeof(ALERT_H) + sizeof(ALERT_L));

While the above code does compile, I cannot access the anonymous structs CC_STATUS bit or any others, I don't understand why, am I doing something wrong? Is there a way that I can expose that data member without coding getters and setters.

I can achieve this using standard composition but that results in more verbose code which is more verbose than dealing with the 8 bit structs as they are. There are also cases in other standards that do a similar thing but have 4 bytes, which further incentives the interface implication.

I want to achieve this with the anonymous struct or something similar because then the abstraction is transparent and reduces verbosity of use.

 alert.regs.CC_STATUS

is more verbose than:

alert.CC_STATUS

This might be impossible in C/C++ but I've thought things were impossible before and been wrong plenty of times.

David Ledger
  • 2,033
  • 1
  • 12
  • 27
  • 1
    In terms of standards, what you are attempting to do is undefined behaviour. There can only be one active member of a union at a time, it is not defined to set the bit-fields and then read `Value` or vice versa – M.M Jun 27 '19 at 05:47
  • Thanks, you might be right, but I though that didn't apply to annoymous unions for backward compatibillity with C? – David Ledger Jun 27 '19 at 05:52
  • I looked around to support what I said, your right as far as I can tell. Is it safe given the static_asserts I placed in the program given knowledge of the target system memory order? – David Ledger Jun 27 '19 at 06:03
  • It's relying on the compiler to support the non-standard behaviour – M.M Jun 27 '19 at 06:19
  • 1
    UB aside, anonymous unions do not work this way. The struct definition inside ALERT only defines a *class* (anonymous and thus useless) and not a data member. – n. m. could be an AI Jun 27 '19 at 06:19
  • They do define a data member as far as I'm aware see this: https://onlinegdb.com/r1wtWJfeB Unless the inheritance breaks that? – David Ledger Jun 27 '19 at 06:26
  • @M.M Thanks yes it definately is, I just don't know an elegant alternative. I think the static_asserts would catch the issue before it became one. – David Ledger Jun 27 '19 at 06:28
  • @n.m. Your right! https://onlinegdb.com/B1b2dJfer Case A doesn't compile: Case B does. Why does inheritance for the anon struct mess it up? – David Ledger Jun 27 '19 at 06:57
  • No, I'm actually wrong! The anonymous struct does define a data member. The problem is, only direct members of **that** struct are injected into the union, not members of anything that it inherits. – n. m. could be an AI Jun 27 '19 at 07:45
  • And another problem is, such anonymous structs are not a part of C++ at all, compile with -pedantic-errors and they get rejected. See https://stackoverflow.com/questions/16202576/c-anonymous-structs – n. m. could be an AI Jun 27 '19 at 07:49
  • @n.m I'm not sure thats right either, because in my example "Case A" has a member called "Value", which is inaccessable when the struct inherits from something. The "Value" member is not part of the base class. – David Ledger Jun 27 '19 at 09:06
  • 1
    None of it is standard anyway, but https://ideone.com/JWtJ5E – n. m. could be an AI Jun 27 '19 at 10:53
  • @n.m. Woah! I suppose the inconsistency between even GCC versions is a pretty good reason to avoid that... Thanks! – David Ledger Jun 27 '19 at 12:50

0 Answers0