It seems so simple...FFI help please?
Joshua 'Schwa' Gargus
schwa at cc.gatech.edu
Sat Sep 27 21:58:05 UTC 2003
>From the "FFI questions" thread of Sept. 4, Andreas helped me
out with:
> > <cdecl: ulong 'cgCreateProgram' (ulong ulong byte*
> > ulong byte* byte** ) module: 'Cg'>
> ^^
> And this is the problem. The FFI can't coerce arrays of pointers
> automatically, so using "foo **" in any way is simply invalid.
See the thread for more details.
I've attached the NullTerminatedStringArray class that I wrote
as a result of that discussion. I hope that you find it useful.
Joshua
On Sat, Sep 27, 2003 at 11:39:19AM -0700, Andrew Berg wrote:
>
> I'm trying to call a library which returns all of its data through char
> **'s. I'm pretty sure I could write a plugin to do this, but I would
> prefer to use FFI. Here's an example of two basic functions with
> signatures similar to this library:
>
> --------------------------------------------------------------------------
> int s_dup(char * in, char ** out);
> int s_free(char * in);
> --------------------------------------------------------------------------
>
> These particular ones are just test functions I wrote in a test library:
>
> --------------------------------------------------------------------------
> int s_dup(char * in, char ** out)
> {
> printf("s_dup(%08x, %08x)", in, out);
> if (out) printf(" ((%08x))", * out);
> printf("\n");
> * out = strdup(in);
> return 0;
> }
>
> int s_free(char * in)
> {
> printf("s_free(%08x)", in);
> if (in)
> free(in);
> return 0;
> }
> --------------------------------------------------------------------------
>
> In English, the returned string is passed as an out parameter, which must
> be freed by the client using a supplied function. Well, that was almost
> English. So, to call these functions I made a FFIStringTest class:
>
> --------------------------------------------------------------------------
> 'From Squeak3.5 of ''11 April 2003'' [latest update: #5180] on 27 September
> 2003 at 11:00:13 am'!
> ExternalLibrary subclass: #FFIStringTest
> instanceVariableNames: ''
> classVariableNames: ''
> poolDictionaries: ''
> category: 'FFI-StringTest'!
>
> !FFIStringTest methodsFor: 'as yet unclassified' stamp: 'acb 9/27/2003
> 10:47'!
> dup: str to: aStrPtr
> "Copy the string passed in to us."
> <cdecl: long 's_dup' (char * FFIStringPtr *) module: 'strt'>
> ^ self externalCallFailed! !
>
> !FFIStringTest methodsFor: 'as yet unclassified' stamp: 'acb 9/26/2003
> 21:40'!
> free: aStrPtr
> "Free the string passed in to us."
> <cdecl: long 's_free' (FFIStringPtr) module: 'strt'>
> ^ self externalCallFailed! !
> --------------------------------------------------------------------------
>
> And, a FFIStringPtr class, since 'char **' is not a happy parameter type:
>
> --------------------------------------------------------------------------
> 'From Squeak3.5 of ''11 April 2003'' [latest update: #5180] on 27 September
> 2003 at 11:03:53 am'!
> ExternalStructure subclass: #FFIStringPtr
> instanceVariableNames: ''
> classVariableNames: ''
> poolDictionaries: ''
> category: 'FFI-StringTest'!
>
> !FFIStringPtr methodsFor: 'as yet unclassified' stamp: 'acb 9/27/2003
> 08:01'!
> str
> "Dereference the pointer and return the string."
> "^ (self handle pointerAt: 1) "
> ^ ( ExternalData fromHandle: self handle type: ExternalType char )
> fromCString! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> FFIStringPtr class
> instanceVariableNames: ''!
>
> !FFIStringPtr class methodsFor: 'as yet unclassified' stamp: 'acb 9/27/2003
> 08:00'!
> fields
> "FFIStringPtr defineFields"
> "Define a structure which is just a string pointer."
>
> ^#(
> (ptr 'char *')
> )! !
>
>
> FFIStringPtr compileFields!
> --------------------------------------------------------------------------
>
> So, it seems to me that I'm pretty close, yet not so close at all. When I
> run this:
>
> --------------------------------------------------------------------------
> | f p |
> f _ FFIStringTest new.
> p _ FFIStringPtr new.
> f dup: 'hi there' to: p.
> f free: p.
> --------------------------------------------------------------------------
>
> I get some of my printf output to the console, followed by an enormous
> stack dump:
>
> --------------------------------------------------------------------------
> s_dup(08155e58, 40cdc904) ((40b283d1))
>
> Segmentation fault
>
> 1087228576 ProtoObject>become:
> 1087228392 MethodContext>doesNotUnderstand:
> 1087228300 MethodContext>doesNotUnderstand:
> 1087228208 Behavior>removeSelectorSimply:
> 1087197804 Compiler>evaluate:in:to:notifying:ifFail:
> 1087198172 [] in ParagraphEditor>evaluateSelection
> .
> . (and a _bunch_ of lines of stuff that looks like Morphic handling of an
> alt-d)
> .
> --------------------------------------------------------------------------
>
> About as much as I can really say is that it is not getting to the s_free
> call. Can anyone tell me what I'm doing wrong, or perhaps suggest another
> place to look for documentation/comments?
>
> Thanks,
>
> -andrew
>
> --
> andrew_c_berg at yahoo.com
>
-------------- next part --------------
'From Squeak3.6beta of ''4 July 2003'' [latest update: #5395] on 27 September 2003 at 5:57:35 pm'!
Object subclass: #NullTerminatedStringArray
instanceVariableNames: 'pointers externalStrings '
classVariableNames: ''
poolDictionaries: ''
category: 'KSimpleGL-Cg'!
!NullTerminatedStringArray commentStamp: 'jcg 9/8/2003 11:08' prior: 0!
NullTerminatedStringArray is used for passing data to FFI functions that require a null-terminated array of null-terminated strings. For example, this class is suitable for the 'char* argv[]' argument of the 'main()' function in C programs. Another example is the arguments passed to the Cg compiler in the context of an OpenGL/Cg application. The first example does not require a null-terminated array, because the number of strings is stored in the 'argc' argument. However, the second example requires both the strings and the array to be null-terminated.
hArray stores a null-terminated list of pointers to the null-terminated strings stored in hData.!
!NullTerminatedStringArray methodsFor: 'initialization' stamp: 'jcg 9/5/2003 02:45'!
free
externalStrings do: [:string | string free].
pointers _ nil.
externalStrings _ nil.! !
!NullTerminatedStringArray methodsFor: 'initialization' stamp: 'jcg 9/5/2003 02:58'!
strings: stringCollection
| extern pointerBytes |
"Allocate space for each string individually, and copy the bytes over."
externalStrings _ stringCollection collect: [:string |
extern _ ExternalAddress allocate: string size + 1.
1 to: string size do: [:ind | extern byteAt: ind put: (string byteAt: ind)].
extern byteAt: string size + 1 put: 0.
extern].
"Create null-terminated array of pointers to strings."
pointerBytes _ ByteArray new: 4*(stringCollection size + 1).
pointers _ ExternalData fromHandle: pointerBytes type: ExternalType void asPointerType.
externalStrings withIndexDo: [:string :ind |
pointerBytes pointerAt: (ind-1)*4 + 1 put: string].
! !
!NullTerminatedStringArray methodsFor: 'testing' stamp: 'jcg 9/5/2003 02:57'!
strings
"Get Smalltalk Strings from byte data."
^ externalStrings collect: [:string | (ExternalData
fromHandle: string
type: ExternalType char asPointerType) fromCString]! !
!NullTerminatedStringArray methodsFor: 'testing' stamp: 'jcg 9/5/2003 02:57'!
strings2
"Get Smalltalk Strings from byte data. This way is longer, but makes sure that 'pointers' is also correct."
^ (1 to: externalStrings size) collect: [:ind |
(ExternalData
fromHandle: (pointers getHandle pointerAt: (ind-1)*4+1)
type: ExternalType char asPointerType) fromCString]! !
!NullTerminatedStringArray methodsFor: 'accessing' stamp: 'jcg 9/5/2003 03:00'!
pointers
"Answer a null-terminated array of pointers (an ExternalData) to null-terminated strings."
^ pointers! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
NullTerminatedStringArray class
instanceVariableNames: ''!
!NullTerminatedStringArray class methodsFor: 'instance creation' stamp: 'jcg 9/5/2003 02:06'!
withStrings: stringCollection
^ self new strings: stringCollection! !
More information about the Squeak-dev
mailing list
|