Page 1 of 1

Does struct support only up to 32 parameters?

Posted: Mon Aug 21, 2017 6:33 pm
by psilwen

Code: Select all

newLISP v.10.7.3 32-bit on Windows IPv4/6 libffi, options: newlisp -h
; 33 "char"
(struct 'abc "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char" "char")
abc
> (pack abc)
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\00
0\000\000\000\000\000\000\000\000\000\000\000\000"
> (length (pack abc))
32
> (length (pack abc (sequence 1 33)))
32

Code: Select all

newLISP v.10.7.3 32-bit on Windows IPv4/6 UTF-8 libffi, options: newlisp -h
> (apply struct (cons 'a  (flat (dup (list "char") 16))))
a
> (length (pack a))
16
> (apply struct (cons 'b  (flat (dup (list "char") 32))))
b
> (length (pack b))
32
> (apply struct (cons 'c  (flat (dup (list "char") 64))))
c
> (length (pack c))
32     ; <-------- why is not 64?
> (apply struct (cons 'd  (flat (dup (list "char") 128))))
d
> (length (pack d))
32     ; <-------- why is not 128?

Code: Select all

> (apply struct (cons 'A  (flat (dup (list "unsigned short int") 16))))
A
> (length (pack A))
32
> (apply struct (cons 'B  (flat (dup (list "unsigned short int") 32))))
B
> (length (pack B))
64
> (apply struct (cons 'C  (flat (dup (list "unsigned short int") 64))))
C
> (length (pack C))
64     ; <-------- why is not 128?
> (apply struct (cons 'D  (flat (dup (list "unsigned short int") 128))))
D
> (length (pack D))
64     ; <-------- why is not 256?

Code: Select all

> (apply struct (cons 'AA  (flat (dup (list "unsigned int") 16))))
AA
> (length (pack AA))
64
> (apply struct (cons 'BB  (flat (dup (list "unsigned int") 32))))
BB
> (length (pack BB))
128
> (apply struct (cons 'CC  (flat (dup (list "unsigned int") 64))))
CC
> (length (pack CC))
128     ; <-------- why is not 256?
> (apply struct (cons 'DD  (flat (dup (list "unsigned int") 128))))
DD
> (length (pack DD))
128     ; <-------- why is not 512?

Re: Does struct support only up to 32 parameters?

Posted: Mon Aug 21, 2017 8:02 pm
by Lutz
You cannot have more than 32 members in the top level of a structure. Use sub-structs to pack more together.

Code: Select all

newLISP v.10.7.3 64-bit on OSX IPv4/6 UTF-8 libffi, options: newlisp -h

> (apply struct (cons 'b  (flat (dup (list "char") 32))))
b
> (length (pack b))
32
> (struct 'comp "b" "b")
comp
> (length (pack comp))
64
> (struct 'bigcomp "b" "b" "b" "b")
bigcomp
> (length (pack bigcomp))
128
This should probably be documented, but hasn’t because more that 32 members never occur in the top level of a ‘C’ structure (may be this is a compiler maximum). The biggest I have ever seen, is the ‘C’ ‘tm’ structure used in the example in the manual with 11 members. ‘struct’ in newLISP is used for importing functions.

Re: Does struct support only up to 32 parameters?

Posted: Wed Aug 23, 2017 4:18 am
by psilwen
Thank you for the explanation.
But the struct function used to deal with ordinary C structure is also very useful.

In newLISP, we have two way to deal with C structure.
The first way, with the struct , pack and unpack.
The other way, without the struct, uses only pack and unpack.

But the 'struct' has its advantages.

Code: Select all

; typedef struct {
;     unsigned long  Data1;
;     unsigned short Data2;
;     unsigned short Data3;
;     byte           Data4[6];
; } fakeGUID;
(struct 'fakeGUID.Data4 "byte" "byte" "byte" "byte" "byte" "byte")
(struct 'fakeGUID "unsigned int" "unsigned short int" "unsigned short int" "GUID.Data4")

> (pack fakeGUID) ; auto fill with 0 when pack
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
> (length(pack fakeGUID))
16
> (pack fakeGUID.Data4)
"\000\000\000\000\000\000"
> (length(pack fakeGUID.Data4))
6
> (setq fg (pack fakeGUID 1 2 3 (pack fakeGUID.Data4 4 5 6 7 8 9))) ; structure nested
"\001\000\000\000\002\000\003\000\004\005\006\007\b\t\000\000" ; structure aligned
> (length fg)
16
> (unpack fakeGUID fg)
(1 2 3 (4 5 6 7 8 9)) ;nested structures are unpacked recursively.

> (setq fakeGUID2 "lu u u bbbbbb")
> (pack fakeGUID2)
""
> (setq fg2 (pack fakeGUID2 0 0 0 0 0 0 0 0 0)) ; we have to give the data explicitly
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
> (length fg2)
14

Code: Select all

typedef struct
{
  int a1;
  int a2;
  int a3;
  int a4;
}A;
typedef struct
{
  int b1;
  int b2;
  int b3;
  int b4;
}B;
typedef struct
{
  int c1;
  int c2;
  int c3;
  int c4;
}C;
typedef struct
{
  int d1;
  int d2;
  int d3;
  int d4;
}D;
typedef struct
{
  A a[10];
  B b[10];
  C c[10];
  D d[10];
}ABCD;
without the 'struct', how uses only pack and unpack to deal with?

In fact, I'm trying to translate some Win32 C header files into newLISP syntax. There are a lot of C structures in these header files.
In these structures, there are some members that are arrays.
The data structures like these, is not easy to deal with in newLISP, fortunately we have the 'struct' function,
coupled with special treatment, I basically can be directly from the C structure translated into newLISP syntax,
make it becomes easy deal with.

for example

Code: Select all

typedef struct {
    unsigned long  Data1;
    unsigned short Data2;
    unsigned short Data3;
    byte           Data4[ 8 ];
} GUID;

typedef struct _NOTIFYICONDATAA
{
    DWORD cbSize;
    HWND hWnd;
    UINT uID;
    UINT uFlags;
    UINT uCallbackMessage;
    HICON hIcon;
    #if (NTDDI_VERSION < NTDDI_WIN2K)
        CHAR szTip[64];
    #endif 
    #if (NTDDI_VERSION >= NTDDI_WIN2K)
        CHAR szTip[128];
        DWORD dwState;
        DWORD dwStateMask;
        CHAR szInfo[256];
        union
        {
            UINT uTimeout;
            UINT uVersion; // used with NIM_SETVERSION, values 0, 3 and 4
        } DUMMYUNIONNAME;
        CHAR szInfoTitle[64];
        DWORD dwInfoFlags;
    #endif 
    #if (NTDDI_VERSION >= NTDDI_WINXP)
        GUID guidItem;
    #endif 
    #if (NTDDI_VERSION >= NTDDI_VISTA)
        HICON hBalloonIcon;
    #endif 
} NOTIFYICONDATAA,  *PNOTIFYICONDATAA;

typedef struct _NOTIFYICONDATAW
{
    DWORD cbSize;
    HWND hWnd;
    UINT uID;
    UINT uFlags;
    UINT uCallbackMessage;
    HICON hIcon;
    #if (NTDDI_VERSION < NTDDI_WIN2K)
        WCHAR szTip[64];
    #endif 
    #if (NTDDI_VERSION >= NTDDI_WIN2K)
        WCHAR szTip[128];
        DWORD dwState;
        DWORD dwStateMask;
        WCHAR szInfo[256];
        union
        {
            UINT uTimeout;
            UINT uVersion; // used with NIM_SETVERSION, values 0, 3 and 4
        } DUMMYUNIONNAME;
        WCHAR szInfoTitle[64];
        DWORD dwInfoFlags;
    #endif 
    #if (NTDDI_VERSION >= NTDDI_WINXP)
        GUID guidItem;
    #endif 
    #if (NTDDI_VERSION >= NTDDI_VISTA)
        HICON hBalloonIcon;
    #endif 
}NOTIFYICONDATAW,  *PNOTIFYICONDATAW;
HWND, HICON are pointer, they are 32 bit on 32 bit platform, and 64 bit on 64 bit platform.

In newLISP, we can describe it in this way:

Code: Select all

(define UINT  "unsigned int")
(define DWORD "unsigned int")
(define WORD  "unsigned short int")
(define BYTE  "byte")
(define CHAR  "char")
(define HANDLE "void*")
(define HWND  HANDLE)
(define HICON HANDLE)

(define (struct-array sym-name str-type len)
	(apply struct (cons sym-name (map string (flat (dup (list str-type) len)))))
)

(define (struct* sym-name list-types)
	(local (lst type-name type-value var-name)
		(setq lst '())
		(dolist (type list-types)
			(setq type-name (first type))
			(setq type-value (if (string? (eval type-name)) (eval type-name) (string type-name)))
			(setq var-name (last type))
			(if (regex {(\w+)\[(\d+)\]} (string var-name))
				(extend lst (list (string (struct-array (sym (string sym-name "." $1)) type-value (int $2)))))
				(extend lst (list type-value))
			)
		)
		(apply struct (cons sym-name lst))
	)
)

(struct* 'GUID '(
    (DWORD Data1)
    (WORD  Data2)
    (WORD  Data3)
    (BYTE  Data4[8])
))

(if UNICODE
	(define TCHAR WCHAR)
#else
	(define TCHAR CHAR)
)

(struct* 'NOTIFYICONDATA '(
	(DWORD cbSize)
	(HWND hWnd)
	(UINT uID)
	(UINT uFlags)
	(UINT uCallbackMessage)
	(HICON hIcon)
	(TCHAR szTip[128])
	(DWORD dwState)
	(DWORD dwStateMask)
	(TCHAR szInfo[256])
	(UINT uTimeoutORhVersion)
	(TCHAR szInfoTitle[64])
	(DWORD dwInfoFlags)
	(GUID guidItem)
	(HICON hBalloonIcon)
))
this code can run on both newLISP 32-bit and newLISP 64-bit. (Assume we can have more than 32 members in a structure)

Code: Select all

newLISP v.10.7.3 32-bit on Windows IPv4/6 UTF-8 libffi, options: newlisp -h
> (length (pack NOTIFYICONDATA))
508

Code: Select all

newLISP v.10.7.3 64-bit on Windows IPv4/6 UTF-8 libffi, options: newlisp -h
> (length (pack NOTIFYICONDATA))
528
without the 'struct', processing it will be more complex. may be

Code: Select all

(if UNICODE)
	(if _WIN64
		(setq NOTIFYICONDATAW "lu Lu lu lu lu Lu s256 lu lu s512 lu s128 lu s16 Lu")
	#else
		(setq NOTIFYICONDATAW "lu lu lu lu lu lu s256 lu lu s512 lu s128 lu s16 lu")
	)
#else
	(if _WIN64
		(setq NOTIFYICONDATAA "lu Lu lu lu lu Lu s128 lu lu s256 lu s64 lu s16 Lu")
	#else
		(setq NOTIFYICONDATAA "lu lu lu lu lu lu s128 lu lu s256 lu s64 lu s16 lu")
	)
)
But when I found that the members of struct restrictions can not exceed 32, the solution has been setbacks.

The IMAGE_VXD_HEADER structure in WinNT.h has 51 members.
The CONTEXT structure in WinNT.h has 208 members. Although this is very rare.

Re: Does struct support only up to 32 parameters?

Posted: Wed Aug 23, 2017 2:54 pm
by Lutz
As you have seen, one can use ‘pack’ and ‘unpack’ alone to access data in a structure. You just need to have good C-language knowledge and know where filler/alignment bytes have to be inserted and be familiar with address arithmetic and usage in C. E.g. arrays in C are simply pointers to memory where the members are packed one after each other.

With ‘struct’ most of this work is done by libffi. Perhaps it is this library which introduces the 32-limit.