summaryrefslogtreecommitdiff
path: root/cpp/rubygen
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/rubygen')
-rwxr-xr-xcpp/rubygen/amqpgen.rb79
-rwxr-xr-xcpp/rubygen/cppgen.rb168
-rwxr-xr-xcpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb27
-rwxr-xr-xcpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb35
-rwxr-xr-xcpp/rubygen/framing.0-10/MethodHolder.rb100
-rwxr-xr-xcpp/rubygen/framing.0-10/Operations.rb96
-rwxr-xr-xcpp/rubygen/framing.0-10/OperationsInvoker.rb92
-rwxr-xr-xcpp/rubygen/framing.0-10/Proxy.rb84
-rw-r--r--cpp/rubygen/framing.0-10/Session.rb199
-rwxr-xr-xcpp/rubygen/framing.0-10/all_method_bodies.rb21
-rwxr-xr-xcpp/rubygen/framing.0-10/constants.rb99
-rw-r--r--cpp/rubygen/framing.0-10/frame_body_lists.rb31
-rw-r--r--cpp/rubygen/framing.0-10/structs.rb583
-rwxr-xr-xcpp/rubygen/generate10
14 files changed, 1518 insertions, 106 deletions
diff --git a/cpp/rubygen/amqpgen.rb b/cpp/rubygen/amqpgen.rb
index 76685aa45b..bfa15bb391 100755
--- a/cpp/rubygen/amqpgen.rb
+++ b/cpp/rubygen/amqpgen.rb
@@ -26,7 +26,7 @@ class String
def bars() tr('- .','_'); end
# Convert to ALL_UPPERCASE_FORM
- def shout() bars.upcase!; end
+ def shout() bars.upcase; end
# Convert to lowerCaseCapitalizedForm
def lcaps() gsub( /\W(\w)/ ) { |m| $1.upcase } end
@@ -89,7 +89,6 @@ class Module
end
end
-
# An AmqpElement contains an XML element and provides a convenient
# API to access AMQP data.
#
@@ -109,6 +108,7 @@ class AmqpElement
@xml, @parent=xml, parent
@children=xml.elements.map { |e| wrap e }.compact
@cache_child={}
+ @cache_child_named={}
@cache_children={}
@cache_children[nil]=@children
end
@@ -142,14 +142,15 @@ class AmqpElement
@cache_child[[elname,name]] ||= children(elname).find { |c| c.name==name }
end
+ # Look up any child with name
+ def child_named(name)
+ @cache_child_named[name] ||= @children.find { |c| c.name==name }
+ end
+
# The root <amqp> element.
def root() @root ||=parent ? parent.root : self; end
- # Are we in preview or final 0-10
- # preview - used to make some classes behave differently for preview vs. final
- def final?() root().version == "0-10"; end
-
- def to_s() "#<#{self.class}(#{name})>"; end
+ def to_s() "#<#{self.class}(#{fqname})>"; end
def inspect() to_s; end
# Text of doc child if there is one.
@@ -207,14 +208,6 @@ class AmqpDomain < AmqpElement
amqp_single_child_reader :enum
def uses() type_=="array" ? ArrayTypes[name] : type_; end
-
- def unalias()
- d=self
- while (d.type_ != d.name and root.domain(d.type_))
- d=root.domain(d.type_)
- end
- return d
- end
end
class AmqpException < AmqpElement
@@ -227,8 +220,6 @@ class AmqpField < AmqpElement
super;
root.used_by[type_].push(parent.fqname) if type_ and type_.index('.')
end
-
- def domain() root.domain(xml.attributes["domain"]); end
amqp_single_child_reader :struct # preview
amqp_child_reader :exception
amqp_attr_reader :type, :default, :code, :required
@@ -278,9 +269,6 @@ class AmqpStruct < AmqpElement
amqp_attr_reader :size, :code, :pack
amqp_child_reader :field
- # preview - preview code needs default "short" for pack.
- alias :raw_pack :pack
- def pack() raw_pack or (not parent.final? and "short"); end
def result?() parent.xml.name == "result"; end
def domain?() parent.xml.name == "domain"; end
end
@@ -297,6 +285,24 @@ class AmqpMethod < AmqpElement
def on_server?() on_chassis? "server"; end
end
+# preview: Map command/control to preview method.
+class AmqpFakeMethod < AmqpMethod
+ def initialize(action)
+ super(action.xml, action.parent);
+ @action=action
+ end
+
+ def content() return "1" if @action.is_a? AmqpCommand and @action.segments end
+ def index() @action.code end
+ def code() @action.code end
+ def synchronous() end # FIXME aconway 2008-04-10: ???
+ def on_chassis?(chassis)
+ @action.received_by?(chassis)
+ end
+ def pack() "2" end # Encode pack=2, size=4 struct
+ def size() "4" end
+end
+
class AmqpImplement < AmqpElement
def initialize(xml,amqp) super; end
amqp_attr_reader :handle, :send
@@ -312,6 +318,11 @@ class AmqpAction < AmqpElement
def initialize(xml,amqp) super; end
amqp_child_reader :implement, :field, :response
amqp_attr_reader :code
+ def implement?(role) xml.elements["./implement[@role='#{role}']"] end
+ def received_by?(client_or_server)
+ return (implement?(client_or_server) or implement?("sender") or implement?("receiver"))
+ end
+ def pack() "2" end
def size() "4" end # Encoded as a size 4 Struct
end
@@ -329,26 +340,37 @@ class AmqpClass < AmqpElement
def initialize(xml,amqp) super; end
amqp_attr_reader :index # preview
- amqp_child_reader :method # preview
- amqp_child_reader :struct, :domain, :control, :command, :role
+ amqp_child_reader :struct, :domain, :control, :command, :role, :method
amqp_attr_reader :code
+ def actions() controls+commands; end
+
+ # preview - command/control as methods
+ def methods_()
+ return (controls + commands).map { |a| AmqpFakeMethod.new(a) }
+ end
+
+ def method(name)
+ a = (command(name) or control(name))
+ return AmqpFakeMethod.new(a)
+ end
+
# chassis should be "client" or "server"
def methods_on(chassis) # preview
@methods_on ||= { }
@methods_on[chassis] ||= methods_.select { |m| m.on_chassis? chassis }
end
+ # FIXME aconway 2008-04-11:
def l4?() # preview
!["connection", "session", "execution"].include?(name) && !control?
end
+ # FIXME aconway 2008-04-11:
def control?()
- ["connection010", "session010"].include?(name)
+ ["connection", "session"].include?(name)
end
-
- def actions() controls+commands; end
end
class AmqpType < AmqpElement
@@ -388,13 +410,6 @@ class AmqpRoot < AmqpElement
def version() major + "-" + minor; end
- # preview - only struct child reader remains for new mapping
- def domain_structs() domains.map{ |d| d.struct }.compact; end
- def result_structs()
- methods_.map { |m| m.result and m.result.struct }.compact
- end
- def structs() result_structs+domain_structs; end
-
def methods_() classes.map { |c| c.methods_ }.flatten; end
#preview
diff --git a/cpp/rubygen/cppgen.rb b/cpp/rubygen/cppgen.rb
index c1121e9bfe..c09ed66b29 100755
--- a/cpp/rubygen/cppgen.rb
+++ b/cpp/rubygen/cppgen.rb
@@ -71,9 +71,9 @@ class String
def varname() lcaps.cppsafe; end
end
-# Hold information about a C++ type.
+# preview: Hold information about a C++ type.
#
-# preview - new mapping does not use CppType,
+# 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.
@@ -88,12 +88,8 @@ class CppType
def passcref() @param="const #{name}&"; self; end
def code(str) @code=str; self; end
def defval(str) @defval=str; self; end
- def fq(namespace)
- @param="const #{namespace}::#{name}&"
- @ret="const #{namespace}::#{name}&"
- @defval="#{namespace}::#{name}()"
- self
- end
+ def encoded() @code end
+ def ret_by_val() @name; end
def encode(value, buffer)
@code ? "#{buffer}.put#{@code}(#{value});" : "#{value}.encode(#{buffer});"
@@ -118,19 +114,86 @@ class CppType
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"),
+ "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("string").passcref.retcref.code("ShortString"),
+ "str16"=>CppType.new("string").passcref.retcref.code("MediumString"),
+ "str32"=>CppType.new("string").passcref.retcref.code("LongString"),
+ "vbin8"=>CppType.new("string").passcref.retcref.code("ShortString"),
+ "vbin16"=>CppType.new("string").passcref.retcref.code("MediumString"),
+ "vbin32"=>CppType.new("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("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<#{ArrayTypes[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 #{self}" end
def cppname() name.lcaps.cppsafe; end
- def cpptype() domain.cpptype; end
- def bit?() domain.type_ == "bit"; end
+ def bit?() type_ == "bit"; end
def signature() cpptype.param+" "+cppname; end
+
def fqtypename()
unless type_.index(".")
c=containing_class
@@ -149,26 +212,11 @@ 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()
- #TODO: remove name mangling after preview path is dropped
- if (parent.name.include?("010"))
- return parent.name.delete("010")
- elsif (parent.name == "cluster")
- return parent.name
- else
- return parent.name + "X"
- end
- end
+ def classname() parent.name; end
def body_name()
classname().caps+name.caps+"Body"
end
-
- def cpp_pack_type() # preview
- CppType.new("uint16_t").code("Short").defval("0");
- end
- def pack() # preview
- "short"
- end
+ def cpp_pack_type() root.lookup_cpptype("uint16") end
end
module AmqpHasFields
@@ -192,7 +240,8 @@ class AmqpAction
end
class AmqpType
- def typename() name.typename; end
+ def cpptype() root.lookup_cpptype(name) end # preview
+ def typename() name.typename; end # new mapping
def fixed?() fixed_width; end
end
@@ -210,67 +259,42 @@ class AmqpClass
end
class AmqpDomain
- @@typemap = {
- "bit"=> CppType.new("bool").code("Octet").defval("false"),
- "octet"=>CppType.new("uint8_t").code("Octet").defval("0"),
- "short"=>CppType.new("uint16_t").code("Short").defval("0"),
- "long"=>CppType.new("uint32_t").code("Long").defval("0"),
- "longlong"=>CppType.new("uint64_t").code("LongLong").defval("0"),
- "timestamp"=>CppType.new("uint64_t").code("LongLong").defval("0"),
- "longstr"=>CppType.new("string").passcref.retcref.code("LongString"),
- "shortstr"=>CppType.new("string").passcref.retcref.code("ShortString"),
- "mediumstr"=>CppType.new("string").passcref.retcref.code("MediumString"),
- "table"=>CppType.new("FieldTable").passcref.retcref,
- "array"=>CppType.new("Array").passcref.retcref,
- "content"=>CppType.new("Content").passcref.retcref,
- "rfc1982-long-set"=>CppType.new("SequenceNumberSet").passcref.retcref,
- "sequence-set"=>CppType.new("SequenceSet").passcref.retcref,
- "long-struct"=>CppType.new("string").passcref.retcref.code("LongString"),
- "uuid"=>CppType.new("Uuid").passcref.retcref
- }
-
- def cppname()
- #TODO: remove name mangling after preview path is dropped
- if (name.include?("010"))
- return name.caps.delete("010")
- elsif (name.include?("properties"))
- return "Preview" + name.caps
- else
- return name.caps
- end
- end
+ # 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
-
- def cpptype()
- d=unalias
- @cpptype ||= @@typemap[d.type_] or
- CppType.new(d.cppname).fq("qpid::framing") or
- raise "Invalid type #{self}"
- end
-
- def AmqpDomain.lookup_type(t)
- @@typemap[t]
- end
end
class AmqpResult
+ # preview
def cpptype()
- @cpptype=CppType.new(parent.classname.caps+parent.name.caps+"Result").passcref
+ 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
- AmqpDomain.lookup_type(pack()) or CppType.new("uint16_t");
+ root.lookup_cpptype(@@pack_types[pack])
end
- def cpptype() parent.cpptype; end # preview
- def cppname() cpptype.name; end # preview
+ 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
diff --git a/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb b/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb
new file mode 100755
index 0000000000..f9ef95f5a0
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class MethodBodyConstVisitorGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace="qpid::framing"
+ @classname="MethodBodyConstVisitor"
+ @filename="qpid/framing/MethodBodyConstVisitor"
+ end
+
+ def generate()
+ h_file("#{@filename}") {
+ namespace(@namespace) {
+ @amqp.methods_.each { |m| genl "class #{m.body_name};" }
+ cpp_class("MethodBodyConstVisitor") {
+ genl "public:"
+ genl "virtual ~MethodBodyConstVisitor() {}"
+ @amqp.methods_.each { |m| genl "virtual void visit(const #{m.body_name}&) = 0;" }
+ }}}
+ end
+end
+
+MethodBodyConstVisitorGen.new($outdir, $amqp).generate();
+
diff --git a/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb b/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb
new file mode 100755
index 0000000000..a74b0c06d6
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb
@@ -0,0 +1,35 @@
+#!/usr/bin/env ruby
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class MethodBodyDefaultVisitorGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace, @classname, @filename = parse_classname("qpid::framing::MethodBodyDefaultVisitor")
+ end
+
+ def generate()
+ h_file(@filename) {
+ include "qpid/framing/MethodBodyConstVisitor"
+ namespace(@namespace) {
+ genl "class AMQMethodBody;"
+ cpp_class(@classname, "public MethodBodyConstVisitor") {
+ genl "public:"
+ genl "virtual void defaultVisit(const AMQMethodBody&) = 0;"
+ @amqp.methods_.each { |m|
+ genl "virtual void visit(const #{m.body_name}&);" }
+ }}}
+
+ cpp_file(@filename) {
+ include(@filename)
+ include("all_method_bodies.h")
+ namespace(@namespace) {
+ @amqp.methods_.each { |m|
+ genl "void #{@classname}::visit(const #{m.body_name}& b) { defaultVisit(b); }"
+ }}}
+ end
+end
+
+MethodBodyDefaultVisitorGen.new($outdir, $amqp).generate();
+
diff --git a/cpp/rubygen/framing.0-10/MethodHolder.rb b/cpp/rubygen/framing.0-10/MethodHolder.rb
new file mode 100755
index 0000000000..90a9333916
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/MethodHolder.rb
@@ -0,0 +1,100 @@
+#!/usr/bin/env ruby
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class MethodHolderGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace="qpid::framing"
+ @classname="BodyHolder"
+ @filename="qpid/framing/BodyHolder"
+ end
+
+ def gen_max_size()
+ # Generate program to generate MaxSize.h
+ cpp_file("generate_MaxMethodBodySize_h") {
+ include "qpid/framing/AMQHeaderBody"
+ include "qpid/framing/AMQContentBody"
+ include "qpid/framing/AMQHeartbeatBody"
+ @amqp.methods_.each { |m| include "qpid/framing/#{m.body_name}" }
+ genl
+ include "<algorithm>"
+ include "<fstream>"
+ genl
+ genl "using namespace std;"
+ genl "using namespace qpid::framing;"
+ genl
+ scope("int main(int, char** argv) {") {
+ genl "size_t maxSize=0;"
+ genl "maxSize=max(maxSize, sizeof(AMQHeaderBody));"
+ genl "maxSize=max(maxSize, sizeof(AMQContentBody));"
+ genl "maxSize=max(maxSize, sizeof(AMQHeartbeatBody));"
+ @amqp.methods_.each { |m|
+ genl "maxSize=max(maxSize, sizeof(#{m.body_name}));" }
+ gen <<EOS
+ofstream out("qpid/framing/MaxMethodBodySize.h");
+out << "// GENERATED CODE: generated by " << argv[0] << endl;
+out << "namespace qpid{ namespace framing { " << endl;
+out << "const size_t MAX_METHOD_BODY_SIZE=" << maxSize << ";" << endl;
+out << "}}" << endl;
+EOS
+ }
+ }
+ end
+
+ def gen_construct
+ cpp_file(@filename+"_gen") {
+ include @filename
+ include "qpid/framing/AMQHeaderBody"
+ include "qpid/framing/AMQContentBody"
+ include "qpid/framing/AMQHeartbeatBody"
+ @amqp.methods_.each { |m| include "qpid/framing/#{m.body_name}" }
+ include "qpid/framing/FrameDefaultVisitor.h"
+ include "qpid/Exception.h"
+ genl
+ namespace(@namespace) {
+ scope("void #{@classname}::setMethod(ClassId c, MethodId m) {") {
+ scope("switch (c) {") {
+ @amqp.classes.each { |c|
+ scope("case #{c.code}: switch(m) {") {
+ c.methods_.each { |m|
+ genl "case #{m.code}: blob = in_place<#{m.body_name}>(); break;"
+ }
+ genl "default: throw Exception(QPID_MSG(\"Invalid method id \" << int(m) << \" for class #{c.name} \"));"
+ }
+ genl "break;"
+ }
+ genl "default: throw Exception(QPID_MSG(\"Invalid class id \" << int(c)));"
+ }
+ }
+
+ struct("CopyVisitor", "public FrameDefaultVisitor") {
+ genl "using FrameDefaultVisitor::visit;"
+ genl "using FrameDefaultVisitor::defaultVisit;"
+ genl "BodyHolder& holder;"
+ genl "CopyVisitor(BodyHolder& h) : holder(h) {}"
+ ["Header", "Content", "Heartbeat"].each { |type|
+ genl "void visit(const AMQ#{type}Body& x) { holder=x; }"
+ }
+ @amqp.methods_.each { |m|
+ genl "void visit(const #{m.body_name}& x) { holder=x; }"
+ }
+ genl "void defaultVisit(const AMQBody&) { assert(0); }"
+ }
+ genl
+
+ scope("void BodyHolder::setBody(const AMQBody& b) {") {
+ genl "CopyVisitor cv(*this); b.accept(cv);"
+ }
+ }}
+ end
+
+ def generate
+ gen_max_size
+ gen_construct
+ end
+end
+
+MethodHolderGen.new($outdir, $amqp).generate();
+
diff --git a/cpp/rubygen/framing.0-10/Operations.rb b/cpp/rubygen/framing.0-10/Operations.rb
new file mode 100755
index 0000000000..a22a591f14
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/Operations.rb
@@ -0,0 +1,96 @@
+#!/usr/bin/env ruby
+# Usage: output_directory xml_spec_file [xml_spec_file...]
+#
+$: << '..'
+require 'cppgen'
+require 'fileutils'
+require 'etc'
+require 'pathname'
+
+class OperationsGen < CppGen
+
+ def initialize(chassis, outdir, amqp)
+ super(outdir, amqp)
+ @chassis=chassis
+ @classname="AMQP_#{@chassis.caps}Operations"
+ end
+
+ def handler_method (m)
+ return_type = m.result ? m.result.cpptype.ret_by_val : "void"
+ gen "\nvirtual #{return_type} #{m.cppname}("
+ gen m.signature.join(",\n")
+ gen ") = 0;\n"
+ end
+
+ def handler_classname(c) c.name.caps+"Handler"; end
+
+ def handler_class(c)
+ if (!c.methods_on(@chassis).empty?)
+ handlerclass=handler_classname c
+ gen <<EOS
+// ==================== class #{handlerclass} ====================
+class #{handlerclass} {
+ // Constructors and destructors
+ public:
+ class Invoker; // Declared in #{@chassis.caps}Invoker
+
+ #{handlerclass}(){};
+ virtual ~#{handlerclass}() {}
+ // Protocol methods
+EOS
+ c.methods_on(@chassis).each { |m| handler_method(m) if !m.content() }
+ gen <<EOS
+}; // class #{handlerclass}
+
+
+EOS
+ end
+ end
+
+ def handler_get(c)
+ if (!c.methods_on(@chassis).empty?)
+ handlerclass=handler_classname c
+ gen "virtual #{handlerclass}* get#{handlerclass}() = 0;\n"
+ end
+ end
+
+ def generate()
+ h_file("qpid/framing/#{@classname}.h") {
+ gen <<EOS
+#include <sstream>
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/framing/amqp_structs.h"
+
+namespace qpid {
+namespace framing {
+
+class AMQMethodBody;
+
+class #{@classname} {
+ public:
+ class Invoker; // Declared in #{@chassis.caps}Invoker
+
+ virtual ~#{@classname}() {}
+
+ virtual ProtocolVersion getVersion() const = 0;
+
+ // Inner classes
+EOS
+ indent { @amqp.classes.each { |c| handler_class(c) } }
+ gen <<EOS
+
+ // Method handler get methods
+
+EOS
+ indent { @amqp.classes.each { |c| handler_get(c) } }
+ gen <<EOS
+}; /* class #{@classname} */
+}}
+EOS
+}
+ end
+end
+
+OperationsGen.new("client",ARGV[0], $amqp).generate()
+OperationsGen.new("server",ARGV[0], $amqp).generate()
+
diff --git a/cpp/rubygen/framing.0-10/OperationsInvoker.rb b/cpp/rubygen/framing.0-10/OperationsInvoker.rb
new file mode 100755
index 0000000000..642f98ce8e
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/OperationsInvoker.rb
@@ -0,0 +1,92 @@
+#!/usr/bin/env ruby
+# Usage: output_directory xml_spec_file [xml_spec_file...]
+#
+$: << '..'
+require 'cppgen'
+
+class OperationsInvokerGen < CppGen
+ def initialize(chassis, outdir, amqp)
+ super(outdir, amqp)
+ @chassis=chassis
+ @ops="AMQP_#{@chassis.caps}Operations"
+ @classname="#{@ops}::Invoker"
+ @filename="qpid/framing/#{@chassis.caps}Invoker"
+ end
+
+ def handler(c) "#{@ops}::#{c.cppname}Handler"; end
+ def getter(c) "get#{c.cppname}Handler"; end
+ def invoker(c) "#{handler(c)}::Invoker"; end
+ def visit_methods(c) c.methods_on(@chassis).select { |m| !m.content } end
+
+ def handler_visits_cpp(c)
+ visit_methods(c).each { |m|
+ scope("void #{invoker(c)}::visit(const #{m.body_name}& body) {") {
+ if (m.result)
+ genl "this->encode(body.invoke(target), result.result);"
+ else
+ genl "body.invoke(target);"
+ end
+ genl "result.handled=true;"
+ }
+ }
+ end
+
+ def ops_visits_cpp()
+ @amqp.classes.each { |c|
+ visit_methods(c).each { |m|
+ scope("void #{@classname}::visit(const #{m.body_name}& body) {") {
+ genl "#{handler(c)}::Invoker invoker(*target.#{getter(c)}());"
+ genl "body.accept(invoker);"
+ genl "result=invoker.getResult();"
+ }
+ }
+ }
+ end
+
+ def invoker_h(invoker, target, methods)
+ return if methods.empty?
+ genl
+ cpp_class(invoker, "public qpid::framing::Invoker") {
+ genl "#{target}& target;"
+ public
+ genl("Invoker(#{target}& target_) : target(target_) {}")
+ genl "using MethodBodyDefaultVisitor::visit;"
+ methods.each { |m| genl "void visit(const #{m.body_name}& body);" }
+ }
+ end
+
+ def generate()
+ h_file(@filename) {
+ include "qpid/framing/#{@ops}"
+ include "qpid/framing/Invoker.h"
+ namespace("qpid::framing") {
+ # AMQP_*Operations invoker.
+ methods=@amqp.classes.map { |c| visit_methods(c).to_a }.flatten
+ invoker_h(@classname, @ops, methods)
+
+ # AMQP_*Operations::*Handler invokers.
+ @amqp.classes.each { |c|
+ invoker_h(invoker(c), handler(c), visit_methods(c))
+ }
+ }
+ }
+
+ cpp_file(@filename) {
+ include @filename
+ @amqp.classes.each { |c|
+ visit_methods(c).each { |m|
+ include "qpid/framing/#{m.body_name}"
+ }}
+ namespace("qpid::framing") {
+ ops_visits_cpp
+ @amqp.classes.each { |c|
+ next if visit_methods(c).empty?
+ handler_visits_cpp(c)
+ }
+ }
+ }
+ end
+end
+
+OperationsInvokerGen.new("client",ARGV[0], $amqp).generate()
+OperationsInvokerGen.new("server",ARGV[0], $amqp).generate()
diff --git a/cpp/rubygen/framing.0-10/Proxy.rb b/cpp/rubygen/framing.0-10/Proxy.rb
new file mode 100755
index 0000000000..87d809d4ad
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/Proxy.rb
@@ -0,0 +1,84 @@
+#!/usr/bin/env ruby
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class ProxyGen < CppGen
+
+ def initialize(chassis, outdir, amqp)
+ super(outdir, amqp)
+ @chassis=chassis
+ @classname="AMQP_#{@chassis.caps}Proxy"
+ @filename="qpid/framing/#{@classname}"
+ end
+
+ def proxy_member(c) c.name.lcaps+"Proxy"; end
+
+ def inner_class_decl(c)
+ cname=c.name.caps
+ cpp_class(cname, "Proxy") {
+ gen <<EOS
+public:
+#{cname}(FrameHandler& f) : Proxy(f) {}
+static #{cname}& get(#{@classname}& proxy) { return proxy.get#{cname}(); }
+EOS
+ c.methods_on(@chassis).each { |m|
+ genl "virtual void #{m.cppname}(#{m.signature.join(",\n ")});"
+ genl
+ }}
+ end
+
+ def inner_class_defn(c)
+ cname=c.cppname
+ c.methods_on(@chassis).each { |m|
+ genl "void #{@classname}::#{cname}::#{m.cppname}(#{m.signature.join(", ")})"
+ scope {
+ params=(["getVersion()"]+m.param_names).join(", ")
+ genl "send(#{m.body_name}(#{params}));"
+ }}
+ end
+
+ def generate
+ # .h file
+ h_file(@filename) {
+ include "qpid/framing/Proxy.h"
+ include "qpid/framing/Array.h"
+ include "qpid/framing/amqp_types.h"
+ include "qpid/framing/amqp_structs.h"
+ namespace("qpid::framing") {
+ cpp_class(@classname, "public Proxy") {
+ public
+ genl "#{@classname}(FrameHandler& out);"
+ genl
+ @amqp.classes.each { |c|
+ inner_class_decl(c)
+ genl
+ genl "#{c.cppname}& get#{c.cppname}() { return #{proxy_member(c)}; }"
+ genl
+ }
+ private
+ @amqp.classes.each{ |c| gen c.cppname+" "+proxy_member(c)+";\n" }
+ }}}
+
+ # .cpp file
+ cpp_file(@filename) {
+ include "<sstream>"
+ include "#{@classname}.h"
+ include "qpid/framing/amqp_types_full.h"
+ @amqp.methods_on(@chassis).each {
+ |m| include "qpid/framing/"+m.body_name
+ }
+ genl
+ namespace("qpid::framing") {
+ genl "#{@classname}::#{@classname}(FrameHandler& f) :"
+ gen " Proxy(f)"
+ @amqp.classes.each { |c| gen ",\n "+proxy_member(c)+"(f)" }
+ genl "{}\n"
+ @amqp.classes.each { |c| inner_class_defn(c) }
+ }}
+ end
+end
+
+
+ProxyGen.new("client", $outdir, $amqp).generate;
+ProxyGen.new("server", $outdir, $amqp).generate;
+
diff --git a/cpp/rubygen/framing.0-10/Session.rb b/cpp/rubygen/framing.0-10/Session.rb
new file mode 100644
index 0000000000..a2f06bfcd7
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/Session.rb
@@ -0,0 +1,199 @@
+#!/usr/bin/env ruby
+# Usage: output_directory xml_spec_file [xml_spec_file...]
+#
+$: << '..'
+require 'cppgen'
+
+class CppGen
+ def session_methods
+ excludes = ["connection", "session", "file", "stream"]
+ gen_methods=@amqp.methods_on(@chassis).reject { |m|
+ excludes.include? m.parent.name or m.body_name.include?("010")
+ }
+ end
+
+ def doxygen(m)
+ doxygen_comment {
+ genl m.doc
+ genl
+ m.fields_c.each { |f|
+ genl "@param #{f.cppname}"
+ genl f.doc if f.doc
+ genl
+ }
+ }
+ end
+end
+
+class ContentField # For extra content parameters
+ def cppname() "content" end
+ def signature() "const MethodContent& content" end
+ def sig_default() signature+"="+"DefaultContent(std::string())" end
+ def unpack() "p[arg::content|DefaultContent(std::string())]"; end
+ def doc() "Message content"; end
+end
+
+class AmqpField
+ def unpack() "p[arg::#{cppname}|#{cpptype.default_value}]"; end
+ def sig_default() signature+"="+cpptype.default_value; end
+end
+
+class AmqpMethod
+ def fields_c() content ? fields+[ContentField.new] : fields end
+ def param_names_c() fields_c.map { |f| f.cppname} end
+ def signature_c() fields_c.map { |f| f.signature }; end
+ def sig_c_default() fields_c.map { |f| f.sig_default }; end
+ def argpack_name() "#{parent.cppname}#{name.caps}Parameters"; end
+ def argpack_type()
+ "boost::parameter::parameters<" +
+ fields_c.map { |f| "arg::keyword_tags::"+f.cppname }.join(',') +
+ ">"
+ end
+ def return_type()
+ return "TypedResult<qpid::framing::#{result.cpptype.ret_by_val}>" if (result)
+ return "Response" if (not responses().empty?)
+ return "Completion"
+ end
+ def session_function() "#{parent.name.lcaps}#{name.caps}"; end
+end
+
+class SessionNoKeywordGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @chassis="server"
+ @namespace,@classname,@file=
+ parse_classname "qpid::client::no_keyword::Session_#{@amqp.version.bars}"
+ end
+
+ def generate()
+ h_file(@file) {
+ include "qpid/client/SessionBase.h"
+
+ namespace("qpid::client") {
+ genl "using std::string;"
+ genl "using framing::Content;"
+ genl "using framing::FieldTable;"
+ genl "using framing::MethodContent;"
+ genl "using framing::SequenceNumber;"
+ genl "using framing::SequenceSet;"
+ genl "using framing::Uuid;"
+ #the following are nasty... would be better to dynamically
+ #include such statements based on params required
+ genl "using framing::Xid;"
+ genl
+ namespace("no_keyword") {
+ doxygen_comment {
+ genl "AMQP #{@amqp.version} session API."
+ genl @amqp.class_("session").doc
+ }
+ cpp_class(@classname, "public SessionBase") {
+ public
+ genl "Session_#{@amqp.version.bars}() {}"
+ genl "Session_#{@amqp.version.bars}(shared_ptr<SessionImpl> core) : SessionBase(core) {}"
+ session_methods.each { |m|
+ genl
+ doxygen(m)
+ args=m.sig_c_default.join(", ")
+ genl "#{m.return_type} #{m.session_function}(#{args});"
+ }
+ }}}}
+
+ cpp_file(@file) {
+ include @classname
+ include "qpid/framing/all_method_bodies.h"
+ namespace(@namespace) {
+ genl "using namespace framing;"
+ session_methods.each { |m|
+ genl
+ sig=m.signature_c.join(", ")
+ func="#{@classname}::#{m.session_function}"
+ scope("#{m.return_type} #{func}(#{sig}) {") {
+ args=(["ProtocolVersion()"]+m.param_names).join(", ")
+ body="#{m.body_name}(#{args})"
+ sendargs=body
+ sendargs << ", content" if m.content
+ genl "return #{m.return_type}(impl->send(#{sendargs}), impl);"
+ }}}}
+ end
+end
+
+class SessionGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @chassis="server"
+ session="Session_#{@amqp.version.bars}"
+ @base="no_keyword::#{session}"
+ @fqclass=FqClass.new "qpid::client::#{session}"
+ @classname=@fqclass.name
+ @fqbase=FqClass.new("qpid::client::#{@base}")
+ end
+
+ def gen_keyword_decl(m, prefix)
+ return if m.fields_c.empty? # Inherited function will do.
+ scope("BOOST_PARAMETER_MEMFUN(#{m.return_type}, #{m.session_function}, 0, #{m.fields_c.size}, #{m.argpack_name}) {") {
+ scope("return #{prefix}#{m.session_function}(",");") {
+ gen m.fields_c.map { |f| f.unpack() }.join(",\n")
+ }
+ }
+ genl
+ end
+
+ def generate()
+ keyword_methods=session_methods.reject { |m| m.fields_c.empty? }
+ max_arity = keyword_methods.map{ |m| m.fields_c.size }.max
+
+ h_file(@fqclass.file) {
+ include @fqbase.file
+ genl
+ genl "#define BOOST_PARAMETER_MAX_ARITY #{max_arity}"
+ include "<boost/parameter.hpp>"
+ genl
+ namespace("qpid::client") {
+ # Generate keyword tag declarations.
+ namespace("arg") {
+ keyword_methods.map{ |m| m.param_names_c }.flatten.uniq.each { |k|
+ genl "BOOST_PARAMETER_KEYWORD(keyword_tags, #{k})"
+ }}
+ genl
+ # Doxygen comment.
+ doxygen_comment {
+ genl "AMQP #{@amqp.version} session API with keyword arguments."
+ genl <<EOS
+This class provides the same set of functions as #{@base}, but also
+allows parameters be passed using keywords. The keyword is the
+parameter name in the namespace "arg".
+
+For example given the normal function "foo(int x=0, int y=0, int z=0)"
+you could call it in either of the following ways:
+
+@code
+session.foo(1,2,3); // Normal no keywords
+session.foo(arg::z=3, arg::x=1); // Keywords and a default
+@endcode
+
+The keyword functions are easy to use but their declarations are hard
+to read. You may find it easier to read the documentation for #{@base}
+which provides the same set of functions using normal non-keyword
+declarations.
+
+\\ingroup clientapi
+EOS
+ }
+ # Session class.
+ cpp_class(@classname,"public #{@base}") {
+ private
+ genl "#{@classname}(shared_ptr<SessionImpl> core) : #{ @base}(core) {}"
+ keyword_methods.each { |m| typedef m.argpack_type, m.argpack_name }
+ genl "friend class Connection;"
+ public
+ genl "#{@classname}() {}"
+ keyword_methods.each { |m| gen_keyword_decl(m,@base+"::") }
+ }}}
+ end
+end
+
+SessionNoKeywordGen.new(ARGV[0], $amqp).generate()
+SessionGen.new(ARGV[0], $amqp).generate()
+
diff --git a/cpp/rubygen/framing.0-10/all_method_bodies.rb b/cpp/rubygen/framing.0-10/all_method_bodies.rb
new file mode 100755
index 0000000000..5971d49189
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/all_method_bodies.rb
@@ -0,0 +1,21 @@
+#!/usr/bin/env ruby
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class AllMethodBodiesGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace="qpid::framing"
+ @filename="qpid/framing/all_method_bodies"
+ end
+
+ def generate()
+ h_file(@filename) {
+ @amqp.methods_.each { |m| include "qpid/framing/"+m.body_name }
+ }
+ end
+end
+
+AllMethodBodiesGen.new($outdir, $amqp).generate();
+
diff --git a/cpp/rubygen/framing.0-10/constants.rb b/cpp/rubygen/framing.0-10/constants.rb
new file mode 100755
index 0000000000..35067a733c
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/constants.rb
@@ -0,0 +1,99 @@
+#!/usr/bin/env ruby
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class ConstantsGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace="qpid::framing"
+ @dir="qpid/framing"
+ end
+
+ def constants_h()
+ h_file("#{@dir}/constants") {
+ namespace(@namespace) {
+ scope("enum AmqpConstant {","};") {
+ l=[]
+ l.concat @amqp.constants.map { |c| "#{c.name.shout}=#{c.value}" }
+ @amqp.classes.each { |c|
+ l << "#{c.name.shout}_CLASS_ID=#{c.code}"
+ l.concat c.methods_.map { |m|
+ "#{c.name.shout}_#{m.name.shout}_METHOD_ID=#{m.code}" }
+ }
+ genl l.join(",\n")
+ }
+ namespace("execution") {
+ define_constants_for(@amqp.class_("execution").domain("error-code").enum)
+ }
+ namespace("connection") {
+ define_constants_for(@amqp.class_("connection").domain("close-code").enum)
+ }
+ namespace("session") {
+ define_constants_for(@amqp.class_("session").domain("detach-code").enum)
+ }
+ define_constants_for(@amqp.class_("dtx").domain("xa-status").enum)
+ }
+ }
+ end
+
+ def define_constants_for(enum)
+ scope("enum #{enum.parent.name.caps} {","};") {
+ genl enum.choices.collect { |c| "#{c.name.shout}=#{c.value}" }.join(",\n")
+ }
+ end
+
+ def define_exception(c, base, package)
+ name=c.name.caps+"Exception"
+ genl
+ doxygen_comment { genl c.doc }
+ struct(c.name.caps+"Exception", base) {
+ genl "#{c.name.caps}Exception(const std::string& msg=std::string()) : #{base}(#{c.value}, \"#{c.name}: \"+msg) {}"
+ }
+ end
+
+ def define_exceptions_for(class_name, domain_name, base)
+ enum = @amqp.class_(class_name).domain(domain_name).enum
+ enum.choices.each { |c| define_exception(c, base, class_name) unless c.name == "normal" }
+ end
+
+ def reply_exceptions_h()
+ h_file("#{@dir}/reply_exceptions") {
+ include "qpid/Exception"
+ namespace(@namespace) {
+ define_exceptions_for("execution", "error-code", "SessionException")
+ define_exceptions_for("connection", "close-code", "ConnectionException")
+ define_exceptions_for("session", "detach-code", "ChannelException")
+ genl
+ genl "void throwExecutionException(int code, const std::string& text);"
+ }
+ }
+ end
+
+ def reply_exceptions_cpp()
+ cpp_file("#{@dir}/reply_exceptions") {
+ include "#{@dir}/reply_exceptions"
+ include "<sstream>"
+ namespace("qpid::framing") {
+ scope("void throwExecutionException(int code, const std::string& text) {"){
+ scope("switch (code) {") {
+ enum = @amqp.class_("execution").domain("error-code").enum
+ enum.choices.each { |c|
+ genl "case #{c.value}: throw #{c.name.caps}Exception(text);"
+ }
+ genl "default: break;"
+ }
+ }
+ }
+ }
+ end
+
+ def generate()
+ constants_h
+ reply_exceptions_h
+ reply_exceptions_cpp
+ end
+end
+
+ConstantsGen.new($outdir, $amqp).generate();
+
diff --git a/cpp/rubygen/framing.0-10/frame_body_lists.rb b/cpp/rubygen/framing.0-10/frame_body_lists.rb
new file mode 100644
index 0000000000..b20e4550f3
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/frame_body_lists.rb
@@ -0,0 +1,31 @@
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class FrameBodyListsGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp);
+ end
+
+ def generate
+ h_file("qpid/framing/frame_body_lists.h") {
+ gen <<EOS
+/**@file
+ * Macro lists of frame body classes, used to generate Visitors
+ */
+EOS
+ gen "#define METHOD_BODIES() "
+ @amqp.methods_.each { |m| gen "\\\n (#{m.body_name}) " }
+ gen <<EOS
+
+
+#define OTHER_BODIES() (AMQContentBody)(AMQHeaderBody)(AMQHeartbeatBody))
+
+EOS
+ }
+ end
+end
+
+FrameBodyListsGen.new(ARGV[0], $amqp).generate;
+
+
diff --git a/cpp/rubygen/framing.0-10/structs.rb b/cpp/rubygen/framing.0-10/structs.rb
new file mode 100644
index 0000000000..35e7717122
--- /dev/null
+++ b/cpp/rubygen/framing.0-10/structs.rb
@@ -0,0 +1,583 @@
+#!/usr/bin/env ruby
+# Usage: output_directory xml_spec_file [xml_spec_file...]
+#
+$: << '..'
+require 'cppgen'
+
+class StructGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ end
+
+ SizeMap={
+ "Octet"=>1,
+ "Short"=>2,
+ "Long"=>4,
+ "LongLong"=>8,
+ "int8"=>1,
+ "int16"=>2,
+ "int32"=>4,
+ "int64"=>8,
+ "uint8"=>1,
+ "uint16"=>2,
+ "uint32"=>4,
+ "uint64"=>8,
+ "timestamp"=>8
+ }
+
+ SizeType={
+ 1=>"Octet",
+ 2=>"Short",
+ 4=>"Long",
+ 8=>"LongLong"
+ }
+
+ ValueTypes=["uint8_t", "uint16_t", "uint32_t", "uint64_t"]
+
+ def is_packed(s) s.pack and s.pack != "0" end
+
+ def execution_header?(s)
+ s.is_a? AmqpMethod and not s.parent.control?
+ # s.kind_of? AmqpMethod and s.parent.name.include?("010") and not s.parent.control?
+ end
+
+ def has_bitfields_only(s)
+ s.fields.select {|f| f.type_ != "bit"}.empty?
+ end
+
+ def default_initialisation(s)
+ params = s.fields.select {|f| ValueTypes.include?(f.cpptype.name) || (!is_packed(s) && f.type_ == "bit")}
+ strings = params.collect {|f| "#{f.cppname}(0)"}
+ strings << "flags(0)" if (is_packed(s))
+ if strings.empty?
+ return ""
+ else
+ return " : " + strings.join(", ")
+ end
+ end
+
+ def printable_form(f)
+ if (f.cpptype.name == "uint8_t")
+ return "(int) " + f.cppname
+ elsif (f.type_ == "bit")
+ return "get#{f.name.caps}()"
+ else
+ return f.cppname
+ end
+ end
+
+ def flag_mask(s, i)
+ pos = s.pack.to_i*8 - 8 - (i/8)*8 + (i % 8)
+ return "(1 << #{pos})"
+ end
+
+ def encode_packed_struct(s)
+ genl s.cpp_pack_type.encode('flags', 'buffer')
+ process_packed_fields(s) { |f, i| encode_packed_field(s, f, i) unless f.type_ == "bit" }
+ end
+
+ def decode_packed_struct(s)
+ genl "#{s.cpp_pack_type.decode('flags', 'buffer')}"
+ process_packed_fields(s) { |f, i| decode_packed_field(s, f, i) unless f.type_ == "bit" }
+ end
+
+ def size_packed_struct(s)
+ genl "total += #{s.pack};"
+ process_packed_fields(s) { |f, i| size_packed_field(s, f, i) unless f.type_ == "bit" }
+ end
+
+ def print_packed_struct(s)
+ process_packed_fields(s) { |f, i| print_packed_field(s, f, i) }
+ end
+
+ def encode_packed_field(s, f, i)
+ genl "if (flags & #{flag_mask(s, i)})"
+ indent { genl f.cpptype.encode(f.cppname,"buffer") }
+ end
+
+ def decode_packed_field(s, f, i)
+ genl "if (flags & #{flag_mask(s, i)})"
+ indent { genl f.cpptype.decode(f.cppname,"buffer") }
+ end
+
+ def size_packed_field(s, f, i)
+ genl "if (flags & #{flag_mask(s, i)})"
+ indent { generate_size(f, []) }
+ end
+
+ def print_packed_field(s, f, i)
+ genl "if (flags & #{flag_mask(s, i)})"
+ indent {
+ genl "out << \"#{f.name}=\" << #{printable_form(f)} << \"; \";"
+ }
+ end
+
+ def generate_encode(f, combined)
+ if (f.type_ == "bit")
+ genl "uint8_t #{f.cppname}_bits = #{f.cppname};"
+ count = 0
+ combined.each { |c| genl "#{f.cppname}_bits |= #{c.cppname} << #{count += 1};" }
+ genl "buffer.putOctet(#{f.cppname}_bits);"
+ else
+ genl f.cpptype.encode(f.cppname,"buffer")
+ end
+ end
+
+ def generate_decode(f, combined)
+ if (f.type_ == "bit")
+ genl "uint8_t #{f.cppname}_bits = buffer.getOctet();"
+ genl "#{f.cppname} = 1 & #{f.cppname}_bits;"
+ count = 0
+ combined.each { |c| genl "#{c.cppname} = (1 << #{count += 1}) & #{f.cppname}_bits;" }
+ else
+ genl f.cpptype.decode(f.cppname,"buffer")
+ end
+ end
+
+ def generate_size(f, combined)
+ if (f.type_ == "bit")
+ names = ([f] + combined).collect {|g| g.cppname}
+ genl "total += 1;//#{names.join(", ")}"
+ else
+ size = SizeMap[f.cpptype.encoded]
+ if (size)
+ genl "total += #{size};//#{f.cppname}"
+ elsif (f.cpptype.name == "SequenceNumberSet")
+ genl "total += #{f.cppname}.encodedSize();"
+ else
+ encoded = f.cpptype.encoded
+ gen "total += ("
+ gen "4 + " if encoded == "LongString"
+ gen "2 + " if encoded == "MediumString"
+ gen "1 + " if encoded == "ShortString"
+ genl "#{f.cppname}.size());"
+ end
+ end
+ end
+
+ def process_packed_fields(s)
+ s.fields.each { |f| yield f, s.fields.index(f) }
+ end
+
+ def process_fields(s)
+ last = nil
+ count = 0
+ bits = []
+ s.fields.each {
+ |f| if (last and last.bit? and f.bit? and count < 7)
+ count += 1
+ bits << f
+ else
+ if (last and last.bit?)
+ yield last, bits
+ count = 0
+ bits = []
+ end
+ if (not f.bit?)
+ yield f
+ end
+ last = f
+ end
+ }
+ if (last and last.bit?)
+ yield last, bits
+ end
+ end
+
+ def all_fields_via_accessors(s)
+ s.fields.collect { |f| "get#{f.name.caps}()" }.join(", ")
+ end
+
+ def methodbody_extra_defs(s)
+ if (s.parent.control?)
+ genl "virtual uint8_t type() const { return 0;/*control segment*/ }"
+ end
+
+
+ gen <<EOS
+ typedef #{s.result ? s.result.cpptype.name : 'void'} ResultType;
+
+ template <class T> ResultType invoke(T& invocable) const {
+ return invocable.#{s.cppname}(#{all_fields_via_accessors(s)});
+ }
+
+ using AMQMethodBody::accept;
+ void accept(MethodBodyConstVisitor& v) const { v.visit(*this); }
+
+ ClassId amqpClassId() const { return CLASS_ID; }
+ MethodId amqpMethodId() const { return METHOD_ID; }
+ bool isContentBearing() const { return #{s.content ? "true" : "false" }; }
+ bool resultExpected() const { return #{s.result ? "true" : "false"}; }
+ bool responseExpected() const { return #{s.responses().empty? ? "false" : "true"}; }
+EOS
+ end
+
+ def define_constructor(name, s)
+ if (s.fields.size > 0)
+ genl "#{name}("
+ if (s.kind_of? AmqpMethod)
+ indent {gen "ProtocolVersion, "}
+ end
+ indent { gen s.fields.collect { |f| "#{f.cpptype.param} _#{f.cppname}" }.join(",\n") }
+ genl ") : "
+ if (is_packed(s))
+ initialisers = s.fields.select { |f| f.type_ != "bit"}.collect { |f| "#{f.cppname}(_#{f.cppname})"}
+
+ initialisers << "flags(0)"
+ indent { gen initialisers.join(",\n") }
+ genl "{"
+ indent {
+ process_packed_fields(s) { |f, i| genl "set#{f.name.caps}(_#{f.cppname});" if f.type_ == "bit"}
+ process_packed_fields(s) { |f, i| genl "flags |= #{flag_mask(s, i)};" unless f.type_ == "bit"}
+ }
+ genl "}"
+ else
+ indent { gen s.fields.collect { |f| " #{f.cppname}(_#{f.cppname})" }.join(",\n") }
+ genl "{}"
+ end
+ end
+ #default constructors:
+ if (s.kind_of? AmqpMethod)
+ genl "#{name}(ProtocolVersion=ProtocolVersion()) #{default_initialisation(s)} {}"
+ end
+ if (s.kind_of? AmqpStruct)
+ genl "#{name}() #{default_initialisation(s)} {}"
+ end
+ end
+
+ def define_packed_field_accessors(s, f, i)
+ if (s.kind_of? AmqpMethod)
+ define_packed_field_accessors_for_method(s, f, i)
+ else
+ define_packed_field_accessors_for_struct(s, f, i)
+ end
+ end
+
+ def define_packed_field_accessors_for_struct(s, f, i)
+ if (f.type_ == "bit")
+ genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {"
+ indent {
+ genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};"
+ genl "else flags &= ~#{flag_mask(s, i)};"
+ }
+ genl "}"
+ genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }"
+ else
+ genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {"
+ indent {
+ genl "#{f.cppname} = _#{f.cppname};"
+ genl "flags |= #{flag_mask(s, i)};"
+ }
+ genl "}"
+ genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return #{f.cppname}; }"
+ if (f.cpptype.name == "FieldTable")
+ genl "#{f.cpptype.name}& #{s.cppname}::get#{f.name.caps}() {"
+ indent {
+ genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set'
+ genl "return #{f.cppname};"
+ }
+ genl "}"
+ end
+ genl "bool #{s.cppname}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }"
+ genl "void #{s.cppname}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }"
+ end
+ genl ""
+ end
+
+ def define_packed_field_accessors_for_method(s, f, i)
+ if (f.type_ == "bit")
+ genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {"
+ indent {
+ genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};"
+ genl "else flags &= ~#{flag_mask(s, i)};"
+ }
+ genl "}"
+ genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }"
+ else
+ genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {"
+ indent {
+ genl "#{f.cppname} = _#{f.cppname};"
+ genl "flags |= #{flag_mask(s, i)};"
+ }
+ genl "}"
+ genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return #{f.cppname}; }"
+ if (f.cpptype.name == "FieldTable")
+ genl "#{f.cpptype.name}& #{s.body_name}::get#{f.name.caps}() {"
+ indent {
+ genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set'
+ genl "return #{f.cppname};"
+ }
+ genl "}"
+ end
+ genl "bool #{s.body_name}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }"
+ genl "void #{s.body_name}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }"
+ end
+ genl ""
+ end
+
+ def define_packed_accessors(s)
+ process_packed_fields(s) { |f, i| define_packed_field_accessors(s, f, i) }
+ end
+
+ def declare_packed_accessors(f)
+ genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname});";
+ genl "#{f.cpptype.ret} get#{f.name.caps}() const;"
+ if (f.cpptype.name == "FieldTable")
+ genl "#{f.cpptype.name}& get#{f.name.caps}();"
+ end
+ if (f.type_ != "bit")
+ #extra 'accessors' for packed fields:
+ genl "bool has#{f.name.caps}() const;"
+ genl "void clear#{f.name.caps}Flag();"
+ end
+ end
+
+ def define_accessors(f)
+ genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) { #{f.cppname} = _#{f.cppname}; }"
+ genl "#{f.cpptype.ret} get#{f.name.caps}() const { return #{f.cppname}; }"
+ if (f.cpptype.name == "FieldTable")
+ genl "#{f.cpptype.name}& get#{f.name.caps}() { return #{f.cppname}; }"
+ end
+ end
+
+ def define_struct(s)
+ classname = s.cppname
+ inheritance = ""
+ if (s.kind_of? AmqpMethod)
+ classname = s.body_name
+ if (execution_header?(s))
+ inheritance = ": public ModelMethod"
+ else
+ inheritance = ": public AMQMethodBody"
+ end
+ end
+
+ h_file("qpid/framing/#{classname}.h") {
+ if (s.kind_of? AmqpMethod)
+ gen <<EOS
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/AMQP_ServerOperations.h"
+#include "qpid/framing/MethodBodyConstVisitor.h"
+EOS
+ end
+ include "qpid/framing/ModelMethod.h" if (execution_header?(s))
+
+ s.fields.each { |f| include "qpid/framing/#{f.cpptype.name}" if f.struct?}
+
+ gen <<EOS
+
+#include <ostream>
+#include "qpid/framing/amqp_types_full.h"
+
+namespace qpid {
+namespace framing {
+
+class #{classname} #{inheritance} {
+EOS
+ if (is_packed(s))
+ indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" unless f.type_ == "bit"} }
+ indent {
+ genl "#{s.cpp_pack_type.name} flags;"
+ }
+ else
+ indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" } }
+ end
+ genl "public:"
+ if (s.kind_of? AmqpMethod)
+ indent { genl "static const ClassId CLASS_ID = #{s.parent.code};" }
+ indent { genl "static const MethodId METHOD_ID = #{s.code};" }
+ end
+
+ if (s.kind_of? AmqpStruct)
+ if (s.code)
+ indent { genl "static const uint16_t TYPE = #{s.full_code};" }
+ end
+ end
+
+ indent {
+ define_constructor(classname, s)
+ genl ""
+ if (is_packed(s))
+ s.fields.each { |f| declare_packed_accessors(f) }
+ else
+ s.fields.each { |f| define_accessors(f) }
+ end
+ }
+ if (s.kind_of? AmqpMethod)
+ methodbody_extra_defs(s)
+ end
+ if (s.kind_of? AmqpStruct)
+ indent {genl "friend std::ostream& operator<<(std::ostream&, const #{classname}&);" }
+ end
+
+ gen <<EOS
+ void encode(Buffer&) const;
+ void decode(Buffer&, uint32_t=0);
+ void encodeStructBody(Buffer&) const;
+ void decodeStructBody(Buffer&, uint32_t=0);
+ uint32_t size() const;
+ uint32_t bodySize() const;
+ void print(std::ostream& out) const;
+}; /* class #{classname} */
+
+}}
+EOS
+ }
+ cpp_file("qpid/framing/#{classname}.cpp") {
+ if (is_packed(s) || s.fields.size > 0 || execution_header?(s))
+ buffer = "buffer"
+ else
+ buffer = "/*buffer*/"
+ end
+ gen <<EOS
+#include "#{classname}.h"
+
+#include "reply_exceptions.h"
+
+using namespace qpid::framing;
+
+EOS
+
+ if (is_packed(s))
+ define_packed_accessors(s)
+ end
+ gen <<EOS
+void #{classname}::encodeStructBody(Buffer& #{buffer}) const
+{
+EOS
+ if (execution_header?(s))
+ genl "encodeHeader(buffer);"
+ end
+
+ if (is_packed(s))
+ indent {encode_packed_struct(s)}
+ else
+ indent { process_fields(s) { |f, combined| generate_encode(f, combined) } }
+ end
+ gen <<EOS
+}
+
+void #{classname}::encode(Buffer& buffer) const
+{
+EOS
+ indent {
+ if (s.kind_of? AmqpStruct)
+ if (s.code)
+ genl "buffer.put#{SizeType[s.size.to_i]}(bodySize() + 2/*typecode*/);" if s.size and s.size.to_i != 0
+ genl "buffer.putShort(TYPE);"
+ else
+ genl "buffer.put#{SizeType[s.size.to_i]}(bodySize());" if s.size and s.size.to_i != 0
+ end
+ end
+ genl "encodeStructBody(buffer);"
+ }
+ gen <<EOS
+}
+
+void #{classname}::decodeStructBody(Buffer& #{buffer}, uint32_t /*size*/)
+{
+EOS
+ if (execution_header?(s))
+ genl "decodeHeader(buffer);"
+ end
+
+ if (is_packed(s))
+ indent {decode_packed_struct(s)}
+ else
+ indent { process_fields(s) { |f, combined| generate_decode(f, combined) } }
+ end
+ gen <<EOS
+}
+
+void #{classname}::decode(Buffer& buffer, uint32_t /*size*/)
+{
+EOS
+ indent {
+ if (s.kind_of? AmqpStruct)
+ genl "buffer.get#{SizeType[s.size.to_i]}();" if s.size and s.size.to_i != 0
+ genl "if (TYPE != buffer.getShort()) throw FramingErrorException(\"Bad type code for struct\");" if s.code
+ end
+ genl "decodeStructBody(buffer);"
+ }
+ gen <<EOS
+}
+
+uint32_t #{classname}::bodySize() const
+{
+ uint32_t total = 0;
+EOS
+ if (execution_header?(s))
+ genl "total += headerSize();"
+ end
+
+ if (is_packed(s))
+ indent {size_packed_struct(s)}
+ else
+ indent { process_fields(s) { |f, combined| generate_size(f, combined) } }
+ end
+ gen <<EOS
+ return total;
+}
+
+uint32_t #{classname}::size() const
+{
+ uint32_t total = bodySize();
+EOS
+ if (s.kind_of? AmqpStruct)
+ genl "total += #{s.size}/*size field*/;" if s.size
+ genl "total += 2/*typecode*/;" if s.code
+ end
+ gen <<EOS
+ return total;
+}
+
+void #{classname}::print(std::ostream& out) const
+{
+ out << "{#{classname}: ";
+EOS
+ if (is_packed(s))
+ indent {print_packed_struct(s)}
+ else
+ copy = Array.new(s.fields)
+ f = copy.shift
+
+ indent {
+ genl "out << \"#{f.name}=\" << #{printable_form(f)};" if f
+ copy.each { |f| genl "out << \"; #{f.name}=\" << #{printable_form(f)};" }
+ }
+ end
+ gen <<EOS
+ out << "}";
+}
+EOS
+
+ if (s.kind_of? AmqpStruct)
+ gen <<EOS
+namespace qpid{
+namespace framing{
+
+ std::ostream& operator<<(std::ostream& out, const #{classname}& s)
+ {
+ s.print(out);
+ return out;
+ }
+
+}
+}
+EOS
+ end
+}
+ end
+
+ def generate()
+ structs = @amqp.collect_all(AmqpStruct).select { |s| not ["command-fragment"].include?(s.name) }
+ structs.each { |s| define_struct(s) }
+ @amqp.methods_.each { |m| define_struct(m) }
+ #generate a single include file containing the list of structs for convenience
+ h_file("qpid/framing/amqp_structs.h") { structs.each { |s| genl "#include \"#{s.cppname}.h\"" } }
+ end
+end
+
+StructGen.new(ARGV[0], $amqp).generate()
+
diff --git a/cpp/rubygen/generate b/cpp/rubygen/generate
index 85fbefdea1..c025c946c7 100755
--- a/cpp/rubygen/generate
+++ b/cpp/rubygen/generate
@@ -33,17 +33,23 @@ def parse_specs(files)
return specs
end
+gendir=File.dirname(__FILE__)
+
# Run selected templates
if ARGV.any? { |arg| arg=="all" }
- templates=Dir["#{File.dirname __FILE__}/*/*.rb"]
+ templates=Dir["#{gendir}/*/*.rb"]
else
templates=ARGV.grep(/\.rb$/)
+ ARGV.each { |arg|
+ d=File.join gendir,arg
+ templates += Dir["#{d}/*.rb"] if File.directory? d
+ }
end
$outdir=ARGV[0]
$models=parse_specs(ARGV.grep(/\.xml$/))
templates.each { |t|
- ver=Pathname.new(t).dirname.basename.to_s
+ ver=Pathname.new(t).dirname.basename.to_s.split('.')[-1]
$amqp=$models[ver]
if $amqp
load t