[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Help-smalltalk] Re: Calling LDAP C API from Smalltalk
From: |
Paolo Bonzini |
Subject: |
[Help-smalltalk] Re: Calling LDAP C API from Smalltalk |
Date: |
Fri, 19 Sep 2008 09:21:53 +0200 |
User-agent: |
Thunderbird 2.0.0.16 (Macintosh/20080707) |
> So in order to call the LDAP C API from Smalltalk I have some questions:-
> 1. How does one action "DLD class>>#addLibrary:" so as to link GNU
> Smalltalk with the LDAP library? I don't know what it means!
You just have to add something like
DLD addLibrary: 'libldap'
before the method definitions.
> 2. Where is the mapping specified between the C function, and the
> Smalltalk name that I am to provide for the function? I wasn't able to
> find this in the documentation. For example, the first function is an
> ldap_init and the C definition looks like this:-
There are two main possibilities: everything goes on the class side, or
constructor functions go on the class side and everything else on the
instance side. In this case, you'd probably opt for the latter.
Also, do you want your methods named #open:/#init: or
#ldapOpen:/#ldapInit:? Or maybe more Smalltalk-ish #openOn:/#on:?
These are the "design" choices to be made.
> 3. In the definition above, I'm expecting to get back a pointer to an
> LDAP struct. Would the return type in the Smalltalk function be a
> "cObject"?
No, it would be #{LDAP} so that you can use APIs like ldap_simple_bind_s
as instance methods of LDAP. For arguments, instead, just use #cObject.
> If it helps, the definition in ldap.h for the LDAP struct is just...
> typedef struct ldap LDAP;
If it is opaque, just define
CObject subclass: LDAP []
otherwise you would have to use CStruct (there are examples in the Cairo
bindings.
Here are the definitions for a couple of methods (untested)
DLD addLibrary: 'libldap'.
CObject subclass: LDAP [
LDAP class >> openOn: host port: port [
"Maybe you prefer #open:port: as the selector, of course."
<cCall: 'ldap_open' returning: #{LDAP} args: #(#string #int)>
]
LDAP class >> on: host port: port [
"Maybe you prefer #init:port: as the selector, of course."
<cCall: 'ldap_init' returning: #{LDAP} args: #(#string #int)>
]
LDAP class >> errorString [
"Utility methods might also go on the class side."
<cCall: 'ldap_err2string' returning: #string args: #(#int)>
]
simpleBindS: foo bar: bar [
"Didn't bother looking at the documentation, so I don't know
what foo and bar actually are... I declared them as #cObject
below, but maybe #string is better... In any case, passing
`nil' gives a NULL to the C side."
<cCall: 'ldap_simple_bind_s' returning: #int
args: #(#self #cObject #cObject)>
]
]
Other random things to know:
- to pass a pointer by reference, use #cObjectPtr and pass a variable
that was set to "CObject new".
- to return an array of strings use #{CString} as the return type (for
ldap_get_values). Then, you can do "(array at: 0)", "(array at: 1)",
etc. to get the strings, or use the other idiom in the translated
program below. You can also make a subclass of CString and return that
class, so that you can override #free to call ldap_value_free. I assume
this below.
- Likewise, you can make a subclass of CObject for the value returned by
ldap_first_attribute and ldap_next_attribute, so that you can override
#free to call ldap_memfree. Similarly, you can make a subclass of
CObject for BER (ber_free) and LDAPMessage (ldap_msgfree). Do that just
to override #free, or as the beginning of a `more Smalltalk-looking'
implementation (more hints below).
- You also need to define constant methods, probably on the class side.
That's not hard to do with sed starting from #defines. Enums are a
little harder.
Here is the translation of your program to Smalltalk, with more commentary:
CObject extend [
"Will be in 3.1"
isNull [ ^address = 0 ]
]
hostName := 'localhost'.
portNumber := LDAP defaultPort. "LDAP_PORT"
findDN := 'uid=bjensen, ou=People, o=airius.com'.
ldap := LDAP on: hostName port: portNumber.
ldap isNil ifTrue: [ File checkError. ObjectMemory quit: 1 ].
"Omitting more error checking. You can also wrap the functions
in `two levels': define the C calls with methods like
#primSimpleBindS:bar:, and wrappers that check the return
code and raise an exception if the return code is bad.
As an advantage, you can also create the CObjects for
#cObjectPtr parameters in the wrapper and return them.
On an even higher level, you could add a field in LDAPMessage
to point back to the LDAP object, so as to have methods like
LDAPMessage>>first
^self ldap firstEntry: self
or even LDAPMessage>>keysAndValuesDo: implementing the
loop below, to be used like
e keysAndValuesDo: [ :a :vals |
vals do: [ :each | (a->each) printNl ] ]
As you can see, designing basic bindings is one thing and can
be automated (e.g. if SWIG had GNU Smalltalk support); designing
bindings that look native is another thing and very hard to
automate. For an example, check out packages/gdbm which has
the same example using the low-level API and a high-level
Dictionary-like binding."
ldap simpleBindS: nil bar: nil.
ldap searchExtS: findDN scope: LDAP scopeBase
???: 'objectclass=*' ???: nil ???: 0 ???: nil ???: nil
???: LDAP noLimit ???: LDAP noLimit
result: (result := LDAPMessage new).
e := ldap firstEntry: result.
e isNil ifFalse: [
('Found ', findDN) displayNl.
"Iterate through each attribute in the entry. Should return a
#string"
aPtr := ldap firstAttribute: e ber: (ber := BER new).
[ aPtr isNil ] whileFalse: [
"Convert as soon as possible to a String, and use that in
the remainder. In more complicated examples it would cause
less memory management headaches."
a := String fromCData: aPtr.
aPtr free.
vals := ldap getValues: e attribute: a.
vals isNull ifTrue: [
val := vals.
[ val value isNil ] whileFalse: [
(a->val value) printNl.
val incr ].
vals free
]
a := ldap nextAttribute: e ber: ber.
].
].
"I don't know if it makes sense but BER>>#free could call
`self free: 0'."
ber isNil ifFalse: [ ber free ].
result free.
ldap unbind
That's it, HTH!
Paolo
- [Help-smalltalk] Calling LDAP C API from Smalltalk, Stephen, 2008/09/18
- [Help-smalltalk] Re: Calling LDAP C API from Smalltalk,
Paolo Bonzini <=
- [Help-smalltalk] Re: Calling LDAP C API from Smalltalk, Paolo Bonzini, 2008/09/19
- [Help-smalltalk] Re: Calling LDAP C API from Smalltalk, Stephen, 2008/09/26
- Re: [Help-smalltalk] Re: Calling LDAP C API from Smalltalk, Paolo Bonzini, 2008/09/26
- Re: [Help-smalltalk] Re: Calling LDAP C API from Smalltalk, Stephen, 2008/09/27
- Re: [Help-smalltalk] Re: Calling LDAP C API from Smalltalk, Paolo Bonzini, 2008/09/27
- Re: [Help-smalltalk] Re: Calling LDAP C API from Smalltalk, Stephen, 2008/09/27
- Re: [Help-smalltalk] Re: Calling LDAP C API from Smalltalk, Paolo Bonzini, 2008/09/27
- Re: [Help-smalltalk] Re: Calling LDAP C API from Smalltalk, Stephen, 2008/09/27
- Re: [Help-smalltalk] Re: Calling LDAP C API from Smalltalk, Paolo Bonzini, 2008/09/27