[Kde-bindings] Some Smoke "documentation" (was: ruby bindings)
Germain Garand
germain at ebooksfrance.org
Wed Apr 30 02:48:20 UTC 2003
Le Mardi 29 Avril 2003 22:51, Richard Dale a écrit :
> On Tuesday 29 April 2003 7:16 pm, Alexander Kellett wrote:
> It shouldn't be too difficult to write a Qt smoke adaptor for ruby - that
> is still the best way forward. Should we try and have a look at seeing
> what's involved - I think so yes, so lets discuss it on this list..
>
Alexander and Richard,
I don't know much about Ruby so I hope I'm not wrong, but it looks like the
"method_missing" functionnality of Ruby would be perfect for redirecting
method calls to the generic marshalling function that issue the Smoke calls.
Alexander : as it's easy to get lost in Smoke, and there doesn't exist, sadly
enough, any documentation at all whatsoever, I'll try to sum up the whole
mechanism so you can figure how it can be implemented in Ruby.
Note that a great part of the necessary guts can be adapted fairly easily from
PerlQt (namely, the Marshall classes, and the associated marshallers, as well
as helper functions simplifying access to Smoke's structs... more on that
later)
------------------
The first thing a binding using Smoke does is initializing the global Smoke
instance encapsulating the target library (here, Qt ) by calling the
appropriate function.
For libsmokeqt :
extern Smoke *qt_Smoke;
extern void init_qt_Smoke();
...
init_qt_Smoke();
then, in order to tie the target language to the target library, it must
instantiate a class derived from SmokeBinding.
qt_Smoke->binding = new RubyQtSmokeBinding(qt_Smoke);
This class will mainly implement 2 methods:
- callMethod(Smoke::Index method, void *obj, Smoke::Stack args, bool
isAbstract = false)
- deleted(Smoke::Index classId, void *obj)
The former is called whenever a virtual method is called by Qt
The later is called whenever the virtual destructor (if any) of a Qt object is
called
So the implementation of callMethod will need to:
1) find the Ruby/Qt object corresponding to "obj" (e.g by looking into a hash
map keeping a reference of all the instanciated objects pointers)
2) see if the corresponding Ruby class has reimplemented "method"
3) if yes, call this method after proper marshalling of "args"
4) marshall back the return value to Qt (more on Marshalling below)
---
Now, next step : how can one actually call Qt
methods/constructors/destructors/etc. ?
Smoke works with a late bindings strategy : you ask it what method matches a
given very simple munged prototype (optimized for scripting languages), and
it answers you with one or several (in case of ambiguity) method Ids ....
e.g: someone try to call myRubyObject.foo( "string", myRubyQWidget )
Typically, your RubyQt classes won't have any methods...
They will delegate everything to a handler ( e.g with the missing_method
mechanism).
The handler will first determine the Qt class hierarchy of the object (using
Smoke's idClass() and looking in the class hierarchy array) then build the
munged prototype of the requested method, following those rules:
- take the requested method name
- append $ for each simple native type argument (string, numeral, etc...)
- append # for each Qt object passed as argument
- append ? for things like an array, or a hash, or an undefined value
with the example above, we would end up asking to Smoke if "foo$#" exists
with findMethod(Index class, Index munged_prototype), on every relevant
"class"
Sample skeleton code for a query:
Smoke::Index meth = qt_Smoke->findMethod(c, name);
if(!meth) {
// empty list
} else if(meth > 0) {
Smoke::Index i = qt_Smoke->methodMaps[meth].method;
if(i > 0) { // single match
// return qt_Smoke->methodMaps[meth].method
} else { // multiple match
i = -i; // turn into ambiguousMethodList index
while(qt_Smoke->ambiguousMethodList[i]) {
// add qt_Smoke->ambiguousMethodList[i] to a list
i++
}
}
}
Once a method Id has been elected for calling, you need to build the stack of
marshalled arguments and call the method...
This step can be greatly simplified by reusing PerlQt's Marshall classes:
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/perlqt/PerlQt-3/PerlQt/marshall.h
Sample implementation in :
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/perlqt/PerlQt-3/PerlQt/Qt.xs?only_with_tag=MAIN
=>look from line 203 to 396 for all classes that are needed to marshall
types back and forth, either for normal method calls, or for virtual method
callbacks, and their return value.
Those classes work by iterating over the arguments, locating for each one the
appropriate marshaller function.
Marshallers are in :
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/perlqt/PerlQt-3/PerlQt/handlers.cpp?only_with_tag=MAIN
(look for marshall_* functions)
They are all referenced in a hash, and selected by:
Marshall::HandlerFn getMarshallFn(const SmokeType &type) (same file)
which is called by the Marshall classes
The function is then called, and it's return value is Marshalled back ; so it
could all boils down in Ruby to something like:
MethodCall c(qt_Smoke, method_id, ruby_arg_stack, ruby_argc);
c.next();
VALUE *ret = c.var();
-----
Something else: you'll need, from your Ruby Qt Classes constructors, to call
the corresponding Qt constructor (e.g, via the above general calling
function), and tie the returned Qt Object to the Ruby Object.
In fact, this tying can be done during the marshalling... you can see that at
line 309 to 344 in handlers.cpp.
(Marshall::ToSV and Marshall::FromSV would mean ToVALUE and FromVALUE in Ruby)
Then, you'd register the new pointer in the object map.
---
That's all I can tell for now, I hope it was not too confusing.
Best luck for this great project! :-)
Germain
> -- Richard
>
> _______________________________________________
> Kde-bindings mailing list
> Kde-bindings at mail.kde.org
> http://mail.kde.org/mailman/listinfo/kde-bindings
More information about the Kde-bindings
mailing list