tag:blogger.com,1999:blog-11264158.post-1119069179691762172005-06-17T19:37:00.000-07:002005-06-17T21:32:59.936-07:00Bindings: XPath is my friendI've had good success with <a href="http://search.cpan.org/~awin/GCC-TranslationUnit-1.00/TranslationUnit.pm">my <code>gcc -fdump-translation-unit</code> parser</a> generating XML, and then using <a href="http://www.w3.org/TR/xslt">XSLT</a> to convert the XML into C++ source-code. I expect to create an XSL stylesheet for the C++/moc4 Smoke binding, another one for the O'Caml binding, and yet another one for the C# binding. All using the same raw data generated from GCC. <a href="#example">Scroll down</a> to see an example of what that XML contains.<br /><br />This data replicates what was availble through the Smoke bindings. Instead of trying to shoehorn all that data into giant, esoteric data structures in a monolithic library (which works great -- don't get me wrong), we're going to leave it as XML. Each class which has a Smoke binding will live in its own .so file, and should be dynamically loaded upon request. Inside that file will be the full XML data which describes that class. Upon loading the library, the class XML will be inserted to a global Smoke DOM, which will be the basis of the Smoke automation system.<br /><br />Once we have the DOM loaded, we can preform the same searches Smoke uses, but with XPath.<br /><br />To get a list of all the methods in QPushButton:<br /><br /><code>//type[@name='QPushButton']/struct/method</code><br /><br />To get a list of the callable methods:<br /><br /><code>//type[@name='QPushButton']/struct/method[not(@access='private')]</code><br /><br />To get a list of virtual methods defined:<br /><br /><code>//type[@name='QPushButton']/struct/method[virtual]</code><br /><br />The list of static methods we can call:<br /><br /><code>//type[@name='QPushButton']/struct/function[not(@access='private')]</code><br /><br />The list of Whatever::setFoo() methods which accept 2 arguments:<br /><br /><code>//method[@name='Whatever::setFoo' and (param/last() = 2 or param[3]/default)]</code><br /><br />The Smoke method lookup will be XPath-based. You will need to lookup the XML node corresponding to the function if you want to call it via Smoke. Once you have the node, you will know the types of all the arguments and anything else you could possibly want to know about the method, its class, etc. You can check if a method exists in a class with a virtual destructor by looking up <code>../destructor/virtual</code>. It's far more powerful than Smoke.<br /><br />How about speed? Well, I don't expect this will come anywhere close to C++ speed. However, with smart caching of the XPath query results, the runtime penalty during tight loops can be minimized.<br /><br />For static languages which require compiled bindings, the XPath gives way to XSLT, so there's no significant runtime penalty there. The Smoke library will still load the DOM, and the function calls will still be routed through the meta-calling convention, but that's not so bad.<br /><br /><a name="example"></a>As promised, I'll spam you with XML:<br /><pre><br />&lt;lib&gt;<br /> &lt;type name="QWorkspace" id="6" href="/opt/qt4/include/QtGui/qworkspace.h#42"&gt;<br /> &lt;struct&gt;<br /> &lt;base class="QWidget" access="public"/&gt;<br /> &lt;constructor name="QWorkspace::QWorkspace" access="public"&gt;<br /> &lt;return&gt;<br /> &lt;type name="void"/&gt;<br /> &lt;/return&gt;<br /> &lt;this&gt;<br /> &lt;pointer&gt;<br /> &lt;type name="QWorkspace"/&gt;<br /> &lt;/pointer&gt;<br /> &lt;/this&gt;<br /> &lt;param name="parent"&gt;<br /> &lt;default/&gt;<br /> &lt;pointer&gt;<br /> &lt;type name="QWidget"/&gt;<br /> &lt;/pointer&gt;<br /> &lt;/param&gt;<br /> &lt;/constructor&gt;<br /> &lt;method name="QWorkspace::addWindow" access="public"&gt;<br /> &lt;return&gt;<br /> &lt;pointer&gt;<br /> &lt;type name="QWidget"/&gt;<br /> &lt;/pointer&gt;<br /> &lt;/return&gt;<br /> &lt;this&gt;<br /> &lt;pointer&gt;<br /> &lt;type name="QWorkspace"/&gt;<br /> &lt;/pointer&gt;<br /> &lt;/this&gt;<br /> &lt;param name="w"&gt;<br /> &lt;pointer&gt;<br /> &lt;type name="QWidget"/&gt;<br /> &lt;/pointer&gt;<br /> &lt;/param&gt;<br /> &lt;param name="flags"&gt;<br /> &lt;default/&gt;<br /> &lt;type name="QFlags&amp;lt;Qt::WindowType&amp;gt;"/&gt;<br /> &lt;/param&gt;<br /> &lt;/method&gt;<br /> &lt;method name="QWorkspace::sizeHint" access="public"&gt;<br /> &lt;return&gt;<br /> &lt;type name="QSize"/&gt;<br /> &lt;/return&gt;<br /> &lt;this&gt;<br /> &lt;pointer&gt;<br /> &lt;type name="QWorkspace" qualifier="const"/&gt;<br /> &lt;/pointer&gt;<br /> &lt;/this&gt;<br /> &lt;virtual/&gt;<br /> &lt;/method&gt;<br /> &lt;method name="QWorkspace::paintEvent" access="protected"&gt;<br /> &lt;return&gt;<br /> &lt;type name="void"/&gt;<br /> &lt;/return&gt;<br /> &lt;this&gt;<br /> &lt;pointer&gt;<br /> &lt;type name="QWorkspace"/&gt;<br /> &lt;/pointer&gt;<br /> &lt;/this&gt;<br /> &lt;param name="e"&gt;<br /> &lt;pointer&gt;<br /> &lt;type name="QPaintEvent"/&gt;<br /> &lt;/pointer&gt;<br /> &lt;/param&gt;<br /> &lt;virtual/&gt;<br /> &lt;/method&gt;<br /> &lt;/struct&gt;<br /> &lt;/type&gt;<br />&lt;/lib&gt;<br /></pre><br /><br />If you run that through my crufty <a href="http://perlqt.sf.net/xml/deparse.xsl">deparser XSL</a>, you end up with something that looks like a C++ header. If you have an XSLT-happy browser (aka. Mozilla/* or IE6), you can <a href="http://perlqt.sf.net/xml/example.xml">perform the transformation in your browser</a>, or you can view <a href="http://perlqt.sf.net/xml/example.txt">the finished result, care of libxslt</a>.<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/11264158-111906917969176217?l=jahqueel.blogspot.com'/></div>Ashnoreply@blogger.com3