Page 1 of 1

address of list elements

Posted: Thu Jun 21, 2007 2:06 am
by m35
I'm working with a DLL that takes a char** as an argument (an array of char*). It would be nice it I could keep the strings being passed in a list, and just pack the addresses of those list elements.

e.g.

Code: Select all

(setq x '("a" "b" "c"))
(setq arg (eval (cons pack (cons (dup "ld" (length x)) (map address x)))))
(My_Dll_Func arg)
However, my tests don't suggest I'm getting the addresses of the list elements:

Code: Select all

> (setq x '("a" "b" "c"))
("a" "b" "c")
> (address x)
4019592
> (address (x 0))
5341424
> (address (x 1))
5341424
> (address (x 2))
5341424
> (address (nth 0 x))
5341424
> (address (nth 1 x))
5341424
> (address (nth 2 x))
5341424
> (map address x)
(4044024 4044136 4044152)
> (map address x)
(4044152 4044136 4044024)
> (map address x)
(4044024 4044136 4044152)
>
Work-arounds I've considered:
* Put every string into a separate symbol so I can get the addresses.
* Pack every string into one big string, then get the address of this big string, and add the offsets of each substring.

Neither of these work-arounds make me very happy. Is there any better solution?

Posted: Thu Jun 21, 2007 2:32 am
by rickyboy
Have you tried using newLISP arrays (instead of lists)?

Don't know if you'll fair better -- just guessing. Good luck. --Rick

Posted: Thu Jun 21, 2007 2:57 am
by Lutz
* Put every string into a separate symbol so I can get the addresses.
* Pack every string into one big string, then get the address of this big string, and add the offsets of each substring.
Either one would work. Only the symbol (variable) guarantees you a fixed addresss for the string contained. All other methods just get addresses of volatile memory objects. The symbol acts like a pointer to the string contained.

Code: Select all

> (set 'V '(v1 v2 v3))
(v1 v2 v3)
> (map set V '("A" "B" "C"))
("A" "B" "C")

> (set 'char** (eval (append (cons pack (dup "lu" (length V))) V)))
"\0000[\176\0000[\128\0000[\144" ; 3 32-bit addresses = 12 bytes

> (length char**) ; the length function works on binary contents
12

> (get-string (get-int (+ (address char**) 0)))
"A"
> (get-string (get-int (+ (address char**) 4)))
"B"
> (get-string (get-int (+ (address char**) 8)))
"C"
> 
similar to your first approach, but the difference is that the strings are referenced via the symbols v1,v2,v3. Symbols holding strings work like C pointers.

Lutz

ps:

Code: Select all

>  (append (cons 'pack (dup "lu" (length V))) V)
(pack "lululu" v1 v2 v3)
> 
The quote before the pack isn't really required, but makes for better looking output ;)

Posted: Thu Jun 21, 2007 4:57 pm
by m35
Thanks Lutz, I guess I'll have to use a work-around.

But since I'm here, may I also propose an improvement to the (address) function? :)

Since the behavior of (address <list>) is currently undefined, could we add one (or both) of the following?

Code: Select all

(address <list>) => [list of addresses of the list elements]

e.g.
> (setq x '("a" "b" "c"))
("a" "b" "c")
>(address x)
(4044152 4044136 4044024)
or

Code: Select all

(address <list> <index>) => [address of element at <index>]

e.g.
> (setq x '("a" "b" "c"))
("a" "b" "c")
>(address x 1)
4044136
Not all that important, but I thought I'd throw it out there.