#!/usr/bin/env ruby # # Copyright (C) 2006-2024 Matthias Koefferlein # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # $:.push(File.dirname($0)) require 'oj' require 'cpp_classes.rb' require 'reader_ext.rb' input_file = "all.db" conf_file = "mkqtdecl.conf" cls_list = nil excl_list = {} modn = "Qt" $gen_dir = "generated" while ARGV.size > 0 o = ARGV.shift if o == "-i" input_file = ARGV.shift elsif o == "-c" cls_list = ARGV.shift elsif o == "-x" excl_file = ARGV.shift File.open(excl_file, "r") do |file| file.each_line { |l| excl_list[l.chop] = true } end elsif o == "-s" conf_file = ARGV.shift elsif o == "-m" modn = ARGV.shift else raise("Invalid option #{o} - usage is 'produce.rb -s mkqtdecl.conf -i all.db -c QWidget,QSizePolicy'") end end # Objects of this type require special treatment when passing values between GSI and Qt # (a converter is instantiated) SpecialClasses = [ "QChar", "WId", "Q_PID" ] # These typedefs are maintained to provide some abstraction MaintainTypedefs = [ "Q_PID", "WId", "HANDLE", "Qt::HANDLE", "qulonglong", "qlonglong", "qint8", "qint16", "qint32", "qint64", "quint8", "quint16", "quint32", "quint64", "quintptr" ] # The substitutions for signal/slot signatures to maintain the signature SignalSubstitutions = { "QList" => "QModelIndexList" } class CPPDeclaration def is_const? self.type.func.cv == :const end def ref self.type.func.ref end def hash_str # TODO: this is a hack for making the hash values unique: $unique ||= {} # (weak) backward compatibility for hash computation func = self.type.func nmax = func.max_args hk = (self.is_const? ? "c" : "") if self.ref if self.ref == "&" hk += "r" elsif self.ref == "&&" hk += "rr" end end if nmax > 0 args = nmax.times.collect { |ia| func.args[ia].anonymous_type.to_s } sig = args.join(",") hk += args.collect { |a| a.split(/\s+/) }.inspect.sum.to_s hku = 0 hk_basic = hk while ($unique[self.type.name + "-" + hk] ||= sig) != sig hku += 1 hk = hk_basic + "u" + hku.to_s end else hk += "0" end hk end def call_sig func = self.type.func nmax = func.max_args args = nmax.times.collect { |ia| func.args[ia].anonymous_type.to_s }.join(", ") res = "(" + args + ")" if self.is_const? res += " const" end if self.ref res += " " + self.ref end res end def sig(cls) self.type.name_substituted_type(CPPQualifiedId::new(false, [ cls, self.type.func.func_name ])).to_s end def raw_sig(cls) # backward compatibility for signature computation s = self.type.name_substituted_type(CPPQualifiedId::new(false, [ cls, self.type.func.func_name ])).to_s if self.ref s = s.sub(/\s+&+$/, "") end if self.is_const? s = s.sub(/\s+const$/, "") end s end end class CPPNamespace def collect_enum_decls(map, &filter) self.members.each do |bd| if bd.is_a?(CPPEnumDeclaration) bd.enum && filter.call(bd) && (map[bd.enum.name] ||= bd) end end end end class CPPModule def collect_enum_decls(map, &filter) self.decls.each do |bd| if bd.is_a?(CPPEnumDeclaration) bd.enum && filter.call(bd) && (map[bd.enum.name] ||= bd) end end end end class CPPEnum def resolve_typedefs(scope) end def rescope(prev_scope, other_scope, idpath) end def each_qid(&block) end end class CPPStruct def resolve_typedefs(scope) end def rescope(prev_scope, other_scope, idpath) end def each_qid(&block) end def is_qobject?(conf) if self.id.to_s == "QObject" return true end self.each_base_class(conf) do |bc| if bc.struct.is_qobject?(conf) return true end end return false end def needs_adaptor(conf) cls = self.id.to_s if conf.is_final_class?(cls) return false end all_methods = {} self.collect_all_methods(all_methods, conf) # If there is a private destructor, we cannot create an adaptor dtor = self.get_dtor if dtor && dtor.visibility == :private return false end # we generate an adaptor if there is a virtual destructor # Note: an adaptor can only be generated if the class has a vtable since # it will get a vtable through QtObjectBase and because of this a pure # cast to the base class will fail (base class = without vtable, derived class # = with vtable). But that cast is the basis of the void * representation of # the objects. needs_adaptor = dtor && dtor.virtual all_methods.each do |mn,m| m.each do |bd| if bd.virtual && conf.target_name(cls, bd, mn) needs_adaptor = true elsif bd.virtual && bd.type.init == "0" # an abstract method was dismissed -> cannot generate an adaptor return false end end end # QObject will always require an adaptor to produce event emitters return is_qobject?(conf) || needs_adaptor end def collect_used_enums(map, conf) cls = self.id.to_s # collect used enums from inner classes (self.body_decl || []).each do |bd| decl = nil if bd.is_a?(CPPStructDeclaration) && bd.visibility == :public && bd.struct.body_decl && bd.myself != "" && !conf.is_class_dropped?(cls, bd.myself) bd.struct.collect_used_enums(map, conf) end end # collect used enums from base classes (self.base_classes || []).each do |bc| bc_obj = self.parent.resolve_qid(bc.class_id) if bc_obj.is_a?(CPPStructDeclaration) && bc_obj.visibility == :public && bc_obj.struct.body_decl && bc_obj.myself != "" && !conf.is_class_dropped?(cls, bc_obj.myself) bc_obj.struct.collect_used_enums(map, conf) end end methods = {} self.collect_all_methods(methods, conf) methods[cls] = self.collect_ctors needs_adaptor = self.needs_adaptor(conf) methods.each do |mn,m| m.each do |bd| vis = bd.visibility if vis == :public || (vis == :protected && needs_adaptor) # don't consider dropped methods conf.target_name(cls, bd, mn) || next bd.type.each_qid do |qid| obj = self.parent.resolve_qid(qid) if obj.is_a?(CPPEnumDeclaration) && obj.visibility == :public map[obj.object_id] = obj end end end end end end def collect_enum_decls(map, &filter) (self.body_decl || []).each do |bd| if bd.is_a?(CPPEnumDeclaration) && bd.visibility == :public bd.enum && filter.call(bd) && (map[bd.enum.name] ||= bd) end end end def collect_methods(map, weak = false) mmap = {} (self.body_decl || []).each do |bd| decl = nil if bd.is_a?(CPPDeclaration) && ! bd.template_decl vis = bd.visibility decl = bd elsif bd.is_a?(CPPUsingSpec) # resolve using specs vis = bd.visibility tbd = self.parent.resolve_qid(bd.qid) if tbd && tbd.is_a?(CPPDeclaration) && ! tbd.template_decl decl = tbd end end if decl func = decl.type.func if func && decl.type.concrete != nil mn = func.func_name cls = self.id.to_s decl_new = decl.dup decl_new.visibility = vis decl_new.type.resolve_typedefs(self.parent) decl_new.type.rescope(self.parent, self.global_scope, false) (mmap[mn] ||= []) << decl_new end end end # take non-duplicates (by call signature) for the map # weak ones do not redefine methods mmap.each do |mn,decls| seen = {} decls.each do |d| s = d.call_sig if !seen[s] seen[s] = true if !weak || !map[mn] (map[mn] ||= []) << d end end end end end def get_dtor (self.body_decl || []).each do |bd| if bd.is_a?(CPPDeclaration) func = bd.type.func if func mn = func.func_name cls = self.id.to_s if mn == "~" + cls if ! bd.virtual # inherit the virtual flag from the base classes (self.base_classes || []).each do |bc| bc_obj = self.parent.resolve_qid(bc.class_id) if bc_obj.is_a?(CPPStructDeclaration) bc_dtor = bc_obj.struct.get_dtor if bc_dtor && bc_dtor.virtual bd.virtual = true return bd end end end end return bd end end end end nil end def collect_ctors ctors = [] (self.body_decl || []).each do |bd| if bd.is_a?(CPPDeclaration) && ! bd.template_decl # only public ctors are considered - currently protected ones are not used bd.visibility == :public || next func = bd.type.func if func mn = func.func_name cls = self.id.to_s if mn == cls bd_new = bd.dup bd_new.type.resolve_typedefs(self.parent) bd_new.type.rescope(self.parent, self.global_scope, false) ctors << bd_new end end end end ctors end def collect_all_methods(map, conf) self.collect_methods(map) (self.base_classes || []).select { |b| conf.imported?(self.id.to_s, b.class_id.to_s) }.each do |bc| bc_obj = self.parent.resolve_qid(bc.class_id) if bc_obj.is_a?(CPPStructDeclaration) base_map = {} bc_obj.struct.collect_all_methods(base_map, conf) # derived classes override base class declarations base_map.each do |mn, d| # virtual is inherited to the derived class map[mn] ||= d d.each do |bd_base| if bd_base.virtual sig = bd_base.call_sig map[mn].each do |bd| bd.call_sig == sig && (bd.virtual = true) end end end end elsif bc_obj puts("Warning: #{bc.class_id.to_s} is not a base class in #{self.id.to_s}") else puts("Cannot find base class: #{bc.class_id.to_s} of #{self.parent.myself} - declaration ignored") end end end def each_base_class(conf, &block) (self.base_classes || []).select { |b| conf.imported?(self.id.to_s, b.class_id.to_s) }.each do |bc| bc_obj = self.parent.resolve_qid(bc.class_id) if bc_obj.is_a?(CPPStructDeclaration) block.call(bc_obj) bc_obj.struct.each_base_class(conf, &block) end end end end class CPPEllipsis def resolve_typedefs(scope) end def rescope(prev_scope, other_scope, idpath) end def each_qid(&block) end end class CPPPOD def resolve_typedefs(scope) end def rescope(prev_scope, other_scope, idpath) end def each_qid(&block) end end class CPPAnonymousId def resolve_typedefs(scope) end def rescope(prev_scope, other_scope, idpath) end def each_qid(&block) end end class CPPOuterType def resolve_typedefs(scope) self.inner.resolve_typedefs(scope) end def rescope(prev_scope, other_scope, idpath) self.inner.rescope(prev_scope, other_scope, idpath) end def each_qid(&block) self.inner.each_qid(&block) end end class CPPFunc def func_name self.inner.is_a?(CPPQualifiedId) && self.inner.parts[-1].to_s end def min_args n = 0 self.args.each do |a| if a.is_a?(CPPType) && !a.init n += 1 else break end end n end def max_args n = 0 self.args.each do |a| if a.is_a?(CPPType) n += 1 else break end end n end def resolve_typedefs(scope) self.args && self.args.each { |a| a.resolve_typedefs(scope) } end def rescope(prev_scope, other_scope, idpath) self.inner && self.inner.rescope(prev_scope, other_scope, idpath) self.args && self.args.each { |a| a.rescope(prev_scope, other_scope, false) } end def each_qid(&block) self.inner && self.inner.each_qid(&block) self.args && self.args.each { |a| a.each_qid(&block) } end end class CPPConst def resolve_typedefs(scope) end def rescope(prev_scope, other_scope, idpath) end def each_qid(&block) end end class CPPTemplateArgs def resolve_typedefs(scope) self.args && self.args.each { |a| a.resolve_typedefs(scope) } end def rescope(prev_scope, other_scope, idpath) self.args && self.args.each { |a| a.rescope(prev_scope, other_scope, false) } end def each_qid(&block) self.args && self.args.each { |a| a.each_qid(&block) } end end class CPPQualifiedId def resolve_typedefs(scope) self.parts.each do |p| p.template_args && p.template_args.resolve_typedefs(scope) end end def each_qid(&block) block.call(self) self.parts.each do |p| p.template_args && p.template_args.each_qid(&block) end end def rescope(prev_scope, other_scope, idpath) # don't rescope identifiers idpath && return self.parts.each do |p| p.template_args && p.template_args.rescope(prev_scope, other_scope, false) end if !self.global && prev_scope != other_scope parents = {} o = other_scope while o parents[o.parent] = true o = o.parent end obj = prev_scope.resolve_qid(self) if obj self.parts = [ self.parts[-1] ] s = obj.parent while s && !parents[s] s.myself && self.parts.unshift(CPPId::new(s.myself, nil)) s = s.parent end parents[s] || raise("Unable to rescope from #{obj.parent.myself || '::'} to #{other_scope.myself || '::'}") end end end end class CPPType def each_qid(&block) self.inner && self.inner.each_qid(&block) self.concrete && self.concrete.each_qid(&block) end # @brief Returns the CPPQualifiedId inside self.concrete or nil if there is none def concrete_qid it = self.concrete while it.respond_to?(:inner) it = it.inner end it.is_a?(CPPQualifiedId) && it end # @brief Replaces the CPPQualifiedId inside self.concrete def concrete_qid=(qid) it = self.concrete if it.is_a?(CPPQualifiedId) self.concrete = qid else itt = nil while it.respond_to?(:inner) itt = it it = it.inner end itt && (itt.inner = qid) end end def resolve_typedefs(scope) while self.concrete_qid qid = self.concrete_qid # keep some typedefs since they provide some abstraction if MaintainTypedefs.index(qid.to_s) break end obj = scope.resolve_qid(qid) if obj && obj.is_a?(CPPTypedef) new_type = obj.type.dup it = new_type while it.respond_to?(:inner) if it.inner.is_a?(CPPAnonymousId) || it.inner.is_a?(CPPQualifiedId) it.inner = self.inner break else it = it.inner end end self.inner = new_type.inner self.concrete_qid = new_type.concrete self.rescope(obj.parent, scope, false) else # resolve template arguments qid.resolve_typedefs(scope) break end end self.inner.resolve_typedefs(scope) end def rescope(prev_scope, other_scope, idpath) self.inner && self.inner.rescope(prev_scope, other_scope, true) self.concrete && self.concrete.rescope(prev_scope, other_scope, false) end def is_enum(decl_obj) if self.concrete_qid obj = decl_obj.resolve_qid(self.concrete_qid) # replace protected enum's by unsigned int (TODO: is that necessary?) return obj.is_a?(CPPEnumDeclaration) && obj.visibility == :protected end false end def is_special(decl_obj) if self.concrete_qid obj = decl_obj.resolve_qid(self.concrete_qid) if obj.is_a?(CPPEnumDeclaration) return true elsif obj.is_a?(CPPTypedef) || obj.is_a?(CPPStructDeclaration) name = obj.myself return SpecialClasses.index(name) != nil end end false end def access_qt_arg(decl_obj, expr = nil) expr ||= self.name expr || raise("access_qt called with nil expression") if self.is_enum(decl_obj) # enums currently use unsigned int for the target name return "#{self.anonymous_type.to_s}(" + expr + ")" elsif self.is_special(decl_obj) ta = self.anonymous_type.to_s if ta =~ /^const (.*) &&$/ ta = $1 acc = ".cmove()" elsif ta =~ /^(.*) &&$/ ta = $1 acc = ".move()" elsif ta =~ /^const (.*) &$/ ta = $1 acc = ".cref()" elsif ta =~ /^(.*) &$/ ta = $1 acc = ".ref()" elsif ta =~ /^const (.*) \*$/ ta = $1 acc = ".cptr()" elsif ta =~ /^(.*) \*$/ ta = $1 acc = ".ptr()" else ta = ta.sub(/^const /, "") acc = ".cref()" end ta = ta.sub(/>$/, "> ") return "qt_gsi::QtToCppAdaptor<#{ta}>(" + expr + ")#{acc}" else return expr end end def access_qt_return(decl_obj, expr = nil) expr ||= self.name expr || raise("access_qt called with nil expression") if self.is_enum(decl_obj) # enums currently use unsigned int for the target name return "#{self.anonymous_type.to_s}(" + expr + ")" elsif self.is_special(decl_obj) ta = self.anonymous_type.to_s acc = "" racc = "" # Handle references and pointers in returns as copies - we loose the ability to write, # but can at least read them. if ta =~ /^const (.*) &$/ || ta =~ /^(.*) &$/ ta = $1 elsif ta =~ /^const (.*) &&$/ || ta =~ /^(.*) &&$/ ta = $1 elsif ta =~ /^const (.*) \*$/ || ta =~ /^(.*) \*$/ ta = $1 acc = "*" else # TODO: this is not required but for backward compatibility - questionable! racc = ".cref()" end ta = ta.sub(/^const /, "") ta = ta.sub(/>$/, "> ") return "qt_gsi::QtToCppAdaptor<#{ta}>(" + acc + expr + ")#{racc}" else return expr end end def access_gsi_arg(decl_obj, expr = nil) if self.is_enum(decl_obj) # enums currently use unsigned int for the target name return "(unsigned int)(" + (expr || self.name) + ")" elsif self.is_special(decl_obj) ta = self.anonymous_type.to_s if ta =~ /^const (.*) &$/ || ta =~ /^(.*) &$/ acc = "" ta = $1 elsif ta =~ /^const (.*) \*$/ || ta =~ /^(.*) \*$/ acc = "*" ta = $1 else ta = ta.sub(/^const /, "") # TODO: should be, but not backward compatible: acc = ".cref()" acc = "" end ta = ta.sub(/>$/, "> ") if expr # Used to derivce init values dynamically - need to store the value on the heap since the # read adaptor's lifetime is very limited and references are kept. return "qt_gsi::CppToQtReadAdaptor<#{ta}>(%HEAP%, " + acc + expr + ")" else return "qt_gsi::CppToQtAdaptor<#{ta}>(" + acc + self.name + ")" end else return expr || self.name end end def access_gsi_return(decl_obj, expr = nil) expr ||= self.name expr || raise("access_gsi called with nil expression") if self.is_enum(decl_obj) # enums currently use unsigned int for the target name return "(unsigned int)(" + expr + ")" elsif self.is_special(decl_obj) ta = self.anonymous_type.to_s acc = "" # Handle references and pointers in returns as copies - we loose the ability to write, # but can at least read them. if ta =~ /^const (.*) &$/ || ta =~ /^(.*) &$/ ta = $1 elsif ta =~ /^const (.*) \*$/ || ta =~ /^(.*) \*$/ ta = $1 acc = "*" end ta = ta.sub(/^const /, "") ta = ta.sub(/>$/, "> ") return "qt_gsi::CppToQtAdaptor<#{ta}>(" + acc + expr + ")" else return expr end end def gsi_decl_return(decl_obj) if self.is_enum(decl_obj) # enums currently use unsigned int for the target name return "unsigned int" + (self.name ? " " + self.name : "") elsif self.is_special(decl_obj) ta = (self.name ? self.anonymous_type.to_s : self.to_s) # return values of references or pointers are converted to plain copies - we cannot # convert them on the fly - a temporary object would not be sufficient if ta =~ /^const (.*) &$/ || ta =~ /^(.*) &$/ || ta =~ /^const (.*) \*$/ || ta =~ /^(.*) \*$/ ta = $1 end ta = ta.sub(/^const /, "") ta = ta.sub(/>$/, "> ") return "qt_gsi::Converter<#{ta}>::target_type" + (self.name ? " " + self.name : "") else return self.to_s end end def gsi_decl_arg(decl_obj) if self.is_enum(decl_obj) # enums currently use unsigned int for the target name return "unsigned int" + (self.name ? " " + self.name : "") elsif self.is_special(decl_obj) ta = (self.name ? self.anonymous_type.to_s : self.to_s) if ta =~ /^const (.*) &$/ ta = $1 const = "const " acc = " &" elsif ta =~ /^(.*) &$/ ta = $1 const = "" acc = " &" elsif ta =~ /^const (.*) \*$/ ta = $1 const = "const " acc = " *" elsif ta =~ /^(.*) \*$/ ta = $1 const = "" acc = " *" else ta = ta.sub(/^const /, "") acc = " &" const = "const " end ta = ta.sub(/>$/, "> ") return "#{const}qt_gsi::Converter<#{ta}>::target_type#{acc}" + (self.name ? " " + self.name : "") else return self.to_s end end end class Configurator def initialize @include = {} @no_copy_ctor = {} @no_default_ctor = {} @final_classes = {} @dropped_methods = {} @dropped_classes = {} @dropped_enums = {} @included_enums = {} @renamed_methods = {} @kept_args = {} @owner_args = {} @return_new = {} @aliased_methods = {} @dropped_enum_consts = {} @renamed_enum_consts = {} @aliased_enum_consts = {} @events = {} @property_readers = {} @property_writers = {} @no_imports = {} @native_impl = {} end def include(cls, files) @include[cls] ||= [] @include[cls] += files end def no_copy_ctor(cls) @no_copy_ctor[cls] = true end def has_copy_ctor?(cls) !@no_copy_ctor[cls] end def no_default_ctor(cls) @no_default_ctor[cls] = true end def has_default_ctor?(cls) !@no_default_ctor[cls] end def final_class(cls) @final_classes[cls] = true end def is_final_class?(cls) @final_classes[cls] end def includes(cls) @include[cls] end def no_imports(cls, base = :every_base) @no_imports[cls] ||= {} @no_imports[cls][base] = true end def imported?(cls, base) !(@no_imports[cls] && (@no_imports[cls][:every_base] || @no_imports[cls][base])) end def drop_enum_const(cls, sig) @dropped_enum_consts[cls] ||= [] @dropped_enum_consts[cls] << sig end def rename_enum_const(cls, sig, to) @renamed_enum_consts[cls] ||= [] @renamed_enum_consts[cls] << [ sig, to ] end def def_enum_const_alias(cls, sig, to) @aliased_enum_consts[cls] ||= [] @aliased_enum_consts[cls] << [ sig, to ] end def is_enum_const_dropped?(cls, sig) dm = (@dropped_enum_consts[:all_classes] || []) + (@dropped_enum_consts[cls] || []) dm.find { |d| sig =~ d } != nil end def drop_class(cls, inner_cls = :whole_class) @dropped_classes[cls] ||= [] @dropped_classes[cls] << inner_cls end def drop_method(cls, sig) @dropped_methods[cls] ||= [] @dropped_methods[cls] << sig end def return_new(cls, sig) @return_new[cls] ||= [] @return_new[cls] << sig end def owner_arg(cls, sig, nth) @owner_args[cls] ||= [] @owner_args[cls] << [ sig, nth ] end def keep_arg(cls, sig, nth) @kept_args[cls] ||= [] @kept_args[cls] << [ sig, nth ] end def rename(cls, sig, to) @renamed_methods[cls] ||= [] @renamed_methods[cls] << [ sig, to ] end def def_alias(cls, sig, to) @aliased_methods[cls] ||= [] @aliased_methods[cls] << [ sig, to ] end def is_dropped?(cls, sig) dm = (@dropped_methods[:all_classes] || []) + (@dropped_methods[cls] || []) dm.find { |d| sig =~ d } != nil end def is_class_dropped?(cls, sig = :whole_class) if cls != :all_classes && cls !~ /^Q\w+$/ # don't consider classes which are not plain Qt classes (i.e. templates, internal ones etc.) return true elsif cls != :all_classes && cls =~ /^QPrivateSignal$/ # drop QPrivateSignal because that's just a marker return true else dc = (@dropped_classes[:all_classes] || []) + (@dropped_classes[cls] || []) if sig != :whole_class return dc.find { |d| d == :whole_class || sig =~ d } != nil else return dc.find { |d| d == :whole_class || sig == d } != nil end end end # @brief Produce an enum target name def target_name_for_enum_const(cls, sig, mid, mm = nil, decl = nil) if is_enum_const_dropped?(cls, sig) return nil else name = mid rn = (@renamed_enum_consts[:all_classes] || []) + (@renamed_enum_consts[cls] || []) rt = rn.find { |d| sig =~ d[0] } if rt name = rt[1] end al = (@aliased_enum_consts[:all_classes] || []) + (@aliased_enum_consts[cls] || []) at = al.select { |d| sig =~ d[0] } if !at.empty? name += "|" + at.collect { |a| a[1] }.join("|") end name end end # @brief Gets a list of owner arguments which will keep the object is non-null def owner_args(bd) cls = bd.parent.myself || "::" kn = (@owner_args[:all_classes] || []) + (@owner_args[cls] || []) if !kn.empty? sig = bd.sig(bd.parent.myself || "") return kn.select { |d| sig =~ d[0] }.collect { |d| d[1] } else return [] end end # @brief Gets a list of arguments which need to be kept def kept_args(bd) cls = bd.parent.myself || "::" kn = (@kept_args[:all_classes] || []) + (@kept_args[cls] || []) if !kn.empty? sig = bd.sig(bd.parent.myself || "") return kn.select { |d| sig =~ d[0] }.collect { |d| d[1] } else return [] end end # @brief Gets a value indicating whether a method returns a new object # The lifetime of an object returned by such a method is managed by the # script client def returns_new(bd) cls = bd.parent.myself || "::" rn = (@return_new[:all_classes] || []) + (@return_new[cls] || []) if !rn.empty? sig = bd.sig(bd.parent.myself || "") return rn.find { |d| sig =~ d } != nil else return false end end # @brief Produce the target name for a method # Produces the target name string or nil if the method is supposed # to be dropped. def target_name(cls, bd, mid, mm = nil, decl = nil) sig = bd.sig(cls) # we also test for the parent of bd which may be different from cls if # the method is imported from a base class or through "using" cls2 = bd.parent.myself || "" sig2 = bd.sig(cls2) # the drop test includes the static attribute so we can distinguish between # static and non-static methods if bd.storage_class == :static sig = "static " + sig sig2 = "static " + sig2 end if is_dropped?(cls, sig) || is_dropped?(cls2, sig2) return nil end # strip operator name = mid.sub(/^operator\s*/, "") # replace assignment operator if name == "=" # drop assignment if the class does not have a copy ctor ("no copy semantics") # (required because otherwise operator= is exposed for adaptor classes) if !has_copy_ctor?(cls) return nil end name = "assign" end rn = (@renamed_methods[:all_classes] || []) + (@renamed_methods[cls] || []) + (@renamed_methods[cls2] || []) rt = rn.find { |d| sig =~ d[0] } if rt name = rt[1] end al = (@aliased_methods[:all_classes] || []) + (@aliased_methods[cls] || []) + (@aliased_methods[cls2] || []) at = al.select { |d| sig =~ d[0] } if !at.empty? name += "|" + at.collect { |a| a[1] }.uniq.join("|") elsif mm && decl && decl.type.func rn = property_reader_name(cls, sig) || property_reader_name(cls2, sig2) wn = property_writer_name(cls, sig) || property_writer_name(cls2, sig2) if rn && (decl.type.func.args || []).size == 0 # property readers become # isX -> isX?|:x # x -> :x # the colon hints that this has to be a getter (for Python) if name =~ /^is([A-Z])(.*)/ name += "?" end if name == rn name = ":" + rn else name += "|:" + rn end elsif wn && (decl.type.func.args || []).size == 1 # property writers become setX -> setX|x= if name == wn name = wn + "=" else name += "|" + wn + "=" end elsif name =~ /^is([A-Z])(.*)/ # implicit alias: isX -> isX? name += "?" end end name end # @brief Returns a string for building identifers from a method id def mid2str(mid) mid.gsub(/\(\)/, "_func_"). gsub(/\[\]/, "_index_"). gsub(/\//, "_slash_"). gsub(/\*/, "_star_"). gsub(/\^/, "_acute_"). gsub(/=/, "_eq_"). gsub(/-/, "_minus_"). gsub(/~/, "_tilde_"). gsub(/\+/, "_plus_"). gsub(//, "_gt_"). gsub(/!/, "_excl_"). gsub(/&/, "_amp_"). gsub(/\|/, "_pipe_"). gsub(/\s+/, "") end def include_enum(cls, sig) @included_enums[cls] ||= [] @included_enums[cls] << sig end def is_enum_included?(cls, sig) dm = (@included_enums[:all_classes] || []) + (@included_enums[cls] || []) dm.find { |d| sig =~ d } != nil end def drop_enum(cls, sig) @dropped_enums[cls] ||= [] @dropped_enums[cls] << sig end def is_enum_dropped?(cls, sig) dm = (@dropped_enums[:all_classes] || []) + (@dropped_enums[cls] || []) dm.find { |d| sig =~ d } != nil end def add_native_impl(cls, code, decl) @native_impl[cls] ||= [] @native_impl[cls] << [ code, decl ] end def native_impl(cls) @native_impl[cls] end def property_writer(cls, sig, name) @property_writers[cls] ||= [] @property_writers[cls] << [ sig, name ] end def property_writer_name(cls, sig) pm = (@property_writers[:all_classes] || []) + (@property_writers[cls] || []) p = pm.find { |d| sig =~ d[0] } p && p[1] end def property_reader(cls, sig, name) @property_readers[cls] ||= [] @property_readers[cls] << [ sig, name ] end def property_reader_name(cls, sig) pm = (@property_readers[:all_classes] || []) + (@property_readers[cls] || []) p = pm.find { |d| sig =~ d[0] } p && p[1] end def event(cls, sig, args) @events[cls] ||= [] @events[cls] << [ sig, args ] end # @brief Returns the event arguments or nil if the method is not an event def event_args(cls, sig) dm = (@events[:all_classes] || []) + (@events[cls] || []) e = dm.find { |d| sig =~ d[0] } e && e[1] end def load(filename) # with Ruby 1.9 # found = ([ File.dirname($0) ] + $:).each do |d| # ap = File.absolute_path(filename, d) # File.readable?(ap) && ap # end # found = found.select { |f| f } # found.is_empty? && raise("Cannot load file #{filename} - file not found or not readable") # File.open(found[0], "r") do |file| # self.instance_eval(file.read, found[0]) # end # with Ruby 1.8 File.open(filename, "r") do |file| self.instance_eval(file.read, filename) end end end class BindingProducer attr_accessor :modn, :root # @brief Read the input file (JSON) # # This method will set up the binding producer for generating # the class declarations def read(input_file) @source_files = nil @classes = nil @ext_decls = [] File.open(input_file, "r") do |file| json = file.read @root = Oj.load(json) puts "Reading done." # replace default visibility by the real one @root.set_visibility # collect the children and create the lookup tables. Also establish # the parent relationships @root.set_parent # move cross-scoped items such as "class A::B { ... };" to the right place @root.inject_scoped # and regenerate the lookup tables plus new parent relationships @root.set_parent @used_enums = {} end end # @brief Gets a list of objects to produce in the root namespace def prod_list(conf) pl = [] @root.ids.each do |cls| # only take plain (non-template) classes which are not dropped if !conf.is_class_dropped?(cls) pl << @root.id2obj(cls) end end return pl end # @brief Collects the used enum declarations def collect_used_enums(conf, decl_obj) if decl_obj.is_a?(CPPStructDeclaration) decl_obj.struct.collect_used_enums(@used_enums, conf) end end # @brief Substitute words from the context of a declaration def rescope_expr(expr, decl_obj) expr.gsub(/(::)?\w+/) do |s| if s[0] == ":" s else res = decl_obj.resolve_qid(CPPQualifiedId::new(false, [ CPPId::new(s, nil) ])) (res && res.parent && res.parent.myself) ? (res.parent.myself + "::" + s) : s end end end # @brief Produce the main declaration file for the given cls inside the given module def produce_cpp(conf, cls) qid = CPPQualifiedId::new(false, [ CPPId::new(cls, nil) ]) decl_obj = @root.resolve_qid(qid) decl_obj || raise("Class not found: #{cls}") produce_cpp_from_decl(conf, decl_obj) end def produce_cpp_from_decl(conf, decl_obj) if !decl_obj.is_a?(CPPStructDeclaration) && !decl_obj.is_a?(CPPNamespace) && !decl_obj.is_a?(CPPEnumDeclaration) return end index = 0 cont = true while cont (cls, clsn) = make_cls_names(decl_obj) @classes ||= [] @classes << clsn if index > 0 ofile_name = "gsiDecl#{clsn}_#{index}.cc" else ofile_name = "gsiDecl#{clsn}.cc" end ofile_path = $gen_dir + "/" + ofile_name File.open(ofile_path, "w") do |ofile| @source_files ||= [] @source_files << ofile_name ofile.puts(<<"END"); /* KLayout Layout Viewer Copyright (C) 2006-2024 Matthias Koefferlein This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file #{ofile_name} * * DO NOT EDIT THIS FILE. * This file has been created automatically */ END if decl_obj.is_a?(CPPStructDeclaration) cont = produce_class(conf, decl_obj, ofile, index) elsif decl_obj.is_a?(CPPNamespace) cont = produce_namespace(conf, decl_obj, ofile, index) elsif decl_obj.is_a?(CPPEnumDeclaration) cont = produce_enum(conf, decl_obj, ofile, index) end puts("#{ofile_name} written.") index += 1 end end end def is_enum_used?(bd) @used_enums[bd.object_id] end def make_child_cls_name(s) s[0].upcase + s[1..-1] end def make_cls_names(decl_obj) cls = decl_obj.myself clsn = make_child_cls_name(decl_obj.myself) o = decl_obj while o.parent && o.parent.myself o = o.parent cls = o.myself + "::" + cls clsn = make_child_cls_name(o.myself) + "_" + clsn end return [ cls, clsn ] end def make_cls_name(cls) return cls.split("::").collect { |s| make_child_cls_name(s) }.join("_") end def produce_class_include(conf, decl_obj, ofile) ( cls, clsn ) = make_cls_names(decl_obj) if !conf.includes(cls) if cls =~ /^(.*?)::/ ofile.puts("#include <#{$1}>") else ofile.puts("#include <#{cls}>") end # derive dependencies where possible if decl_obj.is_a?(CPPStructDeclaration) struct = decl_obj.struct methods = {} struct.collect_all_methods(methods, conf) methods[cls] = struct.collect_ctors needs_adaptor = struct.needs_adaptor(conf) used_classes = {} methods.each do |mn,m| m.each do |bd| vis = bd.visibility is_signal = conf.event_args(cls, bd.sig(cls)) if vis == :public || (vis == :protected && needs_adaptor) || is_signal # don't consider dropped methods conf.target_name(cls, bd, mn) || next bd.type.each_qid do |qid| obj = decl_obj.parent.resolve_qid(qid) if obj.is_a?(CPPStructDeclaration) used_classes[obj.object_id] = obj end end end end end used_classes.values.map { |uc| uc.myself || uc.myself_weak }.select { |uc| !conf.is_class_dropped?(uc) && uc != cls }.sort.each do |uc| ofile.puts("#include <#{uc}>") end end else conf.includes(cls).each do |f| ofile.puts("#include #{f}") end end ofile.puts("#include \"gsiQt.h\"") ofile.puts("#include \"gsi#{modn}Common.h\"") end def produce_enum(conf, decl_obj, ofile, index) ( cls, clsn ) = make_cls_names(decl_obj) produce_class_include(conf, decl_obj, ofile) ofile.puts("#include ") ofile.puts("") ofile.puts("// -----------------------------------------------------------------------") ofile.puts("// enum #{cls}") ofile.puts("") # emit enum wrapper classes (top level, hence container class is nil) produce_enum_wrapper_class(ofile, conf, nil, cls, decl_obj) return false end def produce_namespace(conf, decl_obj, ofile, index) ( cls, clsn ) = make_cls_names(decl_obj) enum_decls_by_name = {} decl_obj.collect_enum_decls(enum_decls_by_name) { true } produce_class_include(conf, decl_obj, ofile) ofile.puts("#include ") ofile.puts("") ofile.puts("// -----------------------------------------------------------------------") ofile.puts("// namespace #{cls}") ofile.puts("") ofile.puts("class #{cls}_Namespace { };") ofile.puts("") if index == 0 ofile.puts("namespace gsi") ofile.puts("{") ofile.puts("gsi::Class<#{cls}_Namespace> decl_#{cls}_Namespace (\"#{modn}\", \"#{clsn}\",") ofile.puts(" gsi::Methods(),") ofile.puts(" \"@qt\\n@brief This class represents the #{cls} namespace\");") ofile.puts("}") ofile.puts("") end # emit enum wrapper classes enums_per_file = 20 en_names = enum_decls_by_name.keys.sort en_names.each_with_index do |en,en_index| if en_index < (index + 1) * enums_per_file && en_index >= index * enums_per_file ed = enum_decls_by_name[en] produce_enum_wrapper_class(ofile, conf, cls, en, ed) end end # produce more files if required return en_names.size > (index + 1) * enums_per_file end def produce_enum_wrapper_class(ofile, conf, cls, en, ed) clsn = cls && make_cls_name(cls) # emit enum wrapper classes ofile.puts("") ofile.puts("// Implementation of the enum wrapper class for #{cls}::#{en}") ofile.puts("namespace qt_gsi") ofile.puts("{") ofile.puts("") if cls ofile.puts("static gsi::Enum<#{cls}::#{en}> decl_#{clsn}_#{en}_Enum (\"#{modn}\", \"#{clsn}_#{en}\",") else ofile.puts("static gsi::Enum<#{en}> decl_#{en}_Enum (\"#{modn}\", \"#{en}\",") end edecl = [] ec = ed.enum.specs.collect { |s| s.name } ec.each_with_index do |ei,i| ei_name = conf.target_name_for_enum_const(cls ? cls : "::", "#{en}::#{ei}", ei) if ! ei_name # enum dropped next end if cls if ed.enum.is_class edecl << " gsi::enum_const (\"#{ei_name}\", #{cls}::#{en}::#{ei}, \"@brief Enum constant #{cls}::#{en}::#{ei}\")" else edecl << " gsi::enum_const (\"#{ei_name}\", #{cls}::#{ei}, \"@brief Enum constant #{cls}::#{ei}\")" end else if ed.enum.is_class edecl << " gsi::enum_const (\"#{ei_name}\", #{en}::#{ei}, \"@brief Enum constant #{en}::#{ei}\")" else edecl << " gsi::enum_const (\"#{ei_name}\", #{ei}, \"@brief Enum constant #{ei}\")" end end end ofile.puts(" " + edecl.join(" +\n ") + ",\n") if cls ofile.puts(" \"@qt\\n@brief This class represents the #{cls}::#{en} enum\");") else ofile.puts(" \"@qt\\n@brief This class represents the #{en} enum\");") end ofile.puts("") if cls ofile.puts("static gsi::QFlagsClass<#{cls}::#{en} > decl_#{clsn}_#{en}_Enums (\"#{modn}\", \"#{clsn}_QFlags_#{en}\",") ofile.puts(" \"@qt\\n@brief This class represents the QFlags<#{cls}::#{en}> flag set\");") ofile.puts("") else ofile.puts("static gsi::QFlagsClass<#{en} > decl_#{en}_Enums (\"#{modn}\", \"QFlags_#{en}\",") ofile.puts(" \"@qt\\n@brief This class represents the QFlags<#{en}> flag set\");") ofile.puts("") end if cls # inject the declarations into the parent namespace or class pdecl_obj = ed.parent pcls = pdecl_obj.myself o = pdecl_obj while o.parent && o.parent.myself o = o.parent pcls = o.myself + "::" + pcls end pname = pcls if pdecl_obj.is_a?(CPPNamespace) pname = pcls + "_Namespace" end if ! ed.enum.is_class ofile.puts("// Inject the declarations into the parent") ofile.puts("static gsi::ClassExt<#{pname}> inject_#{clsn}_#{en}_Enum_in_parent (decl_#{clsn}_#{en}_Enum.defs ());") end ofile.puts("static gsi::ClassExt<#{pname}> decl_#{clsn}_#{en}_Enum_as_child (decl_#{clsn}_#{en}_Enum, \"#{en}\");") ofile.puts("static gsi::ClassExt<#{pname}> decl_#{clsn}_#{en}_Enums_as_child (decl_#{clsn}_#{en}_Enums, \"QFlags_#{en}\");") ofile.puts("") end ofile.puts("}") ofile.puts("") end def produce_keep_self(ofile, alist, obj, owner_args = []) owner_args.each do |a| ofile.puts(" if (#{alist[a]}) {"); ofile.puts(" qt_gsi::qt_keep (#{obj});") ofile.puts(" } else {"); ofile.puts(" qt_gsi::qt_release (#{obj});") ofile.puts(" }"); end end def produce_arg_read(ofile, decl_obj, func, alist, kept_args = []) n_args = func.max_args n_min_args = func.min_args has_heap = false n_args.times do |ia| t = func.args[ia] argname = alist[ia] at = t.anonymous_type ta = at.gsi_decl_arg(decl_obj) nt = t.renamed_type(argname) tn = nt.gsi_decl_arg(decl_obj) if !has_heap ofile.puts(" tl::Heap heap;") has_heap = true end if ia < n_min_args || !t.init ofile.puts(" #{tn} = gsi::arg_reader<#{ta} >() (args, heap);") else init = rescope_expr(t.init.to_s, decl_obj) init_expr = at.access_gsi_arg(decl_obj, init) if init_expr =~ /%HEAP%/ init_expr = init_expr.gsub("%HEAP%", "heap") end ofile.puts(" #{tn} = args ? gsi::arg_reader<#{ta} >() (args, heap) : gsi::arg_maker<#{ta} >() (#{init_expr}, heap);") end if kept_args.index(ia) ofile.puts(" qt_gsi::qt_keep (#{argname});") end end end def produce_arg_init(ofile, decl_obj, func) n_min_args = func.min_args func.max_args.times do |ia| t = func.args[ia] argname = t.name || "arg#{ia + 1}" at = t.anonymous_type ta = at.gsi_decl_arg(decl_obj); if ia < n_min_args || !t.init ofile.puts(" static gsi::ArgSpecBase argspec_#{ia} (\"#{argname}\");") else init = rescope_expr(t.init.to_s, decl_obj) istr = init.gsub(/"/, "\\\"") ofile.puts(" static gsi::ArgSpecBase argspec_#{ia} (\"#{argname}\", true, \"#{istr}\");") end ofile.puts(" decl->add_arg<#{ta} > (argspec_#{ia});") end end def produce_class(conf, decl_obj, ofile, index) if index > 0 raise "Internal error: no splitting of class definitions yet." end struct = decl_obj.struct (cls, clsn) = make_cls_names(decl_obj) cclsn = make_child_cls_name(decl_obj.myself) # produce public subclasses (struct.body_decl || []).each do |bd| if bd.is_a?(CPPStructDeclaration) && bd.visibility == :public && bd.struct.body_decl && bd.myself != "" && !conf.is_class_dropped?(cls, bd.myself) produce_cpp_from_decl(conf, bd) end end base_classes = (struct.base_classes || []).select { |b| conf.imported?(cls, b.class_id.to_s) } base_cls = base_classes[0] && base_classes[0].class_id.to_s base_clsn = base_cls && make_cls_name(base_cls) # as we only support single base classes (a tribute to Ruby), we treat all other base classes as # mixins mixin_base_classes = base_classes[1..] || [] methods_by_name = {} all_methods_by_name = {} enum_decls_by_name = {} bc_methods_by_name = {} base_classes.each do |bc| bc_decl_obj = decl_obj.resolve_qid(bc.class_id) bc_decl_obj && bc_decl_obj.respond_to?(:struct) && bc_decl_obj.struct.collect_all_methods(bc_methods_by_name, conf) end mmap = {} struct.collect_all_methods(all_methods_by_name, conf) struct.collect_methods(methods_by_name) struct.collect_enum_decls(enum_decls_by_name) { |bd| self.is_enum_used?(bd) || conf.is_enum_included?(cls, bd.myself) } # if one method is abstract, omit ctors for example is_abstract = all_methods_by_name.values.find do |m| m.find { |bd| bd.virtual && bd.type.init == "0" } != nil end # gets the operator= if there is one eq_op = (all_methods_by_name["operator="] || [])[0] # collect used enums in order to generate forward converter declarations # ("used" implies argument types and defined enums) used_ed = {} enum_decls_by_name.each do |en,ed| used_ed[ed.object_id] = ed end struct.collect_used_enums(used_ed, conf) used_enums_by_name = {} used_ed.each do |id,ed| id = CPPQualifiedId::new(false, [ CPPId::new(ed.enum.name, nil) ]) id.rescope(ed.parent, ed.global_scope, false) used_enums_by_name[id.to_s] = ed end # needs_adaptor is true if there is any virtual method which can potentially # be reimplemented in script needs_adaptor = struct.needs_adaptor(conf) # is_qobject is true, if the class is derived from QObject is_qobject = struct.is_qobject?(conf) # provide constructors only if we really can create an object # (we can't, if the class is abstract and there is no adaptor) if !is_abstract || needs_adaptor # collect ctors ctors = struct.collect_ctors # create a default ctor if there is no ctor at all and we can have one if ctors.empty? && conf.has_default_ctor?(cls) func = CPPFunc::new(CPPQualifiedId::new(false, [ CPPId::new(decl_obj.myself, nil) ]), [], nil, nil) type = CPPType::new(nil, func, nil) def_ctor = CPPDeclaration::new(type, nil, :public, nil, false, false) def_ctor.parent = decl_obj ctors << def_ctor end else ctors = [] end global_operators = [] # collect global operators with the given class as the first argument @root.decls.each do |bd| if bd.is_a?(CPPDeclaration) && bd.type.func && bd.type.name =~ /^operator/ op_func = bd.type.func if op_func.args.size >= 1 a1 = op_func.args[0].anonymous_type.to_s if a1 =~ /^(const\s+)?#{cls}(\s*&)?$/ global_operators << bd end end end end native_impl = conf.native_impl(cls) has_metaobject = ((struct.body_decl || []).find { |bd| bd.is_a?(CPPDeclaration) && bd.type.name == "metaObject" } != nil) has_metaobject = has_metaobject && !conf.is_dropped?(cls, cls + "::staticMetaObject") mdecl = [] mdecl_ctors = [] produce_class_include(conf, decl_obj, ofile) ofile.puts("#include ") ofile.puts("") ofile.puts("// -----------------------------------------------------------------------") ofile.puts("// #{struct.kind.to_s} #{cls}") if has_metaobject ofile.puts("") ofile.puts("// get static meta object") ofile.puts("") ofile.puts("static void _init_smo (qt_gsi::GenericStaticMethod *decl)") ofile.puts("{") ofile.puts(" decl->set_return ();") ofile.puts("}") ofile.puts("") ofile.puts("static void _call_smo (const qt_gsi::GenericStaticMethod *, gsi::SerialArgs &, gsi::SerialArgs &ret) ") ofile.puts("{") ofile.puts(" ret.write (#{cls}::staticMetaObject);") ofile.puts("}") ofile.puts("") mdecl << "new qt_gsi::GenericStaticMethod (\"staticMetaObject\", \"@brief Obtains the static MetaObject for this class.\", &_init_smo, &_call_smo);" end native_impl && native_impl.each { |n| n[0] && ofile.puts(n[0]) } if ! needs_adaptor # expose ctors here (with virtual functions, the adaptor will expose the ctors) ctors.each do |bd| bd.visibility == :public || next func = bd.type.func hk = bd.hash_str sig = bd.sig(cls) rsig = bd.raw_sig(cls) mn = decl_obj.myself # ctor! mn_name = conf.target_name(cls, bd, mn) if ! mn_name # method dropped next elsif mn_name == mn mn_name = "new" end n_args = func.max_args n_min_args = func.min_args ant = n_args.times.collect { |ia| func.args[ia].anonymous_type } alist = n_args.times.collect { |ia| "arg#{ia + 1}" } qt_alist = n_args.times.collect { |ia| func.args[ia].renamed_type(alist[ia]).access_qt_arg(decl_obj) } ofile.puts("") ofile.puts("// Constructor #{rsig}") ofile.puts("") ofile.puts("") ofile.puts("static void _init_ctor_#{clsn}_#{hk} (qt_gsi::GenericStaticMethod *decl)") ofile.puts("{") produce_arg_init(ofile, decl_obj, func) ofile.puts(" decl->set_return_new<#{cls}> ();") ofile.puts("}") ofile.puts("") ofile.puts("static void _call_ctor_#{clsn}_#{hk} (const qt_gsi::GenericStaticMethod * /*decl*/, gsi::SerialArgs &args, gsi::SerialArgs &ret) ") ofile.puts("{") ofile.puts(" __SUPPRESS_UNUSED_WARNING(args);") produce_arg_read(ofile, decl_obj, func, alist, conf.kept_args(bd)) if conf.owner_args(bd).size > 0 ofile.puts(" #{cls} *obj = new #{cls} (#{qt_alist.join(', ')});") conf.owner_args(bd).each do |a| ofile.puts(" if (#{alist[a]}) {"); ofile.puts(" qt_gsi::qt_keep (obj);") ofile.puts(" }"); end ofile.puts(" ret.write<#{cls} *> (obj);") else ofile.puts(" ret.write<#{cls} *> (new #{cls} (#{qt_alist.join(', ')}));") end ofile.puts("}") ofile.puts("") mdecl_ctors << "new qt_gsi::GenericStaticMethod (\"#{mn_name}\", \"@brief Constructor #{rsig}\\nThis method creates an object of class #{cls}.\", &_init_ctor_#{clsn}_#{hk}, &_call_ctor_#{clsn}_#{hk});" end end # produce public, non-static methods methods_by_name.keys.sort.each do |mid| methods_by_name[mid].each do |bd| if bd.visibility != :public || bd.storage_class == :static next end mn = conf.mid2str(mid) mn_name = conf.target_name(cls, bd, mid, all_methods_by_name, bd) if ! mn_name # method dropped next end func = bd.type.func const = bd.is_const? hk = bd.hash_str sig = bd.sig(cls) rsig = bd.raw_sig(cls) if conf.event_args(cls, sig) && bd.type.return_type.is_void? # don't produce bindings for signals (which are public in Qt5) next end is_reimp = nil if bc_methods_by_name[mid] call_sig = bd.call_sig bc_methods_by_name[mid].each do |bd_base| if bd_base.call_sig == call_sig bd_base.virtual && (is_reimp = bd_base) break end end end rt = bd.type.return_type n_args = func.max_args n_min_args = func.min_args ant = n_args.times.collect { |ia| func.args[ia].anonymous_type } alist = n_args.times.collect { |ia| "arg#{ia + 1}" } qt_alist = n_args.times.collect { |ia| func.args[ia].renamed_type(alist[ia]).access_qt_arg(decl_obj) } ofile.puts("") ofile.puts("// #{rsig}") ofile.puts("") ofile.puts("") ofile.puts("static void _init_f_#{mn}_#{hk} (qt_gsi::GenericMethod *decl)") ofile.puts("{") produce_arg_init(ofile, decl_obj, func) rpf = (conf.returns_new(bd) ? "_new" : "") ofile.puts(" decl->set_return#{rpf}<#{rt.gsi_decl_return(decl_obj)} > ();") ofile.puts("}") ofile.puts("") ofile.puts("static void _call_f_#{mn}_#{hk} (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) ") ofile.puts("{") ofile.puts(" __SUPPRESS_UNUSED_WARNING(args);") produce_arg_read(ofile, decl_obj, func, alist, conf.kept_args(bd)) produce_keep_self(ofile, alist, "(#{cls} *)cls", conf.owner_args(bd)) if !rt.is_void? ofile.puts(" ret.write<#{rt.gsi_decl_return(decl_obj)} > ((#{rt.gsi_decl_return(decl_obj)})" + rt.access_gsi_return(decl_obj, "((#{cls} *)cls)->#{mid} (#{qt_alist.join(', ')})") + ");") else ofile.puts(" __SUPPRESS_UNUSED_WARNING(ret);") ofile.puts(" ((#{cls} *)cls)->#{mid} (#{qt_alist.join(', ')});") end ofile.puts("}") ofile.puts("") mdecl << "new qt_gsi::GenericMethod (\"#{mn_name}\", \"@brief Method #{rsig}\\n" + (is_reimp ? "This is a reimplementation of #{is_reimp.parent.myself}::#{mid}" : "") + "\", #{const.to_s}, &_init_f_#{mn}_#{hk}, &_call_f_#{mn}_#{hk});" end end # handle events if is_qobject all_methods_by_name.keys.sort.each do |mid| all_methods_by_name[mid].each do |bd| if bd.visibility == :private || bd.storage_class == :static || !bd.type.return_type.is_void? next end mn = conf.mid2str(mid) mn_name = conf.target_name(cls, bd, mid, all_methods_by_name, bd) if ! mn_name # method dropped next end if conf.event_args(cls, bd.sig(cls)) # strip QPrivateSignal argument if present bd_short = bd.dup func = bd_short.type.func if func.args.size > 0 && func.args[-1].anonymous_type.to_s =~ /QPrivateSignal/ func.args.pop end sig = bd_short.sig(cls) rsig = bd_short.raw_sig(cls) hk = bd_short.hash_str n_args = func.max_args argnames = n_args.times.collect { |ia| (func.args[ia].name || "arg#{ia + 1}") } ant = n_args.times.collect { |ia| func.args[ia].anonymous_type } ren_args = n_args.times.collect { |ia| func.args[ia].renamed_type(argnames[ia]) } gsi_args = ant.collect { |a| a.gsi_decl_arg(decl_obj) } callargs = ren_args.collect { |a| a.access_gsi_arg(decl_obj) } event_al = gsi_args.join(", ") al = ant.collect { |a| a.to_s }.join(", ") aln = ren_args.collect { |a| a.to_s }.join(", ") rsig_wo_void = rsig.sub(/^void /, "") al_subst = al SignalSubstitutions.each do |t,s| al_subst = al_subst.gsub(t, s) end argspecs = argnames.collect { |a| "gsi::arg(\"#{a}\"), " }.join("") if gsi_args.empty? mdecl << "gsi::qt_signal (\"#{mid}(#{al_subst})\", \"#{mn_name}\", \"@brief Signal declaration for #{rsig_wo_void}\\nYou can bind a procedure to this signal.\");" else mdecl << "gsi::qt_signal<#{event_al} > (\"#{mid}(#{al_subst})\", \"#{mn_name}\", #{argspecs}\"@brief Signal declaration for #{rsig_wo_void}\\nYou can bind a procedure to this signal.\");" end end end end end # produce public, static methods methods_by_name.keys.sort.each do |mid| methods_by_name[mid].each do |bd| if bd.visibility != :public || bd.storage_class != :static next end func = bd.type.func const = bd.is_const? hk = bd.hash_str sig = bd.sig(cls) rsig = bd.raw_sig(cls) mn = conf.mid2str(mid) mn_name = conf.target_name(cls, bd, mid, all_methods_by_name, bd) if ! mn_name # method dropped next end rt = bd.type.return_type n_args = func.max_args n_min_args = func.min_args ant = n_args.times.collect { |ia| func.args[ia].anonymous_type } alist = n_args.times.collect { |ia| "arg#{ia + 1}" } qt_alist = n_args.times.collect { |ia| func.args[ia].renamed_type(alist[ia]).access_qt_arg(decl_obj) } ofile.puts("") ofile.puts("// static #{rsig}") ofile.puts("") ofile.puts("") ofile.puts("static void _init_f_#{mn}_#{hk} (qt_gsi::GenericStaticMethod *decl)") ofile.puts("{") produce_arg_init(ofile, decl_obj, func) rpf = (conf.returns_new(bd) ? "_new" : "") ofile.puts(" decl->set_return#{rpf}<#{rt.gsi_decl_return(decl_obj)} > ();") ofile.puts("}") ofile.puts("") ofile.puts("static void _call_f_#{mn}_#{hk} (const qt_gsi::GenericStaticMethod * /*decl*/, gsi::SerialArgs &args, gsi::SerialArgs &ret) ") ofile.puts("{") ofile.puts(" __SUPPRESS_UNUSED_WARNING(args);") produce_arg_read(ofile, decl_obj, func, alist, conf.kept_args(bd)) if !rt.is_void? ofile.puts(" ret.write<#{rt.gsi_decl_return(decl_obj)} > ((#{rt.gsi_decl_return(decl_obj)})" + rt.access_gsi_return(decl_obj, "#{cls}::#{mid} (#{qt_alist.join(', ')})") + ");") else ofile.puts(" __SUPPRESS_UNUSED_WARNING(ret);") ofile.puts(" #{cls}::#{mid} (#{qt_alist.join(', ')});") end ofile.puts("}") ofile.puts("") mdecl << "new qt_gsi::GenericStaticMethod (\"#{mn_name}\", \"@brief Static method #{rsig}\\nThis method is static and can be called without an instance.\", &_init_f_#{mn}_#{hk}, &_call_f_#{mn}_#{hk});" end end # produce global operators seen_sig = {} global_operators.each do |bd| func = bd.type.func mid = bd.type.name const = bd.is_const? hk = bd.hash_str sig = bd.sig("") rsig = bd.raw_sig("") # operators may be present twice with the same signature # (here: same hash key) hash_sig = mid + "-" + hk seen_sig[hash_sig] && next seen_sig[hash_sig] = true mn = conf.mid2str(mid) mn_name = conf.target_name("", bd, mid) if ! mn_name # operator dropped next end rt = bd.type.return_type # modify first argument (reference, value -> pointer) func = func.dup it = func.args[0].inner if it.is_a?(CPPReference) func.args[0].inner = CPPPointer::new(it.inner) else func.args[0].inner = CPPPointer::new(it) end n_args = func.max_args argnames = n_args.times.collect { |ia| (func.args[ia].name || "arg#{ia + 1}") } argnames[0] = "_self" qt_alist = n_args.times.collect { |ia| func.args[ia].renamed_type(argnames[ia]).access_qt_arg(decl_obj) } rnt = n_args.times.collect { |ia| func.args[ia].renamed_type(argnames[ia]) } args = rnt.collect { |t| t.gsi_decl_arg(decl_obj) }.join(", ") ofile.puts("") ofile.puts("// #{rsig}") ofile.puts("static #{rt.gsi_decl_return(decl_obj)} op_#{clsn}_#{mn}_#{hk}(#{args}) {") if !rt.is_void? ofile.puts(" return " + rt.access_gsi_return(decl_obj, "::#{mid}(*#{qt_alist.join(', ')});")) else ofile.puts(" ::#{mid}(*#{qt_alist.join(', ')});") end ofile.puts("}") argspecs = argnames[1..-1].collect { |a| "gsi::arg (\"#{a}\"), " }.join("") mdecl << "gsi::method_ext(\"#{mn_name}\", &::op_#{clsn}_#{mn}_#{hk}, #{argspecs}\"@brief Operator #{rsig}\\nThis is the mapping of the global operator to the instance method.\");" end mdecl_bcc = [] if base_classes.size > 1 ofile.puts("") base_classes.each do |bc| bc_name = bc.class_id.to_s ofile.puts("// base class cast for #{bc_name}") ofile.puts("") ofile.puts("static void _init_f_#{clsn}_as_#{bc_name} (qt_gsi::GenericMethod *decl)") ofile.puts("{") ofile.puts(" decl->set_return<#{bc_name} *> ();") ofile.puts("}") ofile.puts("") ofile.puts("static void _call_f_#{clsn}_as_#{bc_name} (const qt_gsi::GenericMethod *, void *cls, gsi::SerialArgs &, gsi::SerialArgs &ret) ") ofile.puts("{") ofile.puts(" ret.write<#{bc_name} *> ((#{bc_name} *)(#{cls} *)cls);") ofile.puts("}") ofile.puts("") mdecl_bcc << "new qt_gsi::GenericMethod (\"as#{bc_name}\", \"@brief Delivers the base class interface #{bc_name} of #{cls}\\nClass #{cls} is derived from multiple base classes. This method delivers the #{bc_name} base class aspect.\", false, &_init_f_#{clsn}_as_#{bc_name}, &_call_f_#{clsn}_as_#{bc_name});" ofile.puts("static void _init_f_#{clsn}_as_const_#{bc_name} (qt_gsi::GenericMethod *decl)") ofile.puts("{") ofile.puts(" decl->set_return ();") ofile.puts("}") ofile.puts("") ofile.puts("static void _call_f_#{clsn}_as_const_#{bc_name} (const qt_gsi::GenericMethod *, void *cls, gsi::SerialArgs &, gsi::SerialArgs &ret) ") ofile.puts("{") ofile.puts(" ret.write ((const #{bc_name} *)(const #{cls} *)cls);") ofile.puts("}") ofile.puts("") mdecl_bcc << "new qt_gsi::GenericMethod (\"asConst#{bc_name}\", \"@brief Delivers the base class interface #{bc_name} of #{cls}\\nClass #{cls} is derived from multiple base classes. This method delivers the #{bc_name} base class aspect.\\n\\nUse this version if you have a const reference.\", true, &_init_f_#{clsn}_as_const_#{bc_name}, &_call_f_#{clsn}_as_const_#{bc_name});" end end mdecl = mdecl_ctors + mdecl + mdecl_bcc if ! needs_adaptor ofile.puts("") end ofile.puts("") ofile.puts("namespace gsi") ofile.puts("{") ofile.puts("") ofile.puts("static gsi::Methods methods_#{clsn} () {") ofile.puts(" gsi::Methods methods;") mdecl.each do |s| ofile.puts(" methods += #{s}") end ofile.puts(" return methods;") ofile.puts("}") ofile.puts("") if is_qobject decl_type = "qt_gsi::QtNativeClass<#{cls}>" else decl_type = "gsi::Class<#{cls}>" end if base_cls ofile.puts("gsi::Class<#{base_cls}> &qtdecl_#{base_clsn} ();") ofile.puts("") ofile.puts("#{decl_type} decl_#{clsn} (" + "qtdecl_#{base_clsn} (), \"#{modn}\", \"#{clsn}" + (needs_adaptor ? "_Native" : "") + "\",") else ofile.puts("#{decl_type} decl_#{clsn} (\"#{modn}\", \"#{clsn}" + (needs_adaptor ? "_Native" : "") + "\",") end if native_impl native_impl.each { |n| n[1] && ofile.puts(n[1] + "+") } end ofile.puts(" methods_#{clsn} (),") is_child_class = (decl_obj.parent && decl_obj.parent.myself) if needs_adaptor ofile.puts(" \"@hide\\n@alias #{clsn}\");") else ofile.puts(" \"@qt\\n@brief Binding of #{cls}\");") ofile.puts("") if is_child_class # Produce the child class declaration if applicable pdecl_obj = decl_obj.parent pcls = pdecl_obj.myself o = pdecl_obj while o.parent && o.parent.myself o = o.parent pcls = o.myself + "::" + pcls end ofile.puts("gsi::ClassExt<#{pcls}> decl_#{clsn}_as_child (decl_#{clsn}, \"#{cclsn}\");") end end if !is_child_class # forward decl @ext_decls << "#{struct.kind.to_s} #{cls};\n\n" # only for top-level classes external declarations are produced currently @ext_decls << "namespace gsi { GSI_#{modn.upcase}_PUBLIC gsi::Class<#{cls}> &qtdecl_#{clsn} (); }\n\n" end # Produce the mixin base classes if ! mixin_base_classes.empty? ofile.puts("") ofile.puts("// Additional base classes") ofile.puts("") mixin_base_classes.each do |bc| bc_name = bc.class_id.to_s ofile.puts("gsi::Class<#{bc_name}> &qtdecl_#{bc_name} ();") end ofile.puts("") end mixin_base_classes.each do |bc| bc_name = bc.class_id.to_s ofile.puts("gsi::ClassExt<#{cls}> base_class_#{bc_name}_in_#{clsn} (qtdecl_#{bc_name} ());") end ofile.puts("") ofile.puts("GSI_#{modn.upcase}_PUBLIC gsi::Class<#{cls}> &qtdecl_#{clsn} () { return decl_#{clsn}; }") ofile.puts("") ofile.puts("}") ofile.puts("") if needs_adaptor # need to produce an adaptor native_impl = conf.native_impl(clsn + "_Adaptor") ofile.puts("") ofile.puts("class #{clsn}_Adaptor : public #{cls}, public qt_gsi::QtObjectBase") ofile.puts("{") ofile.puts("public:") native_impl && native_impl.each { |n| n[0] && ofile.puts(n[0]) } ofile.puts("") ofile.puts(" virtual ~#{clsn}_Adaptor();") # expose ctors here ctors.each do |bd| bd.visibility == :public || next func = bd.type.func hk = bd.hash_str sig = bd.sig(cls) rsig = bd.raw_sig(cls) mn = decl_obj.myself # ctor! mn_name = conf.target_name(cls, bd, mn) if ! mn_name # method dropped next elsif mn_name == mn mn_name = "new" end rt = bd.type.return_type i_var = 0 # TODO: provide initializer instead of multiple implementations? (func.min_args .. func.max_args).each do |n_args| argnames = n_args.times.collect { |ia| (func.args[ia].name || "arg#{ia + 1}") } rnt = n_args.times.collect { |ia| func.args[ia].renamed_type(argnames[ia]) } args = rnt.collect { |t| t.to_s }.join(", ") ofile.puts("") ofile.puts(" // [adaptor ctor] #{rsig}") ofile.puts(" #{clsn}_Adaptor(#{args}) : #{cls}(#{argnames.join(', ')})") ofile.puts(" {") ofile.puts(" qt_gsi::QtObjectBase::init (this);") ofile.puts(" }") i_var += 1 end end # expose all protected, non-virtual methods so we can call them from an implementation all_methods_by_name.keys.sort.each do |mid| all_methods_by_name[mid].each do |bd| bd.virtual && next bd.visibility == :protected || next func = bd.type.func hk = bd.hash_str sig = bd.sig(cls) rsig = bd.raw_sig(cls) const = bd.is_const? # exclude events conf.event_args(cls, sig) && next mn = conf.mid2str(mid) mn_name = conf.target_name(cls, bd, mid) if ! mn_name # method dropped next end rt = bd.type.return_type n_args = func.max_args argnames = n_args.times.collect { |ia| (func.args[ia].name || "arg#{ia + 1}") } rnt = n_args.times.collect { |ia| func.args[ia].renamed_type(argnames[ia]) } args = rnt.collect { |t| t.gsi_decl_arg(decl_obj) }.join(", ") argexpr = rnt.collect { |t| t.access_qt_arg(decl_obj) }.join(", ") ofile.puts("") ofile.puts(" // [expose] #{rsig}") ofile.puts(" " + (bd.storage_class == :static ? "static " : "") + "#{rt.gsi_decl_return(decl_obj)} fp_#{clsn}_#{mn}_#{hk} (#{args}) " + (const ? "const " : "") + "{") if rt.is_void? ofile.puts(" #{cls}::#{mid}(#{argexpr});") else ofile.puts(" return " + rt.access_gsi_return(decl_obj, "#{cls}::#{mid}(#{argexpr})") + ";") end ofile.puts(" }") end end callbacks = [] # expose implementation hooks for a virtual methods # first public, then protected for backward compatibility [ :public, :protected ].each do |vis| all_methods_by_name.keys.sort.each do |mid| all_methods_by_name[mid].each do |bd| bd.visibility == vis || next func = bd.type.func hk = bd.hash_str sig = bd.sig(cls) rsig = bd.raw_sig(cls) const = bd.is_const? rt = bd.type.return_type mn = conf.mid2str(mid) mn_name = conf.target_name(cls, bd, mid) if ! mn_name # method dropped next end if is_qobject && conf.event_args(cls, sig) && bd.storage_class != :static && rt.is_void? # if the last argument is a QPrivateSignal we cannot emit is_private = false bd_short = bd.dup func = bd_short.type.func if func.args.size > 0 && func.args[-1].anonymous_type.to_s =~ /QPrivateSignal/ func.args.pop is_private = true end sig = bd_short.sig(cls) rsig = bd_short.raw_sig(cls) # for events produce an emitter function i_var = func.max_args - func.min_args # for backward compatibility n_args = func.max_args argnames = n_args.times.collect { |ia| (func.args[ia].name || "arg#{ia + 1}") }.collect { |a| a == mn ? "_" + a : a } rnt = n_args.times.collect { |ia| func.args[ia].renamed_type(argnames[ia]) } raw_args = rnt.collect { |t| t.to_s }.join(", ") call_args = argnames.join(", ") ofile.puts("") ofile.puts(" // [emitter impl] #{rsig}") ofile.puts(" #{rt.to_s} emitter_#{clsn}_#{mn}_#{hk}(#{raw_args})") ofile.puts(" {") if is_private argnames.each do |a| ofile.puts(" __SUPPRESS_UNUSED_WARNING (#{a});") end ofile.puts(" throw tl::Exception (\"Can't emit private signal '#{sig}'\");") else ofile.puts(" emit #{cls}::#{mid}(#{call_args});") end ofile.puts(" }") elsif bd.virtual # for virtual functions produce a callback and a virtual reimplementation abstract = (bd.virtual && bd.type.init == "0") i_var = func.max_args - func.min_args # for backward compatibility n_args = func.max_args argnames = n_args.times.collect { |ia| (func.args[ia].name || "arg#{ia + 1}") }.collect { |a| a == mn ? "_" + a : a } rnt = n_args.times.collect { |ia| func.args[ia].renamed_type(argnames[ia]) } args = rnt.collect { |t| t.gsi_decl_arg(decl_obj) }.join(", ") argexpr = rnt.collect { |t| t.access_qt_arg(decl_obj) }.join(", ") argexprr = rnt.collect { |t| ", " + t.access_gsi_arg(decl_obj) }.join("") ta = [ "#{clsn}_Adaptor" ] if !rt.is_void? ta << rt.gsi_decl_return(decl_obj) end ta += rnt.collect { |t| t.anonymous_type.gsi_decl_arg(decl_obj) } ta_str = ta.join(", ") if ta_str =~ />$/ ta_str += " " end raw_args = rnt.collect { |t| t.to_s }.join(", ") call_args = argnames.join(", ") ofile.puts("") ofile.puts(" // [adaptor impl] #{rsig}") ofile.puts(" #{rt.gsi_decl_return(decl_obj)} cbs_#{mn}_#{hk}_#{i_var}(#{args})" + (const ? " const" : "")) ofile.puts(" {") if abstract argnames.each do |a| ofile.puts(" __SUPPRESS_UNUSED_WARNING (#{a});") end ofile.puts(" throw qt_gsi::AbstractMethodCalledException(\"#{mn}\");") elsif rt.is_void? ofile.puts(" #{cls}::#{mid}(#{argexpr});") else ofile.puts(" return " + rt.access_gsi_return(decl_obj, "#{cls}::#{mid}(#{argexpr})") + ";") end ofile.puts(" }") ofile.puts("") ofile.puts(" virtual #{rt.to_s} #{mid}(#{raw_args})" + (const ? " const" : "")) ofile.puts(" {") if rt.is_void? ofile.puts(" if (cb_#{mn}_#{hk}_#{i_var}.can_issue()) {"); ofile.puts(" cb_#{mn}_#{hk}_#{i_var}.issue<#{ta_str}>(&#{clsn}_Adaptor::cbs_#{mn}_#{hk}_#{i_var}#{argexprr});"); ofile.puts(" } else {"); if abstract ofile.puts(" throw qt_gsi::AbstractMethodCalledException(\"#{mn}\");") else ofile.puts(" #{cls}::#{mid}(#{call_args});"); end ofile.puts(" }"); else ofile.puts(" if (cb_#{mn}_#{hk}_#{i_var}.can_issue()) {"); ofile.puts(" return " + rt.access_qt_return(decl_obj, "cb_#{mn}_#{hk}_#{i_var}.issue<#{ta_str}>(&#{clsn}_Adaptor::cbs_#{mn}_#{hk}_#{i_var}#{argexprr})") + ";"); ofile.puts(" } else {"); if abstract ofile.puts(" throw qt_gsi::AbstractMethodCalledException(\"#{mn}\");") else ofile.puts(" return #{cls}::#{mid}(#{call_args});"); end ofile.puts(" }"); end ofile.puts(" }") callbacks << "gsi::Callback cb_#{mn}_#{hk}_#{i_var};"; end end end end ofile.puts("") ofile.puts(" " + callbacks.join("\n ") + "\n") ofile.puts("};") ofile.puts("") ofile.puts("#{clsn}_Adaptor::~#{clsn}_Adaptor() { }"); mdecl = [] ctors.each do |bd| bd.visibility == :public || next func = bd.type.func hk = bd.hash_str sig = bd.sig(cls) rsig = bd.raw_sig(cls) mn = decl_obj.myself # ctor! mn_name = conf.target_name(cls, bd, mn) if ! mn_name # method dropped next elsif mn_name == mn mn_name = "new" end n_args = func.max_args n_min_args = func.min_args alist = n_args.times.collect { |ia| "arg#{ia + 1}" } qt_alist = n_args.times.collect { |ia| func.args[ia].renamed_type(alist[ia]).access_qt_arg(decl_obj) } ofile.puts("") ofile.puts("// Constructor #{rsig} (adaptor class)") ofile.puts("") ofile.puts("static void _init_ctor_#{clsn}_Adaptor_#{hk} (qt_gsi::GenericStaticMethod *decl)") ofile.puts("{") produce_arg_init(ofile, decl_obj, func) ofile.puts(" decl->set_return_new<#{clsn}_Adaptor> ();") ofile.puts("}") ofile.puts("") ofile.puts("static void _call_ctor_#{clsn}_Adaptor_#{hk} (const qt_gsi::GenericStaticMethod * /*decl*/, gsi::SerialArgs &args, gsi::SerialArgs &ret) ") ofile.puts("{") ofile.puts(" __SUPPRESS_UNUSED_WARNING(args);") produce_arg_read(ofile, decl_obj, func, alist, conf.kept_args(bd)) if conf.owner_args(bd).size > 0 ofile.puts(" #{clsn}_Adaptor *obj = new #{clsn}_Adaptor (#{qt_alist.join(', ')});") produce_keep_self(ofile, alist, "obj", conf.owner_args(bd)) ofile.puts(" ret.write<#{clsn}_Adaptor *> (obj);") else ofile.puts(" ret.write<#{clsn}_Adaptor *> (new #{clsn}_Adaptor (#{qt_alist.join(', ')}));") end ofile.puts("}") ofile.puts("") mdecl << "new qt_gsi::GenericStaticMethod (\"#{mn_name}\", \"@brief Constructor #{rsig}\\nThis method creates an object of class #{cls}.\", &_init_ctor_#{clsn}_Adaptor_#{hk}, &_call_ctor_#{clsn}_Adaptor_#{hk});" end all_methods_by_name.keys.sort.each do |mid| all_methods_by_name[mid].each do |bd| bd.visibility == :public || bd.visibility == :protected || next func = bd.type.func hk = bd.hash_str sig = bd.sig(cls) rsig = bd.raw_sig(cls) const = bd.is_const? rt = bd.type.return_type mn = conf.mid2str(mid) mn_name = conf.target_name(cls, bd, mid) if ! mn_name # method dropped next end if is_qobject && conf.event_args(cls, sig) && bd.storage_class != :static && rt.is_void? # for events produce the emitter bd_short = bd.dup func = bd_short.type.func if func.args.size > 0 && func.args[-1].anonymous_type.to_s =~ /QPrivateSignal/ func.args.pop end sig = bd_short.sig(cls) rsig = bd_short.raw_sig(cls) n_args = func.max_args n_min_args = func.min_args alist = n_args.times.collect { |ia| "arg#{ia + 1}" } ifc_obj = "GenericMethod" ofile.puts("") ofile.puts("// emitter #{rsig}") ofile.puts("") ofile.puts("static void _init_emitter_#{mn}_#{hk} (qt_gsi::#{ifc_obj} *decl)") ofile.puts("{") produce_arg_init(ofile, decl_obj, func) rpf = (conf.returns_new(bd_short) ? "_new" : "") ofile.puts(" decl->set_return#{rpf}<#{rt.gsi_decl_return(decl_obj)} > ();") ofile.puts("}") ofile.puts("") ofile.puts("static void _call_emitter_#{mn}_#{hk} (const qt_gsi::#{ifc_obj} * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs & /*ret*/) ") ofile.puts("{") ofile.puts(" __SUPPRESS_UNUSED_WARNING(args);") produce_arg_read(ofile, decl_obj, func, alist, conf.kept_args(bd_short)) ofile.puts(" ((#{clsn}_Adaptor *)cls)->emitter_#{clsn}_#{mn}_#{hk} (#{alist.join(', ')});") ofile.puts("}") ofile.puts("") const_flag = "" if bd_short.storage_class != :static const_flag = ", " + const.to_s end mdecl << "new qt_gsi::#{ifc_obj} (\"emit_#{mn_name}\", \"@brief Emitter for signal #{rsig}\\nCall this method to emit this signal.\"#{const_flag}, &_init_emitter_#{mn}_#{hk}, &_call_emitter_#{mn}_#{hk});" elsif !bd.virtual && bd.visibility == :protected # expose all protected, non-virtual methods and the signals so we can call them from an implementation n_args = func.max_args n_min_args = func.min_args alist = n_args.times.collect { |ia| "arg#{ia + 1}" } ifc_obj = bd.storage_class == :static ? "GenericStaticMethod" : "GenericMethod" ofile.puts("") ofile.puts("// exposed #{rsig}") ofile.puts("") ofile.puts("static void _init_fp_#{mn}_#{hk} (qt_gsi::#{ifc_obj} *decl)") ofile.puts("{") produce_arg_init(ofile, decl_obj, func) rpf = (conf.returns_new(bd) ? "_new" : "") ofile.puts(" decl->set_return#{rpf}<#{rt.gsi_decl_return(decl_obj)} > ();") ofile.puts("}") ofile.puts("") if bd.storage_class == :static ofile.puts("static void _call_fp_#{mn}_#{hk} (const qt_gsi::#{ifc_obj} * /*decl*/, gsi::SerialArgs &args, gsi::SerialArgs &ret) ") else ofile.puts("static void _call_fp_#{mn}_#{hk} (const qt_gsi::#{ifc_obj} * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) ") end ofile.puts("{") ofile.puts(" __SUPPRESS_UNUSED_WARNING(args);") produce_arg_read(ofile, decl_obj, func, alist, conf.kept_args(bd)) if bd.storage_class == :static if !rt.is_void? ofile.puts(" ret.write<#{rt.gsi_decl_return(decl_obj)} > ((#{rt.gsi_decl_return(decl_obj)})#{cls}_Adaptor::fp_#{clsn}_#{mn}_#{hk} (#{alist.join(', ')}));") else ofile.puts(" __SUPPRESS_UNUSED_WARNING(ret);") ofile.puts(" #{cls}_Adaptor::fp_#{clsn}_#{mn}_#{hk} (#{alist.join(', ')});") end else if !rt.is_void? ofile.puts(" ret.write<#{rt.gsi_decl_return(decl_obj)} > ((#{rt.gsi_decl_return(decl_obj)})((#{clsn}_Adaptor *)cls)->fp_#{clsn}_#{mn}_#{hk} (#{alist.join(', ')}));") else ofile.puts(" __SUPPRESS_UNUSED_WARNING(ret);") ofile.puts(" ((#{clsn}_Adaptor *)cls)->fp_#{clsn}_#{mn}_#{hk} (#{alist.join(', ')});") end end ofile.puts("}") ofile.puts("") const_flag = "" if bd.storage_class != :static const_flag = ", " + const.to_s end mdecl << "new qt_gsi::#{ifc_obj} (\"*#{mn_name}\", \"@brief Method #{rsig}\\nThis method is protected and can only be called from inside a derived class.\"#{const_flag}, &_init_fp_#{mn}_#{hk}, &_call_fp_#{mn}_#{hk});" elsif bd.virtual # produce call wrappers for the virtual methods pp = (bd.visibility == :protected ? "*" : "") abstract = bd.type.init == "0" i_var = func.max_args - func.min_args # backward compatibility n_args = func.max_args ant = n_args.times.collect { |ia| func.args[ia].anonymous_type } alist = n_args.times.collect { |ia| "arg#{ia + 1}" } ofile.puts("") ofile.puts("// #{rsig}") ofile.puts("") ofile.puts("static void _init_cbs_#{mn}_#{hk}_#{i_var} (qt_gsi::GenericMethod *decl)") ofile.puts("{") ant.each_with_index do |at,ia| ta = at.gsi_decl_arg(decl_obj); argname = func.args[ia].name || "arg#{ia + 1}" ofile.puts(" static gsi::ArgSpecBase argspec_#{ia} (\"#{argname}\");") ofile.puts(" decl->add_arg<#{ta} > (argspec_#{ia});") end ofile.puts(" decl->set_return<#{rt.gsi_decl_return(decl_obj)} > ();") ofile.puts("}") ofile.puts("") ofile.puts("static void _call_cbs_#{mn}_#{hk}_#{i_var} (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) ") ofile.puts("{") ofile.puts(" __SUPPRESS_UNUSED_WARNING(args);") if !ant.empty? ofile.puts(" tl::Heap heap;") end ant.each_with_index do |at,ia| ofile.puts(" #{at.renamed_type(alist[ia]).gsi_decl_arg(decl_obj)} = args.read<#{at.gsi_decl_arg(decl_obj)} > (heap);") end if !rt.is_void? ofile.puts(" ret.write<#{rt.gsi_decl_return(decl_obj)} > ((#{rt.gsi_decl_return(decl_obj)})((#{clsn}_Adaptor *)cls)->cbs_#{mn}_#{hk}_#{i_var} (#{alist.join(', ')}));") else ofile.puts(" __SUPPRESS_UNUSED_WARNING(ret);") ofile.puts(" ((#{clsn}_Adaptor *)cls)->cbs_#{mn}_#{hk}_#{i_var} (#{alist.join(', ')});") end ofile.puts("}") ofile.puts("") ofile.puts("static void _set_callback_cbs_#{mn}_#{hk}_#{i_var} (void *cls, const gsi::Callback &cb)") ofile.puts("{") ofile.puts(" ((#{clsn}_Adaptor *)cls)->cb_#{mn}_#{hk}_#{i_var} = cb;") ofile.puts("}") ofile.puts("") mdecl << "new qt_gsi::GenericMethod (\"#{pp}#{mn_name}\", \"@brief Virtual method #{rsig}\\nThis method can be reimplemented in a derived class.\", #{const.to_s}, &_init_cbs_#{mn}_#{hk}_#{i_var}, &_call_cbs_#{mn}_#{hk}_#{i_var});" mdecl << "new qt_gsi::GenericMethod (\"#{pp}#{mn_name}\", \"@hide\", #{const.to_s}, &_init_cbs_#{mn}_#{hk}_#{i_var}, &_call_cbs_#{mn}_#{hk}_#{i_var}, &_set_callback_cbs_#{mn}_#{hk}_#{i_var});" end end end # produce the main declaration ofile.puts("") ofile.puts("namespace gsi") ofile.puts("{") ofile.puts("") ofile.puts("gsi::Class<#{cls}> &qtdecl_#{clsn} ();") ofile.puts("") ofile.puts("static gsi::Methods methods_#{clsn}_Adaptor () {") ofile.puts(" gsi::Methods methods;") mdecl.each do |s| ofile.puts(" methods += #{s}") end ofile.puts(" return methods;") ofile.puts("}") ofile.puts("") ofile.puts("gsi::Class<#{clsn}_Adaptor> decl_#{clsn}_Adaptor (qtdecl_#{clsn} (), \"#{modn}\", \"#{clsn}\",") if native_impl native_impl.each { |n| n[1] && ofile.puts(n[1] + "+") } end ofile.puts(" methods_#{clsn}_Adaptor (),") ofile.puts(" \"@qt\\n@brief Binding of #{cls}\");") ofile.puts("") if decl_obj.parent && decl_obj.parent.myself # Produce the child class declaration if applicable pdecl_obj = decl_obj.parent pcls = pdecl_obj.myself o = pdecl_obj while o.parent && o.parent.myself o = o.parent pcls = o.myself + "::" + pcls end ofile.puts("gsi::ClassExt<#{pcls}> decl_#{clsn}_as_child (decl_#{clsn}_Adaptor, \"#{cclsn}\");") end ofile.puts("}") ofile.puts("") end # emit enum wrapper classes enum_decls_by_name.keys.sort.each do |en| ed = enum_decls_by_name[en] produce_enum_wrapper_class(ofile, conf, cls, en, ed) end # don't continue return false end def produce_externals externals_name = "gsiQtExternals.h" externals_path = $gen_dir + "/" + externals_name File.open(externals_path, "w") do |extfile| extfile.puts(<<"END"); /* KLayout Layout Viewer Copyright (C) 2006-2024 Matthias Koefferlein This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ END extfile.puts("/*") extfile.puts(" External declarations for for Qt bindings") extfile.puts("") extfile.puts(" DO NOT EDIT THIS FILE. ") extfile.puts(" This file has been created automatically") extfile.puts("*/") extfile.puts("") extfile.puts("#if !defined(HDR_gsi#{modn}Externals)") extfile.puts("#define HDR_gsi#{modn}Externals") extfile.puts("") extfile.puts("#include \"gsiClass.h\"") extfile.puts("#include \"gsi#{modn}Common.h\"") extfile.puts("") @ext_decls.each do |ed| extfile.puts(ed) end extfile.puts("") extfile.puts("#define QT_EXTERNAL_BASE(X) gsi::qtdecl_##X(),") extfile.puts("") extfile.puts("#endif") extfile.puts("") end end def produce_common_header src_name = "gsi#{modn}Common.h" src_path = $gen_dir + "/" + src_name File.open(src_path, "w") do |src| src.puts("/**") src.puts(" * Common header for Qt binding definition library") src.puts(" *") src.puts(" * DO NOT EDIT THIS FILE. ") src.puts(" * This file has been created automatically") src.puts(" */") src.puts("") src.puts("#include \"tlDefs.h\"") src.puts("") src.puts("#if !defined(HDR_gsi#{modn}Common_h)") src.puts("# define HDR_gsi#{modn}Common_h") src.puts("") src.puts("# ifdef MAKE_GSI_#{modn.upcase}_LIBRARY") src.puts("# define GSI_#{modn.upcase}_PUBLIC DEF_INSIDE_PUBLIC") src.puts("# define GSI_#{modn.upcase}_PUBLIC_TEMPLATE DEF_INSIDE_PUBLIC_TEMPLATE") src.puts("# define GSI_#{modn.upcase}_LOCAL DEF_INSIDE_LOCAL") src.puts("# else") src.puts("# define GSI_#{modn.upcase}_PUBLIC DEF_OUTSIDE_PUBLIC") src.puts("# define GSI_#{modn.upcase}_PUBLIC_TEMPLATE DEF_OUTSIDE_PUBLIC_TEMPLATE") src.puts("# define GSI_#{modn.upcase}_LOCAL DEF_OUTSIDE_LOCAL") src.puts("# endif") src.puts("") src.puts("#define FORCE_LINK_GSI_#{modn.upcase} GSI_#{modn.upcase}_PUBLIC int _force_link_gsi#{modn}_f (); int _force_link_gsi#{modn} = _force_link_gsi#{modn}_f ();") src.puts("") src.puts("#endif") puts("#{src_name} written.") end end def produce_main_source src_name = "gsi#{modn}Main.cc" src_path = $gen_dir + "/" + src_name File.open(src_path, "w") do |src| src.puts("/**") src.puts(" * Main source file for Qt binding definition library") src.puts(" *") src.puts(" * DO NOT EDIT THIS FILE. ") src.puts(" * This file has been created automatically") src.puts(" */") src.puts("") src.puts("#include \"gsi#{modn}Common.h\"") src.puts("") src.puts("GSI_#{modn.upcase}_PUBLIC int _force_link_gsi#{modn}_f () { return 0; }") src.puts("") puts("#{src_name} written.") end end def produce_pri makefile_name = modn + ".pri" makefile_path = $gen_dir + "/" + makefile_name File.open(makefile_path, "w") do |makefile| makefile.puts("#") makefile.puts("# Partial QMAKE project file for Qt bindings") makefile.puts("#") makefile.puts("# DO NOT EDIT THIS FILE. ") makefile.puts("# This file has been created automatically") makefile.puts("#") makefile.puts("") makefile.puts("SOURCES += \\") makefile.puts(" gsi#{modn}Main.cc \\") if @source_files makefile.puts(@source_files.collect { |s| " $$PWD/" + s }.join(" \\\n")) end makefile.puts("") makefile.puts("HEADERS += gsi#{modn}Common.h") makefile.puts("") puts("#{makefile_name} written.") end end def produce_class_list File.open("class_list.txt", "w") do |list| (@classes || []).each do |c| list.puts(c) end end end end # --------------------------------------------------------------------- conf = Configurator::new File.open(conf_file, "r") do |file| conf.instance_eval(file.read, conf_file) end bp = BindingProducer::new bp.modn = modn bp.read(input_file) puts("Collecting used enums ..") l = bp.prod_list(conf) l.each_with_index do |decl_obj,i| decl_obj.myself && puts("#{decl_obj.myself}: #{i+1}/#{l.size}") bp.collect_used_enums(conf, decl_obj) end puts("Producing ..") if cls_list cls_list.split(",").each do |cs| bp.produce_cpp(conf, cs) end else bp.prod_list(conf).each do |decl_obj| if decl_obj.myself && !excl_list[decl_obj.myself] bp.produce_cpp_from_decl(conf, decl_obj) end end end puts("Producing class list") bp.produce_class_list puts("Producing main source file ..") bp.produce_main_source puts("Producing common header file ..") bp.produce_common_header puts("Producing .pri file ..") bp.produce_pri puts("Producing external declarations ..") bp.produce_externals