| #!/usr/bin/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 |
| |