Hi

I made another version of the c_wrapper.py program. I have attached the
program, a new smaller diff to doxymlparser.py and output from running
the program. The output can compile now:

> python c_wrapper.py out/xml/classwx_button.xm
> g++ `wx-config --libs` `wx-config --cxxflags` -c wrapped_wxButton.cpp

I have not tried hooking it up to Haskell, as I cannot compile wxHaskell
with the svn-checkout of wxWidgets and as I could not find the
doxygen-directory in my older version of wxWidgets (2.8.7) which do work
with wxHaskell.

Also I think we should consider how to proceed.

Should we proceed with making a python program like c_wrapper.py ?

And how much work is there in this? I guess we need to:

* Finish the c_wrapper.py script. It will properly need a lot of
debugging before working properly.

* Re-hook wxHaskell up to the output of c_wrapper.py script. This may
take some time, as function names and some parameters will change. And a
lot of code will have small changes, so there is a lot of possibility
for error :-(

* Convince the wxWidgets team to maintain the script. I guess this
involves not just talking to the wxWidges team, but also show that other
people will find the c-wrapper useful.

Especially the second task seems big, but the others are not small
either.

If people are interested in working on c_wrapper.py, I would be happy to
put it into our Darcs repo.


Greetings,

Mads Lindstrøm



Mads Lindstrøm wrote:
> Hi
> 
> Kevin Ollivier wrote:
> > Hi Eric,
> > 
> > On Apr 9, 2009, at 11:18 AM, Eric Kow wrote:
> > 
> > > On Thu, Apr 09, 2009 at 18:57:29 +0100, Eric Kow wrote:
> > >> This is where Kevin Ollivier of wxWidgets pointed out something
> > >> interesting: in the current wxWidgets trunk, it should be possible to
> > >> automatically generate the C bindings from the Doxygen XML output.   
> > >> In
> > >> fact, he has already done something similar for Python, metadata to
> > >> Python objects:
> > >>
> > >> http://trac.wxwidgets.org/browser/wxWidgets/trunk/docs/doxygen/doxymlparser.py
> > >
> > > Following up on this, I did
> > >
> > >  svn checkout http://svn.wxwidgets.org/svn/wx/wxWidgets/trunk  
> > > wxWidgets
> > >  cd wxWidgets/doc/doxygen
> > >  ./regen.sh
> > >  python doxymlparser.py --report out/xml/classwx_button.xml
> > >
> > > Attached is the XML file (input) and the output of this script.
> > >
> > > Here is a small extract of the output:
> > >
> > >  Class: wxButton
> > >  Bases: wxControl
> > >  Inlcudes: []
> > >  Brief Description:
> > >
> > > And...
> > >
> > >  Method: SetLabel
> > >  Return Type: void
> > >  Params: [{u'type': u'const&', u'declname': u'label'}]
> > >  Prototype: void wxButton::SetLabel(const wxString &label)
> > >  Brief Description:
> > >
> > > What can we do with this?
> > 
> > As a start, with a few lines of Python code after the  
> > doxyparser.parse(file) calls in the script, you can do something like:
> > 
> > # start code to autogenerate wxc.h
> > wxc_header = ""
> > 
> > for aclass in doxyparse.classes:
> >     for amethod in methods:
> >             self_ref = "TSelf"
> >             if amethod.name == "Create":
> >                     self_ref = "TClass"
> >                     wxc_header += "%s(%s) " % (self_ref, aclass.name)
> >             else:
> >                     wxc_header += "%s " % 
> > (wxc_type_for_type(amethod.return_type)
> > 
> >             wxc_header += "%s_%s( %s _obj" % (aclass.name, amethod.name,  
> > self_ref + (" + aclass.name + ")")
> > 
> >             for param in amethod.params:
> >                     wxc_header += ", %s %s" % 
> > (wxc_type_for_type(param["type"]), "_" +  
> > param["declname"])
> >                     # Note: uncomment this if you support adding default 
> > values
> >                     # if "defval" in param:
> >                     #       wxc_header += "=" + param["defval"]
> >             wxc_header += " );\n"
> > 
> > wxc_file = open("wxc.h", "wb")
> > wxc_file.write(wxc_header)
> > wxc_file.close()
> > 
> > # end code
> > 
> > This is all just pseudo-code, and methods like wxc_type_for_type would  
> > need implemented, and you'd also need to probably work out some  
> > special logic for constructors, a list of classes to exclude, etc. but  
> > this should give you an idea of how to start things off. Ideally you'd  
> > actually have your own generate_c_bindings.py file that does an  
> > "import doxyparser", then runs "doxyparse.parse()" itself rather than  
> > extending the doxymlparser.py script, but anyway, just some ideas.
> 
> I have made an initial attempt at a c_wrapper.py program. It is far from
> finished, but like the XSLT program, I did it to see if it was feasible.
> I have attached the program.
> 
> My initial imprecision is that this could work out. Python is a very
> nice language, and it was a joy to make my little program. I am already
> further along than with the XSLT program, and I have spend a third of
> the time. And more importantly, it did not feel painful at all.
> 
> I had to change doxymlparser.py a bit though, otherwise we get
> type-names like constwxString:
> 
> > svn diff:
> 
> Index: doxymlparser.py
> ===================================================================
> --- doxymlparser.py   (revision 60099)
> +++ doxymlparser.py   (working copy)
> @@ -98,7 +98,11 @@
>          if child.nodeType == child.ELEMENT_NODE and child.nodeName == "ref":
>              text += getTextValue(child)
>          if child.nodeType == child.TEXT_NODE:
> -            text += child.nodeValue.strip()
> +            #text += child.nodeValue.strip()
> +            # We changed line above to remove qualifiers. Maybe we need 
> another field on parameters?
> +            for token in string.split(child.nodeValue.strip()):
> +                if (token != "virtual" and token != "static" and token != 
> "const"):
> +                    text += token
>              
>      return text
> 
> Kevin, what do you think about this change?
> 
> 
> The output looks like:
> 
> > python c_wrapper.py out/xml/classwx_button.xml | indent
> 
> /* Constructor */
>        wxButton wxButton_wxButton ();
> /* Constructor */
>      wxButton wxButton_wxButton (wxWindow * _parent, wxWindowID _id,
>                                wxString & _label, wxPoint & _pos,
>                                wxSize & _size, long _style,
>                                wxValidator & _validator, wxString & _name);
>      void wxButton_Destruct (wxButton * _obj);
>      bool wxButton_Create (wxButton _obj, wxWindow * _parent, wxWindowID _id,
>                          wxString & _label, wxPoint & _pos, wxSize & _size,
>                          long _style, wxValidator & _validator,
>                          wxString & _name);
>      wxString wxButton_GetLabel (wxButton _obj);
>      wxWindow *wxButton_SetDefault (wxButton _obj);
>      void wxButton_SetLabel (wxButton _obj, wxString & _label);
>      wxSize wxButton_GetDefaultSize (wxButton _obj);
> 
> 
> Greetings,
> 
> Mads Lindstrøm
> 

"""
Name: c_wrapper.py
Author: Mads Lindstroem
License: wxWidgets License
"""


#!/usr/bin/env python

import doxymlparser
import optparse
import sys
import os
import string

parser = optparse.OptionParser(usage="usage: %prog <doxyml files to parse>\n" , version="%prog 1.0")

options, arguments = parser.parse_args()

# Intersperses elem between each list element
def intersperse(elem, alist):
    ret = []
    first = True
    for i in alist:
        if (not first) :
            ret.append(elem)
        ret.append(i)
        first = False
    
    return ret

def wxc_type_for_type(type):
    return type

def is_constructor(amethod, aclass):
    return amethod.name == aclass.name

def is_destructor(amethod, aclass):
    return amethod.name == "~" + aclass.name

def is_void(atype):
    return atype == "void"

def is_buildin_type(atype):
    #FIXME: needs more than bool
    return atype == "bool"

def param_name(aparam):
    return "_" + aparam["declname"]

def keep_alpha(s):
    return filter(lambda y: y.isalpha(), s)

# Mangles the method name if two methods has the same name.
# Otherwise the method name is returned unchanged.
#
# We mangle by addding all parameter-types to the method name.
def mangled_method_name(amethod, aclass):
    if len(filter(lambda x: x.name == amethod.name, aclass.methods)) == 1:
        return amethod.name
    else:
        # Methods in C++ can vary only in being const
        if amethod.return_type.find("const") == -1:
            constReturn = ""
        else:
            constReturn = "_const"
        return amethod.name + "".join(intersperse("_", \
                   map(lambda x: keep_alpha(x["type"]), amethod.params))) \
                   + constReturn
        #str(len(amethod.params))

def prototype(amethod, aclass):
    prot = "";
    className = wxc_type_for_type(aclass.name)

    if is_constructor(amethod, aclass):
        prot += "/* Constructor */\n"
        prot += "%s * %s_%s(" % (className, className, mangled_method_name(amethod, aclass))
        set_comma = False

    elif is_destructor(amethod, aclass) :
        prot += "void %s_Destruct(%s * _obj" % (className, className)
        set_comma = True

    else:
        if is_void(amethod.return_type):
            prot += "void "
        elif is_buildin_type(amethod.return_type):
            prot += wxc_type_for_type(amethod.return_type) + " "
        else:
            prot += wxc_type_for_type(amethod.return_type) + " " #"* "
        prot += "%s_%s(%s * _obj" % (aclass.name, mangled_method_name(amethod, aclass), className)
        set_comma = True

    for param in amethod.params:
        if set_comma:
            prot += ", "
                
        prot += "%s %s" % (wxc_type_for_type(param["type"]), param_name(param))
        set_comma = True
        
    prot += " )"

    return prot

def write_and_indent(contents, filename):
    wxc_file = open(filename, "wb")
    wxc_file.write(contents)
    wxc_file.close()
    # FIXME: Everybody may not have indent installed. We need to handle failure gracefully/silently.
    os.system("indent " + filename)
  

def make_header_file(aclass, filename):
    gaurd = string.replace(string.upper(filename), ".", "_")
    
    wxc_header =  "#ifndef %s\n #define %s\n" % (gaurd, gaurd)
    # Warning: #Include-s must come before extern "C", otherwise we get
    # "error: template with C linkage", see
    # http://www.gidforums.com/t-9853.html
    wxc_header += """
#include "wx/wx.h"

#ifdef __cplusplus
extern "C" {
#endif


"""
        
    for amethod in aclass.methods:
        wxc_header += "extern " + prototype(amethod, aclass)
        wxc_header += ";\n"
    
    wxc_header += """
#ifdef __cplusplus
}
#endif
"""
    wxc_header += "#endif //" + gaurd
    
    write_and_indent(wxc_header, filename)

def invoke_params(amethod):
    return "(%s)" % ("".join(intersperse(", ", map (param_name, amethod.params))))

def make_cpp_file(aclass, header_filename, cpp_filename):
    wxc_cpp = "#include \"" + header_filename + "\"\n\n"
    for amethod in aclass.methods:
        wxc_cpp += "\n\n"
        wxc_cpp += prototype(amethod, aclass)
        wxc_cpp += "{"
        
        if is_constructor(amethod, aclass):
            wxc_cpp += "new %s %s;"           % (aclass.name, invoke_params(amethod))
        elif is_destructor(amethod, aclass):
            wxc_cpp += "delete (_obj);"
        elif is_void(amethod.return_type):
            wxc_cpp += "_obj->%s %s;"         % (amethod.name, invoke_params(amethod))
        elif (amethod.return_type == "bool"):
            wxc_cpp += "return _obj->%s %s;"  % (amethod.name, invoke_params(amethod))
        else:
            wxc_cpp += "return _obj->%s %s ;" % (amethod.name, invoke_params(amethod))

        wxc_cpp += "}"

    write_and_indent(wxc_cpp, cpp_filename)

def make_files(doxyparse):
    for aclass in doxyparse.classes:
        base_filename   = "wrapped_" + os.path.basename(aclass.name)
        header_filename = base_filename + ".h"
        cpp_filename    = base_filename + ".cpp"
        
        make_header_file(aclass, header_filename)
        make_cpp_file(aclass, header_filename, cpp_filename)

if __name__ == "__main__":
    if len(arguments) < 1:
        parser.print_usage()
        sys.exit(1)
    
    doxyparse = doxymlparser.DoxyMLParser()
    for arg in arguments:
        doxyparse.parse(arg)

    make_files(doxyparse)

    #    for aclass in doxyparse.classes:
    #        print str(aclass)

    # We need smarter include file handling, but doxymlparser.py ignores includes.
    

# http://www.haskell.org/haskellwiki/IO_inside
# http://www.haskell.org/haskellwiki/CPlusPlus_from_Haskell

# rm -f wrapped_* && python c_wrapper.py out/xml/classwx_button.xml
# g++ `wx-config --libs` `wx-config --cxxflags` -c wrapped_wxButton.cpp
# ghc --make wrapped_wxButton.o Main.hs

Index: doxymlparser.py
===================================================================
--- doxymlparser.py	(revision 60099)
+++ doxymlparser.py	(working copy)
@@ -54,7 +54,7 @@
         str_repr = """
 Class: %s
 Bases: %s
-Inlcudes: %s
+Includes: %s
 Brief Description: 
 %s
 
@@ -98,9 +98,10 @@
         if child.nodeType == child.ELEMENT_NODE and child.nodeName == "ref":
             text += getTextValue(child)
         if child.nodeType == child.TEXT_NODE:
-            text += child.nodeValue.strip()
+            text += child.nodeValue.strip() + " "
+            # Changed line above to get space between qualifies and parameter names
             
-    return text
+    return text.strip()
 
 def doxyMLToText(node):
     return text
#include "wrapped_wxButton.h"



/* Constructor */
wxButton *
wxButton_wxButton ()
{
  new wxButton ();
}

/* Constructor */
wxButton *
wxButton_wxButtonwxWindow_wxWindowID_constwxString_constwxPoint_constwxSize_long_constwxValidator_constwxString
  (wxWindow * _parent, wxWindowID _id, const wxString & _label,
   const wxPoint & _pos, const wxSize & _size, long _style,
   const wxValidator & _validator, const wxString & _name)
{
  new wxButton (_parent, _id, _label, _pos, _size, _style, _validator, _name);
}

void
wxButton_Destruct (wxButton * _obj)
{
  delete (_obj);
}

bool
wxButton_Create (wxButton * _obj, wxWindow * _parent, wxWindowID _id,
		 const wxString & _label, const wxPoint & _pos,
		 const wxSize & _size, long _style,
		 const wxValidator & _validator, const wxString & _name)
{
  return _obj->Create (_parent, _id, _label, _pos, _size, _style, _validator,
		       _name);
}

wxString
wxButton_GetLabel (wxButton * _obj)
{
  return _obj->GetLabel ();
}

wxWindow *
wxButton_SetDefault (wxButton * _obj)
{
  return _obj->SetDefault ();
}

void
wxButton_SetLabel (wxButton * _obj, const wxString & _label)
{
  _obj->SetLabel (_label);
}

wxSize
wxButton_GetDefaultSize (wxButton * _obj)
{
  return _obj->GetDefaultSize ();
}
#ifndef WRAPPED_WXBUTTON_H
#define WRAPPED_WXBUTTON_H

#include "wx/wx.h"

#ifdef __cplusplus
extern "C"
{
#endif


  extern			/* Constructor */
  wxButton *wxButton_wxButton ();
  extern			/* Constructor */
  wxButton
    *wxButton_wxButtonwxWindow_wxWindowID_constwxString_constwxPoint_constwxSize_long_constwxValidator_constwxString
    (wxWindow * _parent, wxWindowID _id, const wxString & _label,
     const wxPoint & _pos, const wxSize & _size, long _style,
     const wxValidator & _validator, const wxString & _name);
  extern void wxButton_Destruct (wxButton * _obj);
  extern bool wxButton_Create (wxButton * _obj, wxWindow * _parent,
			       wxWindowID _id, const wxString & _label,
			       const wxPoint & _pos, const wxSize & _size,
			       long _style, const wxValidator & _validator,
			       const wxString & _name);
  extern wxString wxButton_GetLabel (wxButton * _obj);
  extern wxWindow *wxButton_SetDefault (wxButton * _obj);
  extern void wxButton_SetLabel (wxButton * _obj, const wxString & _label);
  extern wxSize wxButton_GetDefaultSize (wxButton * _obj);

#ifdef __cplusplus
}
#endif
#endif				//WRAPPED_WXBUTTON_H
------------------------------------------------------------------------------
This SF.net email is sponsored by:
High Quality Requirements in a Collaborative Environment.
Download a free trial of Rational Requirements Composer Now!
http://p.sf.net/sfu/www-ibm-com
_______________________________________________
wxhaskell-users mailing list
wxhaskell-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/wxhaskell-users

Reply via email to