From: Christian Szegedy Date: 2002-06-11T22:19:49+09:00 Subject: Re: ruby-qt3 proposal (c++ based extensions) This is a multi-part message in MIME format. --------------060102080401090803070707 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Martin Man wrote: > hi all, > > I've prepared a short proposal on how to quickly (but without loosing > features) create qt3 extension module for ruby, ..., if you are interested > please read on and comment. This proposal came from the fact that we have > actually gcc-xml output of all qt3 headers that can be reused somehow... > > note that such proposal does not include any talk about signals and slots, > these have to come yet, but it hopefully covers the c++ overloading problem.. > > I proposed a similar solution already. Please refer the last thread on qt. I could imagine some improvement to your solution: For each qt class with virtual functions an overridable and not overridable version. This would improve performance in the very commmon case, when you know that you don't want to reimplement any virtual functions. For the overridable class, I would create a function pointer table (playing the same role as the virtual function pointer table.), having the pointer to the original virtual methods. If a new method on the ruby-side is defined, then it could change the pointer in this new method table. This, again, would improrve runtime. The drawback would be that the handling of singleton methods can get quite complicated, if not impossible. This no tragedy, but it must be documented then. (Using this strategy, the first proposal may get unnecessary.) Some weeks ago, I created the XML files for qt3 using xmlgcc. This was fairly straightforward. I also created a small Ruby programm using REXML, that parses the generated representation and builds an internal representation which could be used to generate the wrapper. I planned to write a complete wrapper using this implementation, since the SWIG has some annoying features including the strangeness for the .new methods. Additionally, I would like to see a more intuitive handling of properties, i.e., all the methods setXXX could be renamed to XXX=, and a block oriented wrapping of signals. I.e., I would like blocks to be connectable with signeals immediately, which would be the real Ruby way. I have attached my intial Ruby script I planned to be the base of this new wrapper generator. The most complicated part of the script (which I does not assume to function 100%-ly correctly), is the output of type names and function declarations based on the internal representation (The with_full_type methods). However, this will be quite crucial for the planned wrapper generator. Best regards, Christian --------------060102080401090803070707 Content-Type: text/plain; name="parse.rb" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="parse.rb" #!/usr/bin/env ruby require "rexml/document" require "rexml/parseexception" include REXML module CPP PUBLIC=0 PROTECTED=1 PRIVATE=2 INVALID_ACCESS=3 AccessTrans = { "public" => PUBLIC, "protected" => PROTECTED, "private" => PRIVATE } AccessToString = ["public","protected","private"] ById =[] ConstById ={} FileById =[] module WithLocation attr_reader :fname,:line def location=(loc) @fname,@line = loc end end class Type include WithLocation def named(tdef,cx=nil) name end end module Decl attr_accessor :const,:rettype,:args def init_decl @const = false @rettype = nil @args = [] end def add_arg(type) @args << type end def put_decl(fname,cx=nil) i=0 ret = rettype ? rettype.named("",cx) : "" ret << " #{fname}("+@args.map{|a| i+=1; a.named("p#{i}",cx)}.join(",")+")" end end class FunctionType < Type include Decl def initialize init_decl end def to_s put_decl("") end def named(tdef,cx=nil) put_decl("(#{tdef})") end end class MethodType < Type include Decl attr_accessor :basetype,:const def initialize init_decl @const = false end def to_s "--- invalid type ---" end def named(tdef,cx=nil) conststr = const ? " const " : "" put_decl("(#{basetype.named(tdef,cx)}::#{tdef})")+conststr; end end class FundamentalType < Type attr_reader :name def initialize(name) @name = name end def to_s @name end def named(tdef,cx=nil) "#{@name} #{tdef}" end end class Base attr_accessor :tp,:access end module WithContext attr_reader :context attr_accessor :access def context=(cx) if ( @context = cx ) cx.add(self) end @access ||= PUBLIC end def full_name(cx=nil) ret = @name if @context && ((!cx) || !(cx.equal?(@context))) if @context.full_name(cx) != "::" ret = @context.full_name(cx)+"::"+@name else ret = "::#{@name}" end else ret = @name end ret end end class Multifield < Type include WithContext def initialize(name) init_context(name) end def meta_name # For C compatibility (we have to prefix structs and unions) "" end def full_meta_name meta_name end def to_s meta_name + full_name end def with_full_type(cx=nil) full_meta_name + " "+ @name+"{\n"+ dump+ "}\n" end def anon_name full_meta_name + " {\n"+ dump+ "}\n" end def named(n,cx=nil) if !equal?(cx) fn = full_name(cx) if fn.include? "." return anon_name + n else return fn end else name+n end end end class Data include WithLocation include WithContext attr_reader :name attr_accessor :tp def to_s full_name end def initialize(name) @name = name @access = PUBLIC end def with_full_type(cx=nil) @tp.named(@name,cx) end end class Variable < Data end class Field < Data end class Method include WithContext include Decl def to_s put_decl(name) end end class Class < Multifield attr_accessor :bases def full_meta_name "class" end def initialize(name) super(name) @bases = [] end end class Struct < Multifield attr_accessor :bases def initialize(name) super(name) @bases = [] end def meta_name "struct " end end class Union < Multifield def meta_name "union " end end class Typedef include WithContext attr_reader :name attr_accessor :tp def initialize(name) @name = name end def to_s @tp.named(full_name) end def with_full_type(cx=nil) @tp.with_full_type(cx) end end class Enumeration < Type include WithContext def to_s full_name end def with_full_type(cx=nil) @name #TODO end end class PointerType < Type attr_accessor :tp def to_s if @tp @tp.named("*") else "" end end def named(tdef,cx=nil) if @tp @tp.named("*#{tdef}",cx) else "" end end end class ArrayType < Type attr_accessor :tp attr_accessor :min attr_accessor :max def to_s @tp.named("[#{@max+1}]") if @tp end def named(tdef,cx=nil) if @tp @tp.named("#{tdef}[#{@max+1}]",cx) else "" end end end class ReferenceType < Type attr_accessor :tp def to_s @tp.named("&") if @tp end def named(tdef,cx=nil) @tp.named("&#{tdef}",cx) end end class MultiHash < Hash alias orig_get [] def add(key,value) (orig_get(key)||(self[key]=[]))< :vars, Typedef => :typedefs, Enumeration => :enums, Method => :methods, Class => :classes, Field => :fields } Attribs = [ :classes,:enums,:typedefs,:vars,:fields,:methods ] def add(obj) id = Type2Attr[obj.type] send(id).add(obj.name,obj) if id end def dump acc=INVALID_ACCESS result = "" Attribs.each do |a| send(a).each_value do |v| puts v.name if v.access != acc acc = v.access result << AccessToString[acc] + ":\n" end n = v.name if n && !(n.include? ".") result << v.with_full_type(self) << ";" end end end result end end class Multifield include Context end class Const < Type attr_reader :tp def initialize(tp) @tp = tp end def to_s @tp.named(" const ") end def named(tdef,cx=nil) @tp.named(" const "+tdef,cx) end end def id2obj(id) return nil unless id id2 = id.dup if id2.chomp!("c") id3 = id2.to_i ConstById[id3] ||= Const.new((ById[id3] or raise "Invalid id #{id3} encountered.")) else id3 = id2.to_i ById[id3] or raise "Invalid id #{id3} encountered." end end class Method include WithContext include Decl attr_reader :name attr_accessor :extern attr_accessor :virtual,:abstract,:static def initialize(name) init_decl @name = name @access = PUBLIC @extern = false @virtual = false @abstract = false @static = false end def to_s put_decl(@name) end def with_full_type(cx=nil) put_decl(full_name(cx),cx) end end class Namespace include WithLocation include WithContext include Context def initialize(name) init_context(name) end def to_s full_name end end class EnumValue attr_reader :name,:value,:tp def initialize(name,value,tp) @name = name @value = value end def to_s "#{@tp}::#{@name}" end end class Enumeration < Type include WithContext attr_reader :name attr_accessor :name,:values def initialize(name) @name = name @values = {} end def add_value(name,value) @values[name] = EnumValue.new(name,value,self) end def to_s full_name end end NameToClass = { "Namespace" => Namespace, "Enumeration" => Enumeration, "Function" => Method, "Class" => Class, "Struct" => Class, "Constructor" => Method, "FundamentalType" => FundamentalType, "Method" => Method, "ReferenceType" => ReferenceType, "PointerType" => PointerType, "ArrayType" => ArrayType, "Variable" => Variable, "Typedef" => Typedef, "FunctionType" => FunctionType, "MethodType" => MethodType, "Field" => Field, "Union" => Union } def int2bool(i) i && (i!="0") end def access_by_string(s) AccessTrans[s] end def str2int(s) s.to_i end def str2loc(s) fname,line = s[1..-1].split(":") fname = FileById[fname.to_i] line = line.to_i [fname,line] end PossibleAttributes = [ ["tp=","type",:id2obj], ["context=","context",:id2obj], ["const=","const",:int2bool], ["static=","static",:int2bool], ["extern=","extern",:int2bool], ["virtual=","virtual",:int2bool], ["abstract=","abstract",:int2bool], ["rettype=","returns",:id2obj], ["access=","access",:access_by_string], ["basetype=","basetype",:id2obj], ["min=","min",:str2int], ["max=","max",:str2int], ["location=","location",:str2loc] ] def parse(doc) doc.each_element("*/*") do |e| a = e.attributes if e.name == "File" FileById[a["id"][1..-1].to_i] = a["name"] elsif (c = NameToClass[e.name]) n=a["name"] #puts "hmm: #{c} #{n}" ById[a["id"].to_i] = n ? c.new(n) : c.new else puts "Not interpreted tag #{e.name}" end end puts "Pass 1 ready." doc.each_element("*/*") do |e| a = e.attributes i=a["id"].to_i if obj =ById[i] PossibleAttributes.each do |attr,transl,func| if obj.respond_to?(attr) && (t=a[transl]) puts attr obj.send(attr,send(func,t)) end end e.each_element("*") do |child| # p child ca = child.attributes puts child.name case child.name when "Argument" puts "add arg" obj.add_arg(id2obj(ca["type"])) when "EnumValue" obj.add_value(ca["name"],ca["init"].to_i) end end end end puts "BEGIN" for obj in ById puts "----------------------------" puts obj if obj.respond_to? "with_full_type" puts "Full type:" puts(obj.with_full_type) end if obj.respond_to? "fname" puts obj.fname if obj.fname end if obj.respond_to? "line" puts obj.line if obj.fname end end end extend CPP end f = File.new( "t2.xml" ) puts "Parsing document..." doc = REXML::Document.new f puts "Parsing reeady." #rec(r,0) #p doc.root CPP.parse(doc) --------------060102080401090803070707--