summaryrefslogtreecommitdiff
path: root/cpp/rubygen
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/rubygen')
-rwxr-xr-xcpp/rubygen/amqpgen.rb65
-rwxr-xr-xcpp/rubygen/cppgen.rb31
-rwxr-xr-xcpp/rubygen/generate12
-rwxr-xr-xcpp/rubygen/samples/runme18
-rw-r--r--cpp/rubygen/templates/InvocationVisitor.rb105
-rwxr-xr-xcpp/rubygen/templates/Operations.rb (renamed from cpp/rubygen/samples/Operations.rb)51
-rw-r--r--cpp/rubygen/templates/Session.rb6
-rw-r--r--cpp/rubygen/templates/structs.rb345
8 files changed, 593 insertions, 40 deletions
diff --git a/cpp/rubygen/amqpgen.rb b/cpp/rubygen/amqpgen.rb
index 7d635c99c2..044a2497e0 100755
--- a/cpp/rubygen/amqpgen.rb
+++ b/cpp/rubygen/amqpgen.rb
@@ -60,8 +60,7 @@ class AmqpField < AmqpElement
# Get AMQP type for a domain name.
def domain_type(name)
- domain=elements["/amqp/domain[@name='#{name}']"]
- (domain and domain.attributes["type"] or name)
+ @cache_domain_type ||= domain_type_impl(name)
end
# Get the AMQP type of this field.
@@ -70,6 +69,22 @@ class AmqpField < AmqpElement
dt=domain_type d if d
(dt or attributes["type"])
end
+
+ # determine whether this type is defined as a struct in the spec
+ def defined_as_struct()
+ @cache_defined_as_struct ||= defined_as_struct_impl()
+ end
+
+private
+ def domain_type_impl(name)
+ domain=elements["/amqp/domain[@name='#{name}']"]
+ (domain and domain.attributes["type"] or name)
+ end
+
+ def defined_as_struct_impl()
+ domain_name = attributes["domain"]
+ elements["/amqp/domain[@name='#{domain_name}']/struct"]
+ end
end
# AMQP method element
@@ -98,6 +113,18 @@ class AmqpMethod < AmqpElement
}
end
+ def has_result?()
+ @cache_has_result ||= elements["result/struct"]
+ end
+
+ def result_struct()
+ @cache_result_struct ||= has_result? ? AmqpStruct.new(elements["result/struct"], self) : nil
+ end
+
+ def is_server_method?()
+ @cache_is_server_method ||= elements["chassis[@name='server']"]
+ end
+
def request?() responds_to().empty?; end
def response?() not request?; end
end
@@ -116,7 +143,25 @@ class AmqpClass < AmqpElement
def amqp_methods_on(chassis)
@cache_amqp_methods_on ||= { }
@cache_amqp_methods_on[chassis] ||= elements.collect("method/chassis[@name='#{chassis}']/..") { |m| AmqpMethod.new(m,self) }.sort_by_name
+ end
end
+
+
+# AMQP struct element.
+class AmqpStruct < AmqpElement
+ def initialize(xml,amqp) super; end
+
+ def type() attributes["type"]; end
+
+ def size() attributes["size"]; end
+
+ def result?() parent.name == "result"; end
+
+ def domain?() parent.name == "domain"; end
+
+ def fields()
+ @cache_fields ||= elements.collect("field") { |f| AmqpField.new(f,self); }
+ end
end
# AMQP root element.
@@ -152,9 +197,23 @@ class AmqpRoot < AmqpElement
@cache_amqp_classes ||= elements.collect("class") { |c|
AmqpClass.new(c,self) }.sort_by_name
end
+
+ def amqp_structs()
+ @cache_amqp_structs ||= amqp_result_structs + amqp_domain_structs
+ end
+
+ def amqp_domain_structs()
+ @cache_amqp_domain_structs ||= elements.collect("domain/struct") { |s| AmqpStruct.new(s, self) }
+ end
+
+ def amqp_result_structs()
+ @cache_amqp_result_structs ||= amqp_methods.collect { |m| m.result_struct }.compact
+ end
# Return all methods on all classes.
- def amqp_methods() amqp_classes.collect { |c| c.amqp_methods }.flatten; end
+ def amqp_methods()
+ @cache_amqp_methods ||= amqp_classes.collect { |c| c.amqp_methods }.flatten;
+ end
# Return all methods on chassis for all classes.
def amqp_methods_on(chassis)
diff --git a/cpp/rubygen/cppgen.rb b/cpp/rubygen/cppgen.rb
index a3314c7e11..109005e743 100755
--- a/cpp/rubygen/cppgen.rb
+++ b/cpp/rubygen/cppgen.rb
@@ -62,9 +62,11 @@ end
# Additional methods for AmqpField.
class AmqpField
def cppname() @cache_cppname ||= name.lcaps.cppsafe; end
- def cpptype() @cache_cpptype ||= amqp_root.param_type(field_type); end
+ def cpptype() @cache_cpptype ||= defined_as_struct ? "const " + field_type.caps + "&" : amqp_root.param_type(field_type); end
+ def cpp_member_type() @cache_cpp_member_type ||= defined_as_struct ? field_type.caps : amqp_root.member_type(field_type); end
def cppret_type() @cache_cpptype ||= amqp_root.return_type(field_type); end
def type_name () @type_name ||= cpptype+" "+cppname; end
+ def bit?() field_type == "bit" end
end
# Additional methods for AmqpMethod
@@ -82,6 +84,32 @@ class AmqpClass
end
end
+class AmqpStruct
+ def cppname()
+ @cache_cppname ||= cppname_impl()
+ end
+
+private
+
+ def cppname_impl()
+ #The name of the struct comes from the context it appears in:
+ #structs defined in a domain get their name from the domain
+ #element, structs defined in a result element get their name by
+ #appending 'Result' to the enclosing method name.
+ if (domain?)
+ parent.attributes["name"].caps
+ else
+ if (result?)
+ method = parent.parent
+ method.parent.attributes["name"].caps + method.attributes["name"].caps + "Result"
+ else
+ raise "Bad struct context: expected struct to be child of <domain> or <result>"
+ end
+ end
+
+ end
+end
+
# Additional methos for AmqpRoot
class AmqpRoot
# FIXME aconway 2007-06-20: fix u_int types, should be uint
@@ -97,6 +125,7 @@ class AmqpRoot
"table"=>["FieldTable", "const FieldTable&", "const FieldTable&"],
"content"=>["Content", "const Content&", "const Content&"],
"rfc1982-long-set"=>["SequenceNumberSet", "const SequenceNumberSet&", "const SequenceNumberSet&"],
+ "long-struct"=>["string", "const string&"],
"uuid"=>["string", "const string&"] # FIXME should be: ["Uuid", "const Uuid&", "const Uuid&"]
}
diff --git a/cpp/rubygen/generate b/cpp/rubygen/generate
index 14b273b688..52313186ed 100755
--- a/cpp/rubygen/generate
+++ b/cpp/rubygen/generate
@@ -38,6 +38,10 @@ if makefile
generator_files=Dir["**/*.rb"] << File.basename(__FILE__)
Dir.chdir dir
rgen_generator=generator_files.map{ |f| "$(rgen_dir)/#{f}" }
+ rgen_srcs=GenFiles.get.map{ |f| "#{Outdir}/#{f}" }
+ rgen_client_cpp = rgen_srcs.select { |f| f =~ /qpid\/client\/.+\.cpp$/ }
+ rgen_common_cpp = rgen_srcs.select { |f| f =~ /qpid\/framing\/.+\.cpp$/ }
+ rgen_h = rgen_srcs.select { |f| f =~ /.+\.h$/ }
File.open(makefile, 'w') { |out|
out << <<EOS
@@ -46,7 +50,13 @@ if makefile
rgen_generator=#{rgen_generator.join(" \\\n ")}
-rgen_srcs=#{GenFiles.get.join(" \\\n ")}
+rgen_client_cpp=#{rgen_client_cpp.join(" \\\n ")}
+
+rgen_common_cpp=#{rgen_common_cpp.join(" \\\n ")}
+
+rgen_h=#{rgen_h.join(" \\\n ")}
+
+rgen_srcs=rgen_h rgen_client_cpp rgen_common_cpp
if GENERATE
$(rgen_srcs) $(srcdir)/#{File.basename makefile}: $(rgen_generator) $(specs)
diff --git a/cpp/rubygen/samples/runme b/cpp/rubygen/samples/runme
deleted file mode 100755
index e06d128707..0000000000
--- a/cpp/rubygen/samples/runme
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env ruby
-#
-# Run this script to generate code for the C++ broker.
-#
-$: << ".." # Include .. in load path
-if ARGV.size < 2
- puts "Usage: <output_directory> <amqp .xml files...>"
- puts "Note: passing '-' as the output directory lists generated files."
- exit 1
-end
-
-require 'amqpgen'
-Amqp=AmqpRoot.new(*ARGV[1,ARGV.size])
-
-require 'Proxy.rb'
-require 'Operations.rb'
-
-
diff --git a/cpp/rubygen/templates/InvocationVisitor.rb b/cpp/rubygen/templates/InvocationVisitor.rb
new file mode 100644
index 0000000000..f3ac03ece2
--- /dev/null
+++ b/cpp/rubygen/templates/InvocationVisitor.rb
@@ -0,0 +1,105 @@
+#!/usr/bin/env ruby
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class InvocationVisitor < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace="qpid::framing"
+ @classname="InvocationVisitor"
+ @filename="qpid/framing/InvocationVisitor"
+ end
+
+ def invocation_args(m)
+ if (m.amqp_parent.name == "message" && (m.name == "transfer" || m.name == "append"))
+ "body"
+ else
+ m.param_names.collect {|p| "body.get" + p.caps + "()" }.join(",\n")
+ end
+ end
+
+ def null_visit(m)
+ genl "void InvocationVisitor::visit(const #{m.body_name}&){}"
+ end
+
+ def define_visit(m)
+ if (m.fields.size > 0)
+ body = "body"
+ else
+ body = "/*body*/"
+ end
+ gen <<EOS
+void InvocationVisitor::visit(const #{m.body_name}& #{body})
+{
+ AMQP_ServerOperations::#{m.amqp_parent.cppname}Handler* ptr(0);
+ if (invocable) {
+ ptr = dynamic_cast<AMQP_ServerOperations::#{m.amqp_parent.cppname}Handler*>(invocable);
+ } else {
+ ptr = ops->get#{m.amqp_parent.cppname}Handler();
+ }
+
+ if (ptr) {
+EOS
+ if (m.has_result?)
+ indent(2) { genl "encode<#{m.result_struct.cppname.caps}>(ptr->#{m.cppname}(#{invocation_args(m)}), result);" }
+ else
+ indent(2) { genl "ptr->#{m.cppname}(#{invocation_args(m)});" }
+ end
+
+ gen <<EOS
+ succeeded = true;
+ } else {
+ succeeded = false;
+ }
+}
+EOS
+ end
+
+ def generate()
+ h_file("#{@filename}") {
+ include "AMQP_ServerOperations.h"
+ include "MethodBodyConstVisitor.h"
+ include "qpid/framing/StructHelper.h"
+ namespace(@namespace) {
+ cpp_class("InvocationVisitor", ["public MethodBodyConstVisitor", "private StructHelper"]) {
+ indent {
+ genl "AMQP_ServerOperations* const ops;"
+ genl "Invocable* const invocable;"
+ genl "std::string result;"
+ genl "bool succeeded;"
+ }
+ genl "public:"
+ indent {
+ genl "InvocationVisitor(AMQP_ServerOperations* _ops) : ops(_ops), invocable(0) {}"
+ genl "InvocationVisitor(Invocable* _invocable) : ops(0), invocable(_invocable) {}"
+ genl "const std::string& getResult() const { return result; }"
+ genl "const bool hasResult() const { return !result.empty(); }"
+ genl "bool wasHandled() const { return succeeded; }"
+ genl "void clear();"
+ genl "virtual ~InvocationVisitor() {}"
+ }
+ @amqp.amqp_methods.each { |m| genl "void visit(const #{m.body_name}&);" }
+ }
+ }
+ }
+
+ cpp_file("#{@filename}") {
+ include "InvocationVisitor.h"
+ @amqp.amqp_methods.each { |m| include m.body_name }
+ namespace(@namespace) {
+ genl "void InvocationVisitor::clear() {"
+ indent {
+ genl "succeeded = false;"
+ genl "result.clear();"
+ }
+ genl "}"
+ genl
+ @amqp.amqp_methods.each { |m| m.is_server_method? ? define_visit(m) : null_visit(m) }
+ }
+ }
+ end
+end
+
+InvocationVisitor.new(Outdir, Amqp).generate();
+
diff --git a/cpp/rubygen/samples/Operations.rb b/cpp/rubygen/templates/Operations.rb
index 6b0a24a26c..79b82a9d59 100755
--- a/cpp/rubygen/samples/Operations.rb
+++ b/cpp/rubygen/templates/Operations.rb
@@ -13,46 +13,69 @@ class OperationsGen < CppGen
end
def handler_method (m)
- gen "\nvirtual void #{m.cppname}("
- gen m.signature.join(",\n")
- gen ") = 0;\n"
+ if (m.has_result?)
+ return_type = "#{m.amqp_parent.name.caps}#{m.cppname.caps}Result"
+ else
+ return_type = "void"
+ end
+ if (m.amqp_parent.name == "message" && (m.name == "transfer" || m.name == "append"))
+ gen "\nvirtual #{return_type} #{m.cppname}(const framing::AMQMethodBody& context) = 0;\n"
+ else
+ gen "\nvirtual #{return_type} #{m.cppname}("
+ gen m.signature.join(",\n")
+ gen ") = 0;\n"
+ end
end
def handler_classname(c) c.name.caps+"Handler"; end
def handler_class(c)
- handlerclass=handler_classname c
- gen <<EOS
+ if (!c.amqp_methods_on(@chassis).empty?)
+ handlerclass=handler_classname c
+ gen <<EOS
// ==================== class #{handlerclass} ====================
-class #{handlerclass} : Invocable {
+class #{handlerclass} : public virtual Invocable {
// Constructors and destructors
public:
#{handlerclass}(){};
virtual ~#{handlerclass}() {}
// Protocol methods
EOS
- c.amqp_methods_on(@chassis).each { |m| handler_method(m) }
- gen <<EOS
+ c.amqp_methods_on(@chassis).each { |m| handler_method(m) }
+ gen <<EOS
}; // class #{handlerclass}
EOS
+ end
end
def handler_get(c)
- handlerclass=handler_classname c
- gen "virtual #{handlerclass}* get#{handlerclass}() = 0;\n"
+ if (!c.amqp_methods_on(@chassis).empty?)
+ handlerclass=handler_classname c
+ gen "virtual #{handlerclass}* get#{handlerclass}() = 0;\n"
+ end
end
def generate()
- h_file("#{@classname}.h") {
+ 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 Invocable
+{
+protected:
+ Invocable() {}
+ virtual ~Invocable() {}
+};
+
class #{@classname} {
public:
@@ -60,8 +83,8 @@ class #{@classname} {
virtual ProtocolVersion getVersion() const = 0;
- // Include framing constant declarations
- #include "AMQP_Constants.h"
+ // Include framing constant declarations - why?
+ //#include "qpid/framing/AMQP_Constants.h"
// Inner classes
EOS
@@ -74,7 +97,7 @@ EOS
indent { @amqp.amqp_classes.each { |c| handler_get(c) } }
gen <<EOS
}; /* class #{@classname} */
-}
+}}
EOS
}
end
diff --git a/cpp/rubygen/templates/Session.rb b/cpp/rubygen/templates/Session.rb
index d5b6d9e8c2..0d5808318a 100644
--- a/cpp/rubygen/templates/Session.rb
+++ b/cpp/rubygen/templates/Session.rb
@@ -67,9 +67,9 @@ class SessionGen < CppGen
#include "qpid/framing/amqp_framing.h"
#include "qpid/framing/ProtocolVersion.h"
#include "qpid/framing/MethodContent.h"
-#include "ConnectionImpl.h"
-#include "Response.h"
-#include "SessionCore.h"
+#include "qpid/client/ConnectionImpl.h"
+#include "qpid/client/Response.h"
+#include "qpid/client/SessionCore.h"
namespace qpid {
namespace client {
diff --git a/cpp/rubygen/templates/structs.rb b/cpp/rubygen/templates/structs.rb
new file mode 100644
index 0000000000..972a0386b9
--- /dev/null
+++ b/cpp/rubygen/templates/structs.rb
@@ -0,0 +1,345 @@
+#!/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
+
+ EncodingMap={
+ "octet"=>"Octet",
+ "short"=>"Short",
+ "long"=>"Long",
+ "longlong"=>"LongLong",
+ "longstr"=>"LongString",
+ "shortstr"=>"ShortString",
+ "timestamp"=>"LongLong",
+ "uuid"=>"ShortString",#FIXME
+ "table"=>"FieldTable",
+ "content"=>"Content",
+ "long-struct"=>"LongString"
+ }
+ SizeMap={
+ "octet"=>"1",
+ "short"=>"2",
+ "long"=>"4",
+ "longlong"=>"8",
+ "timestamp"=>"8"
+ }
+
+ ValueTypes=["octet", "short", "long", "longlong", "timestamp"]
+
+ def printable_form(f)
+ if (f.cpptype == "u_int8_t")
+ return "(int) " + f.cppname
+ else
+ return f.cppname
+ end
+ end
+
+ def generate_encode(f, combined)
+ if (f.field_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
+ encoded = EncodingMap[f.field_type]
+ if (encoded)
+ genl "buffer.put#{encoded}(#{f.cppname});"
+ else
+ genl "#{f.cppname}.encode(buffer);"
+ end
+ end
+ end
+
+ def generate_decode(f, combined)
+ if (f.field_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
+ encoded = EncodingMap[f.field_type]
+ if (encoded)
+ if (ValueTypes.include?(f.field_type))
+ genl "#{f.cppname} = buffer.get#{encoded}();"
+ else
+ genl "buffer.get#{encoded}(#{f.cppname});"
+ end
+ else
+ genl "#{f.cppname}.decode(buffer);"
+ end
+ end
+ end
+
+ def generate_size(f, combined)
+ if (f.field_type == "bit")
+ names = ([f] + combined).collect {|g| g.cppname}
+ genl "+ 1 //#{names.join(", ")}"
+ else
+ size = SizeMap[f.field_type]
+ if (size)
+ genl "+ #{size} //#{f.cppname}"
+ elsif (f.cpp_member_type == "SequenceNumberSet")
+ genl "+ #{f.cppname}.encodedSize()"
+ else
+ encoded = EncodingMap[f.field_type]
+ gen "+ 4 " if encoded == "LongString"
+ gen "+ 1 " if encoded == "ShortString"
+ genl "+ #{f.cppname}.size()"
+ end
+ end
+ 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 methodbody_extra_defs(s)
+ gen <<EOS
+ using AMQMethodBody::accept;
+ void accept(MethodBodyConstVisitor& v) const { v.visit(*this); }
+
+ inline ClassId amqpClassId() const { return CLASS_ID; }
+ inline MethodId amqpMethodId() const { return METHOD_ID; }
+EOS
+ if (s.is_server_method?)
+ gen <<EOS
+ void invoke(AMQP_ServerOperations& target)
+ {
+ target.get#{s.amqp_parent.cppname}Handler()->#{s.cppname}
+ (
+EOS
+ if (s.amqp_parent.name == "message" && (s.name == "transfer" || s.name == "append"))
+ indent(4) { genl "*this" }
+ else
+ indent(4) { genl s.param_names.join(",\n") }
+ end
+
+ genl <<EOS
+ );
+ }
+
+ bool invoke(Invocable* target)
+ {
+ AMQP_ServerOperations::#{s.amqp_parent.cppname}Handler* ptr
+ = dynamic_cast<AMQP_ServerOperations::#{s.amqp_parent.cppname}Handler*>(target);
+ if (ptr) {
+ ptr->#{s.cppname}(
+EOS
+ if (s.amqp_parent.name == "message" && (s.name == "transfer" || s.name == "append"))
+ indent(5) { genl "*this" }
+ else
+ indent(5) { genl s.param_names.join(",\n") }
+ end
+
+ gen <<EOS
+ );
+ return true;
+ } else {
+ return false;
+ }
+ }
+EOS
+ end
+ 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} _#{f.cppname}" }.join(",\n") }
+ gen ")"
+ genl ": " if s.fields.size > 0
+ indent { gen s.fields.collect { |f| " #{f.cppname}(_#{f.cppname})" }.join(",\n") }
+ genl " {}"
+ end
+ if (s.kind_of? AmqpMethod)
+ genl "#{name}(ProtocolVersion=ProtocolVersion()) {}"
+ end
+ end
+
+ def define_accessors(f)
+ genl "void set#{f.name.caps}(#{f.cpptype} _#{f.cppname}) { #{f.cppname} = _#{f.cppname}; }"
+ genl "#{f.cpptype} get#{f.name.caps}() const { return #{f.cppname}; }"
+ end
+
+ def define_struct(s)
+ classname = s.cppname
+ inheritance = ""
+ if (s.kind_of? AmqpMethod)
+ classname = s.body_name
+ inheritance = ": public AMQMethodBody"
+ 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
+
+ #need to include any nested struct definitions
+ s.fields.select {|f| f.defined_as_struct }.map {|f| include f.name.caps}
+
+ gen <<EOS
+
+#include <ostream>
+#include "qpid/framing/amqp_types_full.h"
+
+namespace qpid {
+namespace framing {
+
+class #{classname} #{inheritance} {
+EOS
+ indent { s.fields.each { |f| genl "#{f.cpp_member_type} #{f.cppname};" } }
+ genl "public:"
+ if (s.kind_of? AmqpMethod)
+ indent { genl "static const ClassId CLASS_ID = #{s.amqp_parent.index};" }
+ indent { genl "static const MethodId METHOD_ID = #{s.index};" }
+ end
+
+ if (s.kind_of? AmqpStruct)
+ if (s.type)
+ if (s.result?)
+ #as result structs have types that are only unique to the
+ #class, they have a class dependent qualifier added to them
+ #(this is inline with current python code but a formal
+ #solution is expected from the WG
+ indent { genl "static const uint16_t TYPE = #{s.type} + #{s.amqp_parent.amqp_parent.index} * 256;" }
+ else
+ indent { genl "static const uint16_t TYPE = #{s.type};" }
+ end
+ end
+ end
+
+ indent {
+ define_constructor(classname, s)
+ genl ""
+ s.fields.each { |f| define_accessors(f) }
+ }
+ 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);
+ uint32_t size() const;
+ void print(std::ostream& out) const;
+}; /* class #{classname} */
+
+}}
+EOS
+ }
+ cpp_file("qpid/framing/#{classname}.cpp") {
+ if (s.fields.size > 0)
+ buffer = "buffer"
+ else
+ buffer = "/*buffer*/"
+ end
+ gen <<EOS
+#include "#{classname}.h"
+
+using namespace qpid::framing;
+
+void #{classname}::encode(Buffer& #{buffer}) const
+{
+EOS
+ indent { process_fields(s) { |f, combined| generate_encode(f, combined) } }
+ gen <<EOS
+}
+
+void #{classname}::decode(Buffer& #{buffer}, uint32_t /*size*/)
+{
+EOS
+ indent { process_fields(s) { |f, combined| generate_decode(f, combined) } }
+ gen <<EOS
+}
+
+uint32_t #{classname}::size() const
+{
+ return 0
+EOS
+ indent { process_fields(s) { |f, combined| generate_size(f, combined) } }
+ gen <<EOS
+ ;
+}
+
+void #{classname}::print(std::ostream& out) const
+{
+ out << "#{classname}: ";
+EOS
+ 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)};" }
+ }
+ gen <<EOS
+}
+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()
+ @amqp.amqp_structs.each { |s| define_struct(s) }
+ @amqp.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") { @amqp.amqp_structs.each { |s| genl "#include \"#{s.cppname}.h\"" } }
+ end
+end
+
+StructGen.new(ARGV[0], Amqp).generate()
+