On 25 November 2012 21:59, Stéphane Ducasse <[email protected]> wrote: > Igor your proposal looks nice. > it's not a proposal anymore.. i already made the changes :)
> Stef > > On Nov 22, 2012, at 2:47 PM, Igor Stasenko wrote: > >> Hi, >> >> i am thinking about redesign of FFI top-level api, because it not very good. >> >> First thing is that NBFFICallout referenced in too many places (187 in >> my image). >> So, from one side, we should encourage using a convenience syntax, using >> >> self nbCall: .. >> instead of >> NBFFICallout ... >> >> but there's one more thing: >> - a code generator has many different ways of use, and i actually >> going to add another one, >> but i see that it will explode the protocol(s) without make it >> convenient to use. >> >> Imagine that you want to generate a callout to function, which address >> cannot be obtained directly from external library. >> This is actually a real issue, which Esteban met when implementing >> bindings to Chipmunk library: >> - some functions are not exposed directly by dynamic library, but indirectly: >> the symbol(s) which library exports is not direct pointer(s) to >> functions but instead >> a pointers to variable, where the function pointer is stored. >> So, in order to get function pointer , one needs first to get a >> pointer to symbol, and then read the pointer value at that address. >> >> So, i thought that i would simply add new protocol to NBFFICallout: >> >> call: anonFunctionSignature convention: callConvention >> functionAddress: aFunctionAddressBlock >> >> Then, one could use it like: >> >> myMethod >> <primitive: #primitiveNativeCall module: #NativeBoostPlugin >> error: errorCode > >> >> ^ NBFFICallout >> call: #( int () ) >> convention: #cdecl functionAddress: [ self >> getPointerTo: 'functionname' ] >> >> but it is too elaborate.. >> you can imagine that if one would like to implement a more convenient >> form, he could just do: >> >> myMethod >> <primitive: #primitiveNativeCall module: #NativeBoostPlugin >> error: errorCode > >> >> ^ self indirectCall: #( int () ) name: 'functionname' >> >> or even: >> >> myMethod >> <primitive: #primitiveNativeCall module: #NativeBoostPlugin >> error: errorCode > >> >> ^ self indirectCall: #( int functionname () ) >> >> now the problem is that you cannot wrap the NBFFICallout method in >> order to implement convenience method, because of use of thisContext. >> >> I also concerned about exploding NBFFICallout public API.. which imo >> doesn't makes much sense, because we should encourage the use of >> convenience methods. >> >> So, i looking for improving public API: >> >> - First thing is getting rid of direct references to NBFFICallout.. >> - second thing is avoid tying public API with thisContext (so you can >> create own convenience methods without much hassle). >> >> So, lets imagine what and how we can do it: >> >> >> foo >> <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> >> >> ^ NBFFICallout cdecl: #( int foo() ) module: 'bar' >> >> 1. getting rid of global: >> >> foo >> <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> >> >> ^ self nbCallout cdecl: #( int foo() ) module: 'bar' >> >> 2. separating the call convention from signature: >> >> foo >> <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> >> >> ^ self nbCallout cdecl call: #( int foo() ) module: 'bar' >> >> 3. as a bonus, since cdecl is most used, we can assume it is default: >> >> foo >> <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> >> >> ^ self nbCallout call: #( int foo() ) module: 'bar' >> >> 4. avoid hardcoding module: >> >> foo >> <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> >> >> ^ self nbCallout call: #( int foo() ) module: self module >> >> 5. pass code generation options as a separate message, instead of >> additional keyword: >> >> foo >> <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> >> >> ^ self nbCallout >> options: #( +optFoo - optBar); >> call: #( int foo() ) module: self module >> >> 6. finally users would want to do it in most convenient way: >> >> foo >> <primitive: 'primitiveNativeCall' module: 'NativeBoostPlugin'> >> >> ^ self call: #( int foo() ) >> >> Which means that #call: is convenience method which they implement by >> themselves. >> Here how i think it should look like: >> >> call: fnSpec >> >> ^ (self nbCalloutIn: thisContext sender) >> cdecl; >> options: #(+optFoo - optBar); >> call: fnSpec module: self module. >> >> If you look at current implementation of Object>>nbCall: >> >> nbCall: fnSpec >> " you can override this method if you need to" >> >> | sender | >> sender := thisContext sender. >> >> ^ NBFFICallout >> handleFailureIn: sender >> nativeCode: [ :gen | >> gen >> sender: sender; >> callType: self nbCallingConvention; >> generateCall: fnSpec module: self >> nbLibraryNameOrHandle] >> >> it is almost the same, except from cryptic use of blocks, and private >> internal NBFFICallout methods which doing the job. >> >> So, basicly, i want to get rid of NBFFICallout class >> >> [cdecl/stdcall]: module: [options:] >> [cdecl/stdcall]: emitCall: [options:] >> >> and replace them with #call:module: and #call: emit: >> and also add new one: >> #call:address: >> >> which can be used for passing a function pointer directly. >> >> And any future extensions to public API will be a single method. Not >> 2,3 or more, like today.. >> >> >> -- >> Best regards, >> Igor Stasenko. >> > > -- Best regards, Igor Stasenko.
