# define_struct

Definition: | include cffi.e object x = define_struct(string struct_str, integer machine=machine_bits(), bool bAdd=true)
Description: | parse a C struct definition into sizes/offsets etc. Note: This functionality is now wrapped by c-struct, which you may find slightly easier to use, however that will not play nice with any embedded TCHAR[] or similar fields.
pwa/p2js: | Not supported.
Comments: | The struct_str parameter is text copied from a C header file - note that without a "typedef", nothing gets stored permanantly. The machine parameter can be set to 32 or 64, for testing purposes. The bAdd parameter can be set to false for testing (override/ignore "typedef") If struct_str begins `#pragma pack(1)` the usual padding to 2/4/8 byte boundaries is not performed (32-bit only). This has been specifically added to cope with commdlg.h which contains
                #if !defined(_WIN64)
                #include <pshpack1.h>       /* Assume byte packing throughout */
                #endif
hence that pragma (currently) only applies under 32-bit. No other variant of pragma pack is [yet] supported. If bAdd is true and struct_str contains "typedef", the return value is a small integer id that can be used in calls to allocate_struct(), set_struct_field(), get_struct_field(), get_field_details() and get_struct_size(). Otherwise the full details of the structure are returned, which you can display, use to write a little help file, or perhaps even directly use the sizes and offsets etc. Typically this is only useful for testing/diagnostic purposes, see the dropdown below for more info. The struct definition may contain comments, however they must be C-style, ie/eg `// comment` or `/* comment */`, and not `-- comment`.
Example 1: |
    include cffi.e
    constant tMBP="""
    typedef struct {
      UINT           cbSize;
      HWND           hwndOwner;
      HINSTANCE      hInstance;
      LPCTSTR        lpszText;
      LPCTSTR        lpszCaption;
      DWORD          dwStyle;
      LPCTSTR        lpszIcon;
      DWORD_PTR      dwContextHelpId;
      MSGBOXCALLBACK lpfnMsgBoxCallback;
      DWORD          dwLanguageId;
    } MSGBOXPARAMS, *PMSGBOXPARAMS;
    """
    constant integer idMBP = define_struct(tMBP)
Example 2: |
    include cffi.e
    constant tRECT = """
                        typedef struct _RECT {
                          LONG left;
                          LONG top;
                          LONG right;
                          LONG bottom;
                        } RECT, *PRECT;""",
             tPS = """
                        typedef struct tagPAINTSTRUCT {
                          HDC  hdc;
                          BOOL fErase;
                          RECT rcPaint;
                          BOOL fRestore;
                          BOOL fIncUpdate;
                          BYTE rgbReserved[32];
                        } PAINTSTRUCT, *PPAINTSTRUCT;""",
            idRECT = define_struct(tRECT)
            idPS = define_struct(tPS)
Structures can be nested - tPS can use "RECT", but only after tRECT has been processed. NB: in tRECT, _RECT is discarded/replaced by RECT. Had that struct ended with "};" then _RECT would have been kept, and RECT (etc) ignored. The "sequence structs" in cffi.e currently holds only one name per struct. (see notes therein) In contrast, pointer names such as PRECT are (all) kept separately, albeit to cffi.e they are just pointers that would explicitly need the correct id (eg/ie idRECT) to delve any deeper. Note there is no "rcPaint", instead we have "rcPaint.left" etc.
Bitfields: | Note that cffi.e does not (yet) support bitfields, for example
    include cffi.e
    constant tDCB = """
    typedef struct _DCB {
      DWORD DCBlength;
      DWORD BaudRate;
      DWORD fBinary : 1;
      DWORD fParity : 1;
      DWORD fOutxCtsFlow : 1;
      DWORD fOutxDsrFlow : 1;
      DWORD fDtrControl : 2;
      DWORD fDsrSensitivity : 1;
      DWORD fTXContinueOnXoff : 1;
      DWORD fOutX : 1;
      DWORD fInX : 1;
      DWORD fErrorChar : 1;
      DWORD fNull : 1;
      DWORD fRtsControl : 2;
      DWORD fAbortOnError : 1;
      DWORD fDummy2 : 17;
      WORD  wReserved;
      WORD  XonLim;
      WORD  XoffLim;
      BYTE  ByteSize;
      BYTE  Parity;
      BYTE  StopBits;
      char  XonChar;
      char  XoffChar;
      char  ErrorChar;
      char  EofChar;
      char  EvtChar;
      WORD  wReserved1;
    } DCB, *LPDCB;""",
    idDCB = define_struct(tDCB)
fails with the (somewhat obtuse) error "bitfields are not (yet) supported". In this particular case, the 32 bits of fBinary .. fDummy2 are packed into a single 32-bit dword, but note should the :1 .. :17 over-run it ought to allocate further dwords as needed - if you encounter an example that does that, or slices byte/word[s]/etc let me know, ideally along with something I can properly test. It would (probably) not be particularly difficult to extend cffi.e to cope (eg -ve offset/size/signed), but for now you should replace those 14 fields with a single dword and perform the necessary bit-slicing on that yourself, again let me know should that start getting too tedious or too error-prone.
See Also: | allocate_struct, set_struct_field, get_struct_field, get_field_details, set_unicode, machine_bits
Technicalia | The full details (when bAdd=false or "typedef" is missing) are `{name,size,align,members,ptrnames}` where members is `{{names},{{type,size,offset,sign}}}`, eg:
         {"RECT", 16,4,{{"left", "top", "right", "bottom"},
                       {{"LONG", 4,0,1}, {"LONG", 4,4,1},
                        {"LONG", 4,8,1}, {"LONG", 4,12,1}}},
         {"PRECT"}}
So a RECT is 16 bytes and needs 4 byte alignment, and RECT.bottom is a long, 4 bytes, at offset 12, and is signed. Note that members.type (eg "LONG") is only for debugging/sanity and is not intended or suitable for any practical use. You can set the convert_types flag via set_unicode() to convert any AltNames and UnicodeNames to their SizeNames equivalent, which would reduce the number of types to be dealt with around five-fold, if you like, but I suspect in most cases that would just add more confusion. Also note that members.size is for a single element: a char[32] has a size of 1 not a size of 32. It would be difficult to ascertain the number of elements (such as that 32) from the return value, particularly if nested structs and unions are involved, instead it is assumed that the calling code is happy enough just to know where the first is. These details are only intended for testing and debugging purposes.