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@mail.kde.org > http://mail.kde.org/mailman/listinfo/kde-bindings _______________________________________________ Kde-bindings mailing list Kde-bindings@mail.kde.org http://mail.kde.org/mailman/listinfo/kde-bindings