[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