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