FFI and apicall/cdecl type

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

FFI and apicall/cdecl type

Mathieu SUEN
Hi,

I was wondering how the type was handle when apicall or cdecl occur.
I have read how it is parse by the compiler and don't understand the
following:

======================================
Parser>>externalType: descriptorClass
        "Parse an return an external type"
        | xType |
        xType _ descriptorClass atomicTypeNamed: here.
        xType == nil ifTrue:["Look up from class scope"
*here* Symbol hasInterned: here ifTrue:[:sym|
*here* xType _ descriptorClass structTypeNamed: sym]].
        xType == nil ifTrue:[
                "Raise an error if user is there"
*here* self interactive ifTrue:[^nil].
                "otherwise go over it silently"
*here* xType _ descriptorClass forceTypeNamed: here].
        self advance.
        (self matchToken:#*)
                ifTrue:[^xType asPointerType]
                ifFalse:[^xType]
=============================================

So if somebody can explain it to me thanks :)

Math

Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Andreas.Raab
Since this is the second time you're posting a question in this area
(which makes me assume that you're actually interested in getting an
answer) I'll point you gently to:

   http://linuxmafia.com/faq/Essays/smart-questions.html

Please read it and note in particular the parts about "Be precise and
informative about your problem" and "Be explicit about your question".

Cheers,
   - Andreas

mathieu wrote:

> Hi,
>
> I was wondering how the type was handle when apicall or cdecl occur.
> I have read how it is parse by the compiler and don't understand the
> following:
>
> ======================================
> Parser>>externalType: descriptorClass
> "Parse an return an external type"
> | xType |
> xType _ descriptorClass atomicTypeNamed: here.
> xType == nil ifTrue:["Look up from class scope"
> *here* Symbol hasInterned: here ifTrue:[:sym|
> *here* xType _ descriptorClass structTypeNamed: sym]].
> xType == nil ifTrue:[
> "Raise an error if user is there"
> *here* self interactive ifTrue:[^nil].
> "otherwise go over it silently"
> *here* xType _ descriptorClass forceTypeNamed: here].
> self advance.
> (self matchToken:#*)
> ifTrue:[^xType asPointerType]
> ifFalse:[^xType]
> =============================================
>
> So if somebody can explain it to me thanks :)
>
> Math
>
>


Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Mathieu SUEN
Andreas Raab a écrit :
> Since this is the second time you're posting a question in this area
> (which makes me assume that you're actually interested in getting an
> answer) I'll point you gently to:
>
>   http://linuxmafia.com/faq/Essays/smart-questions.html
>
> Please read it and note in particular the parts about "Be precise and
> informative about your problem" and "Be explicit about your question".

ok sorry.

So what the ExternalType served for?

I don't understand why you have to ask in 2 different way the
ExternalType (xType := descriptorClass atomicTypeNamed: here and xType
:= descriptorClass structTypeNamed: sym)?

What the mssage #hasInterned:ifTrue: served for in this case?


> Cheers,
>   - Andreas
>
> mathieu wrote:
>> Hi,
>>
>> I was wondering how the type was handle when apicall or cdecl occur.
>> I have read how it is parse by the compiler and don't understand the
>> following:
>>
>> ======================================
>> Parser>>externalType: descriptorClass
>>     "Parse an return an external type"
>>     | xType |
>>     xType _ descriptorClass atomicTypeNamed: here.
>>     xType == nil ifTrue:["Look up from class scope"
>> *here*        Symbol hasInterned: here ifTrue:[:sym|
>> *here*            xType _ descriptorClass structTypeNamed: sym]].
>>     xType == nil ifTrue:[
>>         "Raise an error if user is there"
>> *here*        self interactive ifTrue:[^nil].
>>         "otherwise go over it silently"
>> *here*        xType _ descriptorClass forceTypeNamed: here].
>>     self advance.
>>     (self matchToken:#*)
>>         ifTrue:[^xType asPointerType]
>>         ifFalse:[^xType]
>> =============================================
>>
>> So if somebody can explain it to me thanks :)
>>
>> Math
>>
>>
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Nicolas Cellier-3
Le Mercredi 19 Juillet 2006 08:24, mathieu a écrit :

> Andreas Raab a écrit :
> > Since this is the second time you're posting a question in this area
> > (which makes me assume that you're actually interested in getting an
> > answer) I'll point you gently to:
> >
> >   http://linuxmafia.com/faq/Essays/smart-questions.html
> >
> > Please read it and note in particular the parts about "Be precise and
> > informative about your problem" and "Be explicit about your question".
>
> ok sorry.
>
> So what the ExternalType served for?
>

ExternalType is Smalltalk class used to handle external function argument
types and function return types. It is necessary to handle automatic
conversions from Smalltalk Objects to C Objects et vice et versa.

> I don't understand why you have to ask in 2 different way the
> ExternalType (xType := descriptorClass atomicTypeNamed: here and xType
>

This is a list of tests:

1) check if there is an atomic type whose name is here (atomic types are non
composed types like float double short long int etc...)
2) if the answer is nil, then here is not an atomic type, try to see if it is
the name of a structure type...

You must understand that returning nil is a simple way to indicate a failure
(a kind of C-style programming).

> := descriptorClass structTypeNamed: sym)?
>
> What the mssage #hasInterned:ifTrue: served for in this case?

here is aString (a token parsed by the parser). It should be the name of a
known type.

If here is a known type, then corresponding symbol (here asSymbol) must
already exist in the image.

That is exactly what #hasInterned:ifTrue: is cheking: does a Symbol having
same characters as here exist in the image, and if true, it will try to set
the type to be a structure...

Once again, structTypeNamed: will do the job of checking if (here asSymbol) is
really the name of an ExternalStructure (most Symbols are not the name of a
structure). If not, it will answer nil.

If there is no symbol like here, then we are sure that there is currently no
such ExternalStructure declared in the Smalltalk image, and it's not even
necessary to check if an ExternalStructure does have that name.

>
> > Cheers,
> >   - Andreas
> >
> > mathieu wrote:
> >> Hi,
> >>
> >> I was wondering how the type was handle when apicall or cdecl occur.
> >> I have read how it is parse by the compiler and don't understand the
> >> following:
> >>
> >> ======================================
> >> Parser>>externalType: descriptorClass
> >>     "Parse an return an external type"
> >>
> >>     | xType |
> >>
> >>     xType _ descriptorClass atomicTypeNamed: here.
> >>     xType == nil ifTrue:["Look up from class scope"
> >> *here*        Symbol hasInterned: here ifTrue:[:sym|
> >> *here*            xType _ descriptorClass structTypeNamed: sym]].
> >>     xType == nil ifTrue:[
> >>         "Raise an error if user is there"
> >> *here*        self interactive ifTrue:[^nil].

An error is detected; no such named type does exist in the image, neither a
known atomic type, nor a known structure type:
If compilation isInteractive (that is user did select accept menu in the
browser), then it's better to warn the user because he is trying to enter
some incorrect code. Answering nil here will be checked at upper level and
raise a SyntaxError notified to the user.

If not interactive (as for example he is loading code from a file, Monticello
or whatever source), it's bad to warn the user for something he is not
responsible for. And maybe, the type is not known yet, but is defined further
in the file...
In this case, force the type to be recognized as a structure (I let you
inquire how).

> >>         "otherwise go over it silently"
> >> *here*        xType _ descriptorClass forceTypeNamed: here].
> >>     self advance.
> >>     (self matchToken:#*)
> >>         ifTrue:[^xType asPointerType]
> >>         ifFalse:[^xType]
> >> =============================================
> >>
> >> So if somebody can explain it to me thanks :)
> >>
> >> Math


Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Andreas.Raab
In reply to this post by Mathieu SUEN
mathieu wrote:
> I don't understand why you have to ask in 2 different way the
> ExternalType (xType := descriptorClass atomicTypeNamed: here and xType
> := descriptorClass structTypeNamed: sym)?

There is no particular reason that I remember why there isn't a unified
interface like #typeNamed:. I think it's probably because this code
changed a number of times and I wrote it under pressure and never looked
at it again (hey, it works just fine even if it ain't the most pretty
code ;-) There are reasons why you'd have to know in other places but
the parser really couldn't care less.

> What the mssage #hasInterned:ifTrue: served for in this case?

Only as an optimization (if there isn't an interned name for the type we
don't even need to check). I *vaguely* seem to remember that the
structure management used to be a lot more complex than it ultimately
ended up being; this might also explain why I'd split the two into the
(cheap) atomic test and an (expensive) struct variant.

Note that the line saying "self interactive ifTrue:[^nil]" *is* relevant
however. What it does is to disable the "forward declaration" of
external types that are used in FFI calls. It is required since two
ExternalTypes can refer to each other in their methods (e.g.,
TypeA>>methodA using TypeB and TypeB>>methodB using TypeA) and without
this forward declaration feature you'd never be able to load any such
classes. The particular line simply disables that when you use an
interactive input (browser) to ensure that you do indeed declare the
types when you use the parser interactively.

Cheers,
   - Andreas

Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Mathieu SUEN
In reply to this post by Nicolas Cellier-3
Ok thank a lot nicolas (in fact those ifNil: were boring me)

Now I understand the hasInterned:ifTrue: :).
I ask those question because I am adding this to the new compiler.

So I have to follow the same structur to get the ExternalType. But it seem to be not really pretty with nil value.

Now I am writing the grammar so is the atomicType restrict to

 bool,  void,  byte,  schar,  double,
 float,  sbyte,  char,  ushort,  short,
 longlong,  ulong,  ulonglong,  long

?


2006/7/19, nicolas cellier <[hidden email]>:
Le Mercredi 19 Juillet 2006 08:24, mathieu a écrit :

> Andreas Raab a écrit :
> > Since this is the second time you're posting a question in this area
> > (which makes me assume that you're actually interested in getting an
> > answer) I'll point you gently to:
> >
> > http://linuxmafia.com/faq/Essays/smart-questions.html
> >
> > Please read it and note in particular the parts about "Be precise and
> > informative about your problem" and "Be explicit about your question".
>
> ok sorry.
>
> So what the ExternalType served for?
>

ExternalType is Smalltalk class used to handle external function argument
types and function return types. It is necessary to handle automatic
conversions from Smalltalk Objects to C Objects et vice et versa.

> I don't understand why you have to ask in 2 different way the
> ExternalType (xType := descriptorClass atomicTypeNamed: here and xType
>

This is a list of tests:

1) check if there is an atomic type whose name is here (atomic types are non
composed types like float double short long int etc...)
2) if the answer is nil, then here is not an atomic type, try to see if it is
the name of a structure type...

You must understand that returning nil is a simple way to indicate a failure
(a kind of C-style programming).

> := descriptorClass structTypeNamed: sym)?
>
> What the mssage #hasInterned:ifTrue: served for in this case?

here is aString (a token parsed by the parser). It should be the name of a
known type.

If here is a known type, then corresponding symbol (here asSymbol) must
already exist in the image.

That is exactly what #hasInterned:ifTrue: is cheking: does a Symbol having
same characters as here exist in the image, and if true, it will try to set
the type to be a structure...

Once again, structTypeNamed: will do the job of checking if (here asSymbol) is
really the name of an ExternalStructure (most Symbols are not the name of a
structure). If not, it will answer nil.

If there is no symbol like here, then we are sure that there is currently no
such ExternalStructure declared in the Smalltalk image, and it's not even
necessary to check if an ExternalStructure does have that name.

>
> > Cheers,
> > - Andreas
> >
> > mathieu wrote:
> >> Hi,
> >>
> >> I was wondering how the type was handle when apicall or cdecl occur.
> >> I have read how it is parse by the compiler and don't understand the
> >> following:
> >>
> >> ======================================
> >> Parser>>externalType: descriptorClass
> >> "Parse an return an external type"
> >>
> >> | xType |
> >>
> >> xType _ descriptorClass atomicTypeNamed: here.
> >> xType == nil ifTrue:["Look up from class scope"
> >> *here* Symbol hasInterned: here ifTrue:[:sym|
> >> *here* xType _ descriptorClass structTypeNamed: sym]].
> >> xType == nil ifTrue:[
> >> "Raise an error if user is there"
> >> *here* self interactive ifTrue:[^nil].

An error is detected; no such named type does exist in the image, neither a
known atomic type, nor a known structure type:
If compilation isInteractive (that is user did select accept menu in the
browser), then it's better to warn the user because he is trying to enter
some incorrect code. Answering nil here will be checked at upper level and
raise a SyntaxError notified to the user.

If not interactive (as for example he is loading code from a file, Monticello
or whatever source), it's bad to warn the user for something he is not
responsible for. And maybe, the type is not known yet, but is defined further
in the file...
In this case, force the type to be recognized as a structure (I let you
inquire how).

> >> "otherwise go over it silently"
> >> *here* xType _ descriptorClass forceTypeNamed: here].
> >> self advance.
> >> (self matchToken:#*)
> >> ifTrue:[^xType asPointerType]
> >> ifFalse:[^xType]
> >> =============================================
> >>
> >> So if somebody can explain it to me thanks :)
> >>
> >> Math





Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Mathieu SUEN
In reply to this post by Andreas.Raab


2006/7/19, Andreas Raab <[hidden email]>:
mathieu wrote:
> I don't understand why you have to ask in 2 different way the
> ExternalType (xType := descriptorClass atomicTypeNamed: here and xType
> := descriptorClass structTypeNamed: sym)?

There is no particular reason that I remember why there isn't a unified
interface like #typeNamed:. I think it's probably because this code
changed a number of times and I wrote it under pressure and never looked
at it again (hey, it works just fine even if it ain't the most pretty
code ;-) There are reasons why you'd have to know in other places but
the parser really couldn't care less.

> What the mssage #hasInterned:ifTrue: served for in this case?

Only as an optimization (if there isn't an interned name for the type we
don't even need to check). I *vaguely* seem to remember that the
structure management used to be a lot more complex than it ultimately
ended up being; this might also explain why I'd split the two into the
(cheap) atomic test and an (expensive) struct variant.


ok I see. :)

Note that the line saying "self interactive ifTrue:[^nil]" *is* relevant
however. What it does is to disable the "forward declaration" of
external types that are used in FFI calls. It is required since two
ExternalTypes can refer to each other in their methods (e.g.,
TypeA>>methodA using TypeB and TypeB>>methodB using TypeA) and without
this forward declaration feature you'd never be able to load any such
classes. The particular line simply disables that when you use an
interactive input (browser) to ensure that you do indeed declare the
types when you use the parser interactively.


ok thanks :)
 

Cheers,
   - Andreas




Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Andreas.Raab
In reply to this post by Mathieu SUEN
Mathieu SUEN wrote:
> I ask those question because I am adding this to the new compiler.

And why didn't you say so before? See ESR's essay where he talks about
"describe the goal not the steps". Your questions make a lot more sense
when you add just that single sentence for explaining why you're looking
at this code at all.

> So I have to follow the same structur to get the ExternalType. But it
> seem to be not really pretty with nil value.

It is a perfectly reasonable thing to do. The compiler is querying for
the existence of a type by that name. If there is none, the answer is
undefined, an UndefinedObject, e.g., nil. What is not pretty about it?

> Now I am writing the grammar so is the atomicType restrict to
>
>  bool,  void,  byte,  schar,  double,
>  float,  sbyte,  char,  ushort,  short,
>  longlong,  ulong,  ulonglong,  long
>
> ?

Bad idea. Don't assume that this is any fixed set of atomic types. In
fact, the code has been written the way it is (vectoring through
ExternalType instead of hardcoding it in the parser) precisely to allow
later extensions of external types without the compiler needing to
recognize it. All the parser needs to recognize here is the *form* not
the *function*.

Nicolas wrote:
>     You must understand that returning nil is a simple way to indicate a
>     failure (a kind of C-style programming).

Absolutely not. It is a query about "is there a type of that name"?
Returning nil does not indicate failure - it indicates that no type by
that name exist. Whether that denotes "failure" or a perfectly legal
response is in the eye of the beholder (the sender of the message). As
an example for this, look at the senders of #atomicTypeNamed: and notice
how the response is used in a variety of ways none of which denotes
"failure" in any way or shape.

Cheers,
   - Andreas

Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Mathieu SUEN
2006/7/19, Andreas Raab <[hidden email]>:
> Mathieu SUEN wrote:
> > I ask those question because I am adding this to the new compiler.
>
> And why didn't you say so before? See ESR's essay where he talks about
> "describe the goal not the steps". Your questions make a lot more sense
> when you add just that single sentence for explaining why you're looking
> at this code at all.


ok I apologize it is true

> > So I have to follow the same structur to get the ExternalType. But it
> > seem to be not really pretty with nil value.
>
> It is a perfectly reasonable thing to do. The compiler is querying for
> the existence of a type by that name. If there is none, the answer is
> undefined, an UndefinedObject, e.g., nil. What is not pretty about it?


Ok is because it is better to tell not to ask. :)

A think that it is cool to see somthing like this:

aExtType := descriptorClass externalType: aToken value ifAbsent: [
                self interactive ifTrue:[^nil].
                "otherwise go over it silently"
                aExtType := descriptorClass forceTypeNamed: aToken value
]


And #externalType:ifAbsent:keyword do all the stuff of searching throw
all you want (don't want to know).

What do you think?

Math

Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Nicolas Cellier-3
In reply to this post by Mathieu SUEN

I agree, C-Style was not pejorative here. It is just a style consisting in checking returned value of functions (messages). Very simple in this case, and i'am OK with that.

You answer the question which type has this name (nil means none), and testing isNil, you answer the question does such a type exist. This is something equivalent to Dictionary access like:
    ExternalType at: name ifAbsent: [nil].

And yes, nil is much cleaner than C implicit encoding (0, -1, 1 all can mean true, false, nil, failure, depending on the function).

Nicolas

Andreas Raab:

>
> Nicolas wrote:
> >     You must understand that returning nil is a simple way to indicate a
> >     failure (a kind of C-style programming).
>
> Absolutely not. It is a query about "is there a type of that name"?
> Returning nil does not indicate failure - it indicates that no type by
> that name exist. Whether that denotes "failure" or a perfectly legal
> response is in the eye of the beholder (the sender of the message). As
> an example for this, look at the senders of #atomicTypeNamed: and notice
> how the response is used in a variety of ways none of which denotes
> "failure" in any way or shape.
>
> Cheers,
>    - Andreas
>
>

________________________________________________________________________
iFRANCE, exprimez-vous !
http://web.ifrance.com


Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Andreas.Raab
In reply to this post by Mathieu SUEN
Mathieu SUEN wrote:
> A think that it is cool to see somthing like this:
>
> aExtType := descriptorClass externalType: aToken value ifAbsent: [
>         self interactive ifTrue:[^nil].
>         "otherwise go over it silently"
>         aExtType := descriptorClass forceTypeNamed: aToken value
> ]

So what is cool about it? Generally, I find the above pattern *very*
hard to read and understand. There is a non-local return in the block,
xType is assigned to inconsistently (both inside the block and as the
result of evaluating the block), control-flow is mixed with data-flow,
all of which make the code harder to read and interpret. And when you
start extending the #ifAbsent: block and the first line moves outside
your visual field, it is *very* easy to forget that the result of that
block is potentially assigned to the variable.

To illustrate, here is a little quiz for you: What are the possible
results for xType after evaluating the following expression? [*]

   xType := descriptorClass atomicTypeNamed: here ifAbsent:[
        (Symbol hasInterned: here ifTrue:[
                xType := #foo.
        ]) ifFalse:[
                xType := #bar.
        ].
   ].

I think the result is very confusing and non-obvious so I'd rather see:

   xType := descriptorClass atomicTypeNamed: here ifAbsent:[nil].
   xType ifNil:[
        (Symbol hasInterned: here ifTrue:[
                xType := #foo.
        ]) ifFalse:[
                xType := #bar.
        ].
   ].

This may be "uncool" but it sure as hell is also less surprising and
more predictable. Generally, my rule of thumb is: If you have a single
expression, like in

        count := dict at: key ifAbsent:[self defaultCount].

then it's fine to use it in that way. If you have a complex sequence of
actions I prefer a style where I return a token and then take action
depending on that token, say

        count := dict at: key ifAbsent:[token].
        count == token ifTrue:[
                "... go compute count ..."
        ].

This avoids the horrible mix of data and control flow by implicitly and
inconsistently assigning to a variable multiple levels up (which is the
root cause of the problem).

Cheers,
   - Andreas


[*] The answer is nil and #bar. The code never results in #foo because
if that branch is taken the #ifAbsent: block actually evaluates to nil
and *that* value is being assigned to xType.

Reply | Threaded
Open this post in threaded view
|

Re: FFI and apicall/cdecl type

Klaus D. Witzel
On Wed, 19 Jul 2006 21:05:22 +0200, Andreas Raab wrote:
...
> This may be "uncool" but it sure as hell is also less surprising and  
> more predictable.

No not uncool, only "maintainable" :)

/Klaus