From 0731e05211ff5e47e6a8b2c006bd6012da5cc161 Mon Sep 17 00:00:00 2001 From: Alan Conway Date: Wed, 27 Feb 2008 16:37:48 +0000 Subject: Generate code for both 0-99 preview and 0-10 final specs . 0-10 final: extended code generation and non-generated support classes. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@631638 13f79535-47bb-0310-9956-ffa450edef68 --- cpp/rubygen/99-0/MethodBodyConstVisitor.rb | 27 ++ cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb | 35 ++ cpp/rubygen/99-0/MethodHolder.rb | 100 +++++ cpp/rubygen/99-0/Operations.rb | 96 +++++ cpp/rubygen/99-0/OperationsInvoker.rb | 92 +++++ cpp/rubygen/99-0/Proxy.rb | 82 ++++ cpp/rubygen/99-0/Session.rb | 195 ++++++++++ cpp/rubygen/99-0/all_method_bodies.rb | 21 ++ cpp/rubygen/99-0/constants.rb | 82 ++++ cpp/rubygen/99-0/frame_body_lists.rb | 31 ++ cpp/rubygen/99-0/structs.rb | 538 +++++++++++++++++++++++++++ 11 files changed, 1299 insertions(+) create mode 100755 cpp/rubygen/99-0/MethodBodyConstVisitor.rb create mode 100755 cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb create mode 100755 cpp/rubygen/99-0/MethodHolder.rb create mode 100755 cpp/rubygen/99-0/Operations.rb create mode 100755 cpp/rubygen/99-0/OperationsInvoker.rb create mode 100755 cpp/rubygen/99-0/Proxy.rb create mode 100644 cpp/rubygen/99-0/Session.rb create mode 100755 cpp/rubygen/99-0/all_method_bodies.rb create mode 100755 cpp/rubygen/99-0/constants.rb create mode 100644 cpp/rubygen/99-0/frame_body_lists.rb create mode 100644 cpp/rubygen/99-0/structs.rb (limited to 'cpp/rubygen/99-0') diff --git a/cpp/rubygen/99-0/MethodBodyConstVisitor.rb b/cpp/rubygen/99-0/MethodBodyConstVisitor.rb new file mode 100755 index 0000000000..f9ef95f5a0 --- /dev/null +++ b/cpp/rubygen/99-0/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/99-0/MethodBodyDefaultVisitor.rb b/cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb new file mode 100755 index 0000000000..a74b0c06d6 --- /dev/null +++ b/cpp/rubygen/99-0/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/99-0/MethodHolder.rb b/cpp/rubygen/99-0/MethodHolder.rb new file mode 100755 index 0000000000..a708db6676 --- /dev/null +++ b/cpp/rubygen/99-0/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 "" + include "" + 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 <(); 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/99-0/Operations.rb b/cpp/rubygen/99-0/Operations.rb new file mode 100755 index 0000000000..c985bb6105 --- /dev/null +++ b/cpp/rubygen/99-0/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 : "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 < +#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 <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/99-0/Proxy.rb b/cpp/rubygen/99-0/Proxy.rb new file mode 100755 index 0000000000..2829884673 --- /dev/null +++ b/cpp/rubygen/99-0/Proxy.rb @@ -0,0 +1,82 @@ +#!/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 <" + 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/99-0/Session.rb b/cpp/rubygen/99-0/Session.rb new file mode 100644 index 0000000000..e01a28a62d --- /dev/null +++ b/cpp/rubygen/99-0/Session.rb @@ -0,0 +1,195 @@ +#!/usr/bin/env ruby +# Usage: output_directory xml_spec_file [xml_spec_file...] +# +$: << '..' +require 'cppgen' + +class CppGen + def session_methods + excludes = ["channel", "connection", "session", "execution"] + gen_methods=@amqp.methods_on(@chassis).reject { |m| + excludes.include? m.parent.name + } + 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" 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::SequenceNumberSet;" + genl "using framing::Uuid;" + 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 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 "" + 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 < 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/99-0/all_method_bodies.rb b/cpp/rubygen/99-0/all_method_bodies.rb new file mode 100755 index 0000000000..5971d49189 --- /dev/null +++ b/cpp/rubygen/99-0/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/99-0/constants.rb b/cpp/rubygen/99-0/constants.rb new file mode 100755 index 0000000000..b5f559d504 --- /dev/null +++ b/cpp/rubygen/99-0/constants.rb @@ -0,0 +1,82 @@ +#!/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.index}" + l.concat c.methods_.map { |m| + "#{c.name.shout}_#{m.name.shout}_METHOD_ID=#{m.index}" } + } + genl l.join(",\n") + }}} + end + + def exbase(c) + case c.class_ + when "soft-error" then "ChannelException" + when "hard-error" then "ConnectionException" + end + end + + def reply_exceptions_h() + h_file("#{@dir}/reply_exceptions") { + include "qpid/Exception" + namespace(@namespace) { + @amqp.constants.each { |c| + base = exbase c + if base + genl + struct(c.name.caps+"Exception", base) { + genl "#{c.name.caps}Exception(const std::string& msg=std::string()) : #{base}(#{c.value}, \"#{c.name}: \"+msg) {}" + } + end + } + genl + genl "void throwReplyException(int code, const std::string& text);" + } + } + end + + def reply_exceptions_cpp() + cpp_file("#{@dir}/reply_exceptions") { + include "#{@dir}/reply_exceptions" + include "" + namespace("qpid::framing") { + scope("void throwReplyException(int code, const std::string& text) {"){ + scope("switch (code) {") { + genl "case 200: break; // No exception" + @amqp.constants.each { |c| + if exbase c + genl "case #{c.value}: throw #{c.name.caps}Exception(text);" + end + } + scope("default:","") { + genl "std::ostringstream msg;" + genl 'msg << "Invalid reply code " << code << ": " << text;' + genl 'throw InvalidArgumentException(msg.str());' + }}}}} + end + + def generate() + constants_h + reply_exceptions_h + reply_exceptions_cpp + end +end + +ConstantsGen.new($outdir, $amqp).generate(); + diff --git a/cpp/rubygen/99-0/frame_body_lists.rb b/cpp/rubygen/99-0/frame_body_lists.rb new file mode 100644 index 0000000000..b20e4550f3 --- /dev/null +++ b/cpp/rubygen/99-0/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 <"Octet", + "short"=>"Short", + "long"=>"Long", + "longlong"=>"LongLong", + "longstr"=>"LongString", + "shortstr"=>"ShortString", + "timestamp"=>"LongLong", + "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 is_packed(s) + s.kind_of? AmqpStruct + end + + def execution_header?(s) + false and s.kind_of? AmqpMethod and s.parent.l4? + end + + def has_bitfields_only(s) + s.fields.select {|f| f.domain.type_ != "bit"}.empty? + end + + def default_initialisation(s) + params = s.fields.select {|f| ValueTypes.include?(f.domain.type_) || (!is_packed(s) && f.domain.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.domain.type_ == "bit") + return "get#{f.name.caps}()" + else + return f.cppname + end + end + + def flag_mask(s, i) + pos = SizeMap[s.pack]*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.domain.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.domain.type_ == "bit" } + end + + def size_packed_struct(s) + genl "total += #{SizeMap[s.pack]};" + process_packed_fields(s) { |f, i| size_packed_field(s, f, i) unless f.domain.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.domain.cpptype.encode(f.cppname,"buffer") } + end + + def decode_packed_field(s, f, i) + genl "if (flags & #{flag_mask(s, i)})" + indent { genl f.domain.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.domain.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.domain.cpptype.encode(f.cppname,"buffer") + end + end + + def generate_decode(f, combined) + if (f.domain.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.domain.cpptype.decode(f.cppname,"buffer") + end + end + + def generate_size(f, combined) + if (f.domain.type_ == "bit") + names = ([f] + combined).collect {|g| g.cppname} + genl "total += 1;//#{names.join(", ")}" + else + size = SizeMap[f.domain.type_] + if (size) + genl "total += #{size};//#{f.cppname}" + elsif (f.cpptype.name == "SequenceNumberSet") + genl "total += #{f.cppname}.encodedSize();" + else + encoded = EncodingMap[f.domain.type_] + gen "total += (" + gen "4 + " if encoded == "LongString" + 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 methodbody_extra_defs(s) + gen < ResultType invoke(T& invocable) const { + return invocable.#{s.cppname}(#{s.param_names.join ", "}); + } + + 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.domain.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.domain.type_ == "bit"} + process_packed_fields(s) { |f, i| genl "flags |= #{flag_mask(s, i)};" unless f.domain.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()) {}" + end + if (s.kind_of? AmqpStruct) + genl "#{name}() #{default_initialisation(s)} {}" + end + end + + def define_packed_field_accessors(s, f, i) + if (f.domain.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_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.domain.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 < +#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.domain.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.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.parent.parent.parent.index} * 256;" } + else + indent { genl "static const uint16_t TYPE = #{s.type_};" } + end + 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 < 0 || execution_header?(s)) + buffer = "buffer" + else + buffer = "/*buffer*/" + end + gen <