rubygen/cppgen.rb (362 lines of code) (raw):

#!/usr/bin/env ruby # # General purpose C++ code generation. # require 'amqpgen' require 'set' Copyright=<<EOS /* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ /// /// This file was automatically generated from the AMQP specification. /// Do not edit. /// EOS CppKeywords = Set.new(["and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "class", "compl", "const", "const_cast", "continue", "default", "delete", "do", "DomainInfo", "double", "dynamic_cast", "else", "enum", "explicit", "extern", "false", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq", "operator", "or", "or_eq", "private", "protected", "public", "register", "reinterpret_cast", "return", "short", "signed", "sizeof", "static", "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq"]) # Names that need a trailing "_" to avoid clashes. CppMangle = CppKeywords+Set.new(["std::string"]) class String def cppsafe() CppMangle.include?(self) ? self+"_" : self; end def amqp2cpp() path=split(".") name=path.pop return name.typename if path.empty? path.map! { |n| n.nsname } return (path << name.caps.cppsafe).join("::") end def typename() caps.cppsafe; end def nsname() bars.cppsafe; end def constname() shout.cppsafe; end def funcname() lcaps.cppsafe; end def varname() lcaps.cppsafe; end end # preview: Hold information about a C++ type. # # new mapping does not use CppType, # Each amqp type corresponds exactly by dotted name # to a type, domain or struct, which in turns # corresponds by name to a C++ type or typedef. # (see String.amqp2cpp) # class CppType def initialize(name) @name=@param=@ret=name; end attr_reader :name, :param, :ret, :code def retref() @ret="#{name}&"; self; end def retcref() @ret="const #{name}&"; self; end def passcref() @param="const #{name}&"; self; end def code(str) @code=str; self; end def defval(str) @defval=str; self; end def encoded() @code end def ret_by_val() @name; end def encode(value, buffer) @code ? "#{buffer}.put#{@code}(#{value});" : "#{value}.encode(#{buffer});" end def decode(value,buffer) if @code if /&$/===param then "#{buffer}.get#{@code}(#{value});" else "#{value} = #{buffer}.get#{@code}();" end else "#{value}.decode(#{buffer});" end end def default_value() return @defval ||= "#{name}()" end def to_s() name; end; end class AmqpRoot # preview; map 0-10 types to preview code generator types @@typemap = { "bit"=> CppType.new("bool").code("Octet").defval("false"), "boolean"=> CppType.new("bool").code("Octet").defval("false"), "uint8"=>CppType.new("uint8_t").code("Octet").defval("0"), "uint16"=>CppType.new("uint16_t").code("Short").defval("0"), "uint32"=>CppType.new("uint32_t").code("Long").defval("0"), "uint64"=>CppType.new("uint64_t").code("LongLong").defval("0"), "datetime"=>CppType.new("uint64_t").code("LongLong").defval("0"), "str8"=>CppType.new("std::string").passcref.retcref.code("ShortString"), "str16"=>CppType.new("std::string").passcref.retcref.code("MediumString"), "str32"=>CppType.new("std::string").passcref.retcref.code("LongString"), "vbin8"=>CppType.new("std::string").passcref.retcref.code("ShortString"), "vbin16"=>CppType.new("std::string").passcref.retcref.code("MediumString"), "vbin32"=>CppType.new("std::string").passcref.retcref.code("LongString"), "map"=>CppType.new("FieldTable").passcref.retcref, "array"=>CppType.new("Array").passcref.retcref, "sequence-no"=>CppType.new("SequenceNumber").passcref, "sequence-set"=>CppType.new("SequenceSet").passcref.retcref, "struct32"=>CppType.new("std::string").passcref.retcref.code("LongString"), "uuid"=>CppType.new("Uuid").passcref.retcref, "byte-ranges"=>CppType.new("ByteRanges").passcref.retcref } # preview: map amqp types to preview cpp types. def lookup_cpptype(t) t = @@typemap[t] and return t end end class AmqpElement # convert my amqp type_ attribute to a C++ type. def amqp2cpp() return "ArrayDomain<#{array_type(name).amqp2cpp}> " if type_=="array" return type_.amqp2cpp end # Does this object have a type-like child named name? def typechild(name) child = domain(name) if respond_to? :domain child = struct(name) if not child and respond_to? :struct child = type_(name) if not child and respond_to? :type_ child end # dotted name to a type object def dotted_typechild(name) names=name.split('.') context = self while context and names.size > 1 context = context.child_named(names.shift) end return context.typechild(names[0]) if context end # preview mapping - type_ attribute to C++ type def lookup_cpptype(name) if t = root.lookup_cpptype(name) then return t elsif c = containing_class.typechild(name) then return c.cpptype elsif c= root.dotted_typechild(name) then return c.cpptype else raise "Cannot resolve type-name #{name} from #{self}" end end def containing_class() return self if is_a? AmqpClass return parent && parent.containing_class end end class AmqpField def struct?() c=containing_class c.struct(type_) end def cpptype() lookup_cpptype(type_) or raise "no cpptype #{type_} for field #{self}" end def cppname() name.lcaps.cppsafe; end def bit?() type_ == "bit"; end def signature() cpptype.param+" "+cppname; end def fqtypename() unless type_.index(".") c=containing_class return c.domain(type_).fqtypename if c.domain(type_) return c.struct(type_).fqclassname if c.struct(type_) end return amqp2cpp end def paramtype() /^(int|uint|char|boolean|bit)/ === type_ ? fqtypename : "const #{fqtypename}&" end def param_default() "=#{fqtypename}()" end # Default value is normally the C++ default but over-ridden in specific cases def default_value() defval = cpptype.default_value; if name == "accept-mode" and parent.name == "transfer" then defval = "1"; end return defval end end class AmqpMethod def cppname() name.lcaps.cppsafe; end def param_names() fields.map { |f| f.cppname }; end def signature() fields.map { |f| f.signature }; end def classname() parent.name; end def body_name() classname().caps+name.caps+"Body" end def cpp_pack_type() root.lookup_cpptype("uint16") end end module AmqpHasFields def parameters(with_default=nil) fields.map { |f| "#{f.paramtype} #{f.cppname}_#{f.param_default if with_default}" } end def unused_parameters() fields.map { |f| "#{f.paramtype} /*#{f.cppname}_*/"} end def arguments() fields.map { |f| "#{f.cppname}_"} end def values() fields.map { |f| "#{f.cppname}"} end def initializers() fields.map { |f| "#{f.cppname}(#{f.cppname}_)"} end end class AmqpAction def classname() name.typename; end def funcname() parent.name.funcname + name.caps; end def fqclassname() parent.name+"::"+classname; end def full_code() (containing_class.code.hex << 8)+code.hex; end include AmqpHasFields end class AmqpType def cpptype() root.lookup_cpptype(name) end # preview def typename() name.typename; end # new mapping def fixed?() fixed_width; end end class AmqpCommand < AmqpAction def base() "Command"; end end class AmqpControl < AmqpAction def base() "Control"; end end class AmqpClass def cppname() name.caps; end # preview def nsname() name.nsname; end end class AmqpDomain # preview def cpptype() lookup_cpptype(type_) end def cppname() name.caps; end # new mapping def fqtypename() return containing_class.nsname+"::"+name.typename if containing_class name.typename end end class AmqpResult # preview def cpptype() if type_ then lookup_cpptype(type_) else CppType.new(parent.parent.name.caps+parent.name.caps+"Result").passcref end end end class AmqpStruct include AmqpHasFields @@pack_types={ "1"=>"uint8", "2"=>"uint16", "4"=>"uint32"} def cpp_pack_type() # preview root.lookup_cpptype(@@pack_types[pack]) end def cpptype() CppType.new(cppname).passcref.retcref end #def cppname() containing_class.cppname+name.caps; end def cppname() if parent.kind_of? AmqpResult parent.parent.parent.name.caps+parent.parent.name.caps+"Result" else name.caps end end def fqclassname() containing_class.nsname+"::"+name.typename; end def classname() name.typename; end def full_code() (containing_class.code.hex << 8)+code.hex; end end class CppGen < Generator def initialize(outdir, *specs) super(outdir,*specs) # need to sort classes for dependencies @actions=[] # Stack of end-scope actions end # Write a header file. def h_file(path, &block) path = (/\.h$/ === path ? path : path+".h") guard=path.upcase.tr('./-','_') file(path) { gen "#ifndef #{guard}\n" gen "#define #{guard}\n" gen Copyright yield gen "#endif /*!#{guard}*/\n" } end # Write a .cpp file. def cpp_file(path, &block) path = (/\.cpp$/ === path ? path : path+".cpp") file(path) do gen Copyright yield end end def include(header) header+=".h" unless /(\.h|[">])$/===header header="\"#{header}\"" unless /(^<.*>$)|(^".*"$)/===header genl "#include #{header}" end def scope(open="{",close="}", &block) genl open indent &block genl close end def namespace(name, &block) genl names = name.split("::") names.each { |n| genl "namespace #{n} {" } genl "namespace {" if (names.empty?) genl yield genl genl('}'*([names.size, 1].max)+" // namespace "+name) genl end def struct_class(type, name, bases, &block) gen "#{type} #{name}" if (!bases.empty?) genl ":" indent { gen "#{bases.join(",\n")}" } end genl scope("{","};", &block) end def struct(name, *bases, &block) struct_class("struct", name, bases, &block); end def cpp_class(name, *bases, &block) struct_class("class", name, bases, &block); end def cpp_extern_class(scope, name, *bases, &block) struct_class("class "+scope, name, bases, &block); end def typedef(type, name) genl "typedef #{type} #{name};\n"; end def variant(types) "boost::variant<#{types.join(", ")}>"; end def variantl(types) "boost::variant<#{types.join(", \n")}>"; end def blank_variant(types) variant(["boost::blank"]+types); end def tuple(types) "boost::tuple<#{types.join(', ')}>"; end def public() outdent { genl "public:" } end def private() outdent { genl "private:" } end def protected() outdent { genl "protected:" } end # Returns [namespace, classname, filename] def parse_classname(full_cname) names=full_cname.split("::") return names[0..-2].join('::'), names[-1], names.join("/") end def doxygen_comment(&block) genl "/**" prefix(" * ",&block) genl " */" end # Generate code in namespace for each class def each_class_ns() @amqp.classes.each { |c| namespace(c.nsname) { yield c } } end def signature(ret_name, params, trailer="") if params.size <= 1 genl ret_name+"(#{params})"+trailer else scope(ret_name+"(",")"+trailer) { genl params.join(",\n") } end end def function_decl(ret_name, params=[], trailer="") signature(ret_name, params, trailer+";") end def function_defn(ret_name, params=[], trailer="") genl signature(ret_name, params, trailer) scope() { yield } end def ctor_decl(name, params=[]) function_decl(name, params); end def ctor_defn(name, params=[], inits=[]) signature(name, params, inits.empty? ? "" : " :") indent { gen inits.join(",\n") } if not inits.empty? scope() { yield } end def function_call(name, params=[], trailer="") gen name list "(",params, ")" gen trailer end end # Fully-qualified class name class FqClass < Struct.new(:fqname,:namespace,:name,:file) def initialize(fqclass) names=fqclass.split "::" super(fqclass, names[0..-2].join('::'), names[-1], names.join("/")) end end