How to declare union inside a record structure?
Asked Answered
Y

1

7

I am trying to define the TWaveFormatExtensible type, but I am not sure if am I declaring correctly the Samples union. Here is the original declaration from header file (Windows SDK 10.0.17763.0):

typedef struct {
    WAVEFORMATEX    Format;
    union {
        WORD wValidBitsPerSample;       /* bits of precision  */
        WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
        WORD wReserved;                 /* If neither applies, set to zero. */
    } Samples;
    DWORD           dwChannelMask;      /* which channels are */
                                        /* present in stream  */
    GUID            SubFormat;
}

And this is what I've tried:

type
  TWAVEFORMATEX = record
    wFormatTag: Word;
    nChannels: LongWord;
    nSamplesPerSec: Word;
    nAvgBytesPerSec: LongWord;
    nBlockAlign: Word;
    wBitsPerSample: Word;
    cbSize: Word;
  end;

  TWaveFormatExtensible = record
    Format: TWAVEFORMATEX;
    dwChannelMask: LongWord;
    SubFormat: Integer;
    case Word of
      0: (wValidBitsPerSample: Word;);
      1: (wSamplesPerBlock: Word;);
      2: (wReserved: Word;);
  end;

But that's not correct. How would one declare a union inside a record structure in Delphi?

Yahwistic answered 9/11, 2019 at 18:55 Comment(3)
IIRC, Delphi 7 predates the invention of WAVEFORMATEXTENSIBLE. What is stopping you from simply declaring it yourself in your own code?Thermoelectricity
@RemyLebeau: How to declare the union Samples?Yahwistic
P.S. SubFormat is of type GUID (TGUID in Delphi), not Integer. And member nChannels is of type WORD and nSamplesPerSec is DWORD. Except that you should preferrably use native types, e.g. DWORD members declare as DWORD types.Dragonet
J
11

The fields of the structure must be in the same order as in the original (C++) declaration. But there's a problem: the original declaration puts the Samples variant in the middle of the record and that is not allowed in Delphi.

You can solve this by declaring the variant part as a separate record and then include that record as a field in the final structure.

TWaveFormatExtensibleSamples = record
case Word of
  0: (wValidBitsPerSample: Word;);
  1: (wSamplesPerBlock: Word;);
  2: (wReserved: Word;);
end;

and then construct the final structure:

TWaveFormatExtensible = record
  Format: TWAVEFORMATEX;
  Samples: TWaveFormatExtensibleSamples;
  dwChannelMask: DWORD;
  SubFormat: TGUID; 
end;

edit: The documentation for records with variant parts, state:

A record type can have a variant part, which looks like a case statement. The variant part must follow the other fields in the record declaration.

This concerns variant parts without an enclosing record declaration.

However, as Remy Lebeau pointed out, a record with the variant part can be directly declared in the TWaveFormatExtensible declaration as part of the structure, in between other fields:

TWaveFormatExtensible = record
  Format: TWAVEFORMATEX;
  Samples: record
    case Word of
    0: (wValidBitsPerSample: Word;);
    1: (wSamplesPerBlock: Word;);
    2: (wReserved: Word;);
  end;
  dwChannelMask: DWORD;
  SubFormat: TGUID;
end;

So this can be used as well as the separately declared TWaveFormatExtensibleSamples record.

Johnie answered 9/11, 2019 at 20:45 Comment(2)
In this case, because Samples is not an anonymous union in the C declaration, you can merge TWaveFormatExtensibleSamples directly into TWaveFormatExtensible, it doesn't need to be separated: TWaveFormatExtensible = record ... Samples: record case Word of ... end; ... end;Thermoelectricity
Thank you @Remy for your commentJohnie

© 2022 - 2024 — McMap. All rights reserved.