From 0e9451480c83511bd37f99d83ad82b30b399f05d 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@631638 13f79535-47bb-0310-9956-ffa450edef68 --- qpid/cpp/rubygen/0-10/amqp_0_10.rb | 175 +++++++ qpid/cpp/rubygen/99-0/MethodBodyConstVisitor.rb | 27 ++ qpid/cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb | 35 ++ qpid/cpp/rubygen/99-0/MethodHolder.rb | 100 ++++ qpid/cpp/rubygen/99-0/Operations.rb | 96 ++++ qpid/cpp/rubygen/99-0/OperationsInvoker.rb | 92 ++++ qpid/cpp/rubygen/99-0/Proxy.rb | 82 ++++ qpid/cpp/rubygen/99-0/Session.rb | 195 ++++++++ qpid/cpp/rubygen/99-0/all_method_bodies.rb | 21 + qpid/cpp/rubygen/99-0/constants.rb | 82 ++++ qpid/cpp/rubygen/99-0/frame_body_lists.rb | 31 ++ qpid/cpp/rubygen/99-0/structs.rb | 538 +++++++++++++++++++++ qpid/cpp/rubygen/MethodBodyDefaultVisitor.rb | 2 +- qpid/cpp/rubygen/amqpgen.rb | 72 ++- qpid/cpp/rubygen/cppgen.rb | 73 ++- qpid/cpp/rubygen/generate | 35 +- .../rubygen/templates/MethodBodyConstVisitor.rb | 27 -- .../rubygen/templates/MethodBodyDefaultVisitor.rb | 35 -- qpid/cpp/rubygen/templates/MethodHolder.rb | 100 ---- qpid/cpp/rubygen/templates/Operations.rb | 96 ---- qpid/cpp/rubygen/templates/OperationsInvoker.rb | 92 ---- qpid/cpp/rubygen/templates/Proxy.rb | 80 --- qpid/cpp/rubygen/templates/Session.rb | 195 -------- qpid/cpp/rubygen/templates/all_method_bodies.rb | 21 - qpid/cpp/rubygen/templates/amqp_0_10.rb | 64 --- qpid/cpp/rubygen/templates/constants.rb | 82 ---- qpid/cpp/rubygen/templates/frame_body_lists.rb | 31 -- qpid/cpp/rubygen/templates/structs.rb | 538 --------------------- 28 files changed, 1614 insertions(+), 1403 deletions(-) create mode 100755 qpid/cpp/rubygen/0-10/amqp_0_10.rb create mode 100755 qpid/cpp/rubygen/99-0/MethodBodyConstVisitor.rb create mode 100755 qpid/cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb create mode 100755 qpid/cpp/rubygen/99-0/MethodHolder.rb create mode 100755 qpid/cpp/rubygen/99-0/Operations.rb create mode 100755 qpid/cpp/rubygen/99-0/OperationsInvoker.rb create mode 100755 qpid/cpp/rubygen/99-0/Proxy.rb create mode 100644 qpid/cpp/rubygen/99-0/Session.rb create mode 100755 qpid/cpp/rubygen/99-0/all_method_bodies.rb create mode 100755 qpid/cpp/rubygen/99-0/constants.rb create mode 100644 qpid/cpp/rubygen/99-0/frame_body_lists.rb create mode 100644 qpid/cpp/rubygen/99-0/structs.rb delete mode 100755 qpid/cpp/rubygen/templates/MethodBodyConstVisitor.rb delete mode 100755 qpid/cpp/rubygen/templates/MethodBodyDefaultVisitor.rb delete mode 100755 qpid/cpp/rubygen/templates/MethodHolder.rb delete mode 100755 qpid/cpp/rubygen/templates/Operations.rb delete mode 100755 qpid/cpp/rubygen/templates/OperationsInvoker.rb delete mode 100755 qpid/cpp/rubygen/templates/Proxy.rb delete mode 100644 qpid/cpp/rubygen/templates/Session.rb delete mode 100755 qpid/cpp/rubygen/templates/all_method_bodies.rb delete mode 100755 qpid/cpp/rubygen/templates/amqp_0_10.rb delete mode 100755 qpid/cpp/rubygen/templates/constants.rb delete mode 100644 qpid/cpp/rubygen/templates/frame_body_lists.rb delete mode 100644 qpid/cpp/rubygen/templates/structs.rb (limited to 'qpid/cpp/rubygen') diff --git a/qpid/cpp/rubygen/0-10/amqp_0_10.rb b/qpid/cpp/rubygen/0-10/amqp_0_10.rb new file mode 100755 index 0000000000..985d275668 --- /dev/null +++ b/qpid/cpp/rubygen/0-10/amqp_0_10.rb @@ -0,0 +1,175 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class Amqp_0_10 < CppGen + ArrayTypes={ + "str16-array" => "Str16", + "amqp-host-array" => "connection::AmqpHostUrl", + "command-fragments" => "session::CommandFragment", + "in-doubt" => "dtx::Xid" + } + + def initialize(outdir, amqp) + super(outdir, amqp) + @ns="qpid::amqp_0_10" + @dir="qpid/amqp_0_10" + end + + # domains + + def domain_h(d) + typename=d.name.typename + if d.enum + scope("enum #{typename} {", "};") { + genl d.enum.choices.map { |c| "#{c.name.constname} = #{c.value}" }.join(",\n") + } + elsif d.type_ == "array" + @array_domains << "typedef Array<#{ArrayTypes[d.name]}> #{typename};\n" + else + genl "typedef #{d.type_.amqp2cpp} #{typename};" + end + end + + # structs + + # field members and MemberInfo + def member_h(m) + genl "static const MemberInfo INFO;" + m.fields.each { |f| genl "#{f.type_.amqp2cpp} #{f.cppname};" } + end + + # MemberInfo constant definition. + def member_cpp(m) + infotype=m.is_a?(AmqpStruct) ? "Struct1Info" : "MemberInfo" + scope("{infotype} #{m.classname}::INFO = {","};") { + inits=[] + inits << (m.parent.is_a?(AmqpClass) ? "&CLASS_INFO" : "0") + inits << m.name << (m.code or "0"); + if m.is_a?(AmqpStruct) + inits << (m.size or 0) << (m.pack or 0) + end + genl inits.join(", ") + } + end + + def struct_h(s) struct(s.classname, "public Struct") { member_h s }; end + def struct_cpp(s) member_cpp s; end + def gen_structs() + file="#{@dir}/structs" + h_file(file) { + include "#{@dir}/built_in_types.h" + include "#{@dir}/helpers.h" + include "" + genl "using boost::call_traits;" + namespace(@ns) { + @amqp.structs.each { |s| struct_h s } + each_class_ns { |c| c.structs.each { |s| struct_h s }} + } + } + cpp_file(file) { + include file + namespace(@ns) { + @amqp.structs.each { |s| struct_h s } + each_class_ns { |c| c.structs.each { |s| struct_cpp s }} + } + } + end + + # command and control + + def action_h(a) + name=a.name.typename + struct(name, "public #{a.base}") { + genl "#{name}() {}" + scope("#{name}(",");") { genl a.parameters } unless a.fields.empty? + scope("template void invoke(T& target) {","}") { + scope("target.#{a.funcname}(", ");") { genl a.values } + } + scope("template void serialize(S& s) {","}") { + gen "s" + a.fields.each { |f| gen "(#{f.cppname})"} + genl ";" + } unless a.fields.empty? + member_h a + } + end + + def action_cpp(a) # command or control + # ctor + scope("#{a.classname}::#{a.classname}(",") :") { genl a.parameters } + indent() { genl a.initializers } + genl "{}" + # member constants + member_cpp a + end + + def class_h(c) + genl "extern const ClassInfo CLASS_INFO;" + @array_domains="" + c.domains.each { |d| domain_h d } + c.structs.each { |s| struct_h s } + gen @array_domains + end + + def class_cpp(c) + genl "const ClassInfo CLASS_INFO = { #{c.code}, \"#{c.name}\" };" + c.structs.each { |s| struct_cpp s } + end + + def gen_specification() + h_file("#{@dir}/specification") { + include "#{@dir}/built_in_types" + include "#{@dir}/helpers" + include "" + genl "using boost::call_traits;" + namespace(@ns) { + # We don't generate top-level domains, as they have clashing values. + each_class_ns { |c| class_h c } + each_class_ns { |c| c.actions.each { |a| action_h a} + } + } + } + cpp_file("#{@dir}/specification") { + include "#{@dir}/specification" + namespace(@ns) { + each_class_ns { |c| class_cpp c } + each_class_ns { |c| c.actions.each { |a| action_cpp a} + } + } + } + end + + def gen_proxy() + h_file("#{@dir}/Proxy.h") { + include "#{@dir}/specification" + namespace(@ns) { + genl "template " + cpp_class("ProxyTemplate") { + public + genl "ProxyTemplate(F f) : functor(f) {}" + @amqp.classes.each { |c| + c.actions.each { |a| + scope("R #{a.funcname}(", ")") { genl a.parameters } + scope() { + var=a.name.funcname + scope("#{a.classname} #{var}(",");") { genl a.arguments } + genl "return functor(#{var});" + } + } + } + private + genl "F functor;" + } + } + } + end + + def generate + gen_specification + gen_proxy + end +end + +Amqp_0_10.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/99-0/MethodBodyConstVisitor.rb b/qpid/cpp/rubygen/99-0/MethodBodyConstVisitor.rb new file mode 100755 index 0000000000..f9ef95f5a0 --- /dev/null +++ b/qpid/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/qpid/cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb b/qpid/cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb new file mode 100755 index 0000000000..a74b0c06d6 --- /dev/null +++ b/qpid/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/qpid/cpp/rubygen/99-0/MethodHolder.rb b/qpid/cpp/rubygen/99-0/MethodHolder.rb new file mode 100755 index 0000000000..a708db6676 --- /dev/null +++ b/qpid/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/qpid/cpp/rubygen/99-0/Operations.rb b/qpid/cpp/rubygen/99-0/Operations.rb new file mode 100755 index 0000000000..c985bb6105 --- /dev/null +++ b/qpid/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/qpid/cpp/rubygen/99-0/Proxy.rb b/qpid/cpp/rubygen/99-0/Proxy.rb new file mode 100755 index 0000000000..2829884673 --- /dev/null +++ b/qpid/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/qpid/cpp/rubygen/99-0/Session.rb b/qpid/cpp/rubygen/99-0/Session.rb new file mode 100644 index 0000000000..e01a28a62d --- /dev/null +++ b/qpid/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/qpid/cpp/rubygen/99-0/all_method_bodies.rb b/qpid/cpp/rubygen/99-0/all_method_bodies.rb new file mode 100755 index 0000000000..5971d49189 --- /dev/null +++ b/qpid/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/qpid/cpp/rubygen/99-0/constants.rb b/qpid/cpp/rubygen/99-0/constants.rb new file mode 100755 index 0000000000..b5f559d504 --- /dev/null +++ b/qpid/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/qpid/cpp/rubygen/99-0/frame_body_lists.rb b/qpid/cpp/rubygen/99-0/frame_body_lists.rb new file mode 100644 index 0000000000..b20e4550f3 --- /dev/null +++ b/qpid/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 <::param_type"; end end class AmqpMethod @@ -128,8 +137,40 @@ class AmqpMethod def body_name() parent.name.caps+name.caps+"Body"; end end +class AmqpAction + def nsname() name.namespace; end + def classname() name.typename; end + def funcname() parent.name.funcname + name.caps; end + + def parameters() + fields.map { |f| "#{f.paramtype} #{f.cppname}_"}.join(",\n") + end + + def arguments() + fields.map { |f| "#{f.cppname}_"}.join(",\n") + end + + def values() + fields.map { |f| "#{f.cppname}"}.join(",\n") + end + + def initializers() + fields.map { |f| "#{f.cppname}(#{f.cppname}_)}"}.join(",\n") + end + +end + +class AmqpCommand + def base() "Command"; end +end + +class AmqpControl + def base() "Control"; end +end + class AmqpClass def cppname() name.caps; end + def nsname() name.nsname; end end class AmqpDomain @@ -172,17 +213,20 @@ end class AmqpStruct def cpp_pack_type() AmqpDomain.lookup_type(pack()) or CppType.new("uint16_t"); end - def cpptype() parent.cpptype; end - def cppname() cpptype.name; end + def cpptype() parent.cpptype; end # preview + def cppname() cpptype.name; end # preview + def classname() name.typename; end end class CppGen < Generator def initialize(outdir, *specs) super(outdir,*specs) + # need to sort classes for dependencies + @actions=[] # Stack of end-scope actions end # Write a header file. - def h_file(path) + def h_file(path, &block) path = (/\.h$/ === path ? path : path+".h") guard=path.upcase.tr('./-','_') file(path) { @@ -190,12 +234,12 @@ class CppGen < Generator gen "#define #{guard}\n" gen Copyright yield - gen "#endif /*!#{guard}*/\n" + gen "#endif /*!#{guard}*/\n" } end # Write a .cpp file. - def cpp_file(path) + def cpp_file(path, &block) path = (/\.cpp$/ === path ? path : path+".cpp") file(path) do gen Copyright @@ -209,10 +253,12 @@ class CppGen < Generator genl "#include #{header}" end - def scope(open="{",close="}", &block) - genl open; indent(&block); genl close + def scope(open="{",close="}", &block) + genl open + indent &block + genl close end - + def namespace(name, &block) genl names = name.split("::") @@ -231,7 +277,7 @@ class CppGen < Generator indent { gen "#{bases.join(",\n")}" } end genl - scope("{","};") { yield } + scope("{","};", &block) end def struct(name, *bases, &block) @@ -263,6 +309,11 @@ class CppGen < Generator prefix(" * ",&block) genl " */" end + + # Generate code in namespace for each class + def each_class_ns() + @amqp.classes.each { |c| namespace(c.nsname) { yield c } } + end end # Fully-qualified class name diff --git a/qpid/cpp/rubygen/generate b/qpid/cpp/rubygen/generate index f7bf334569..4ea8aef425 100755 --- a/qpid/cpp/rubygen/generate +++ b/qpid/cpp/rubygen/generate @@ -1,5 +1,6 @@ #!/usr/bin/env ruby require 'amqpgen' +require 'pathname' # # Run a set of code generation templates. @@ -18,17 +19,39 @@ EOS exit 1 end -Outdir=ARGV[0] -Specs=ARGV.grep(/\.xml$/) -Amqp=AmqpRoot.new(*Specs) +# Create array of specs by version +def parse_specs(specs) + roots={ } + specs.each { |spec| + root=AmqpRoot.new(spec) + ver=root.version + if (roots[ver]) + roots[ver].merge(root) + else + roots[ver]=root + end + } + roots +end # Run selected templates if ARGV.any? { |arg| arg=="all" } - templates=Dir["#{File.dirname __FILE__}/templates/*.rb"] + templates=Dir["#{File.dirname __FILE__}/*/*.rb"] else templates=ARGV.grep(/\.rb$/) end -templates.each { |t| load t } + +$outdir=ARGV[0] +$models=parse_specs(ARGV.grep(/\.xml$/)) +templates.each { |t| + ver=Pathname.new(t).dirname.basename.to_s + $amqp=$models[ver] + if $amqp + load t + else + puts "Warning: skipping #{t}, no spec file for version #{ver}." + end +} def make_continue(lines) lines.join(" \\\n "); end @@ -40,7 +63,7 @@ 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_srcs=GenFiles.get.map{ |f| "#{$outdir}/#{f}" } File.open(makefile, 'w') { |out| out << <" - 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/qpid/cpp/rubygen/templates/Operations.rb b/qpid/cpp/rubygen/templates/Operations.rb deleted file mode 100755 index 91007ef3e1..0000000000 --- a/qpid/cpp/rubygen/templates/Operations.rb +++ /dev/null @@ -1,96 +0,0 @@ -#!/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/qpid/cpp/rubygen/templates/Proxy.rb b/qpid/cpp/rubygen/templates/Proxy.rb deleted file mode 100755 index 467476506c..0000000000 --- a/qpid/cpp/rubygen/templates/Proxy.rb +++ /dev/null @@ -1,80 +0,0 @@ -#!/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/qpid/cpp/rubygen/templates/Session.rb b/qpid/cpp/rubygen/templates/Session.rb deleted file mode 100644 index 6a50fdb462..0000000000 --- a/qpid/cpp/rubygen/templates/Session.rb +++ /dev/null @@ -1,195 +0,0 @@ -#!/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/qpid/cpp/rubygen/templates/all_method_bodies.rb b/qpid/cpp/rubygen/templates/all_method_bodies.rb deleted file mode 100755 index d06f459493..0000000000 --- a/qpid/cpp/rubygen/templates/all_method_bodies.rb +++ /dev/null @@ -1,21 +0,0 @@ -#!/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/qpid/cpp/rubygen/templates/amqp_0_10.rb b/qpid/cpp/rubygen/templates/amqp_0_10.rb deleted file mode 100755 index ef779c9015..0000000000 --- a/qpid/cpp/rubygen/templates/amqp_0_10.rb +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env ruby -$: << ".." # Include .. in load path -require 'cppgen' - -class Amqp_0_10 < CppGen - - def initialize(outdir, amqp) - super(outdir, amqp) - @namespace="qpid::amqp_0_10" - @filename="qpid/amqp_0_10" - end - - def action_h(a, base) - struct(a.cppname, "public #{base}") { - genl "static const uint8_t CODE=#{a.code};" - a.fields.each { |f| - genl "#{(f.type_.amqp2cpp)} #{f.cppname};" - } - } - end - - def action_cpp(a, base) - - end - - def gen_amqp_0_10_h - h_file("#{@filename}.h") { - namespace(@namespace) { - @amqp.classes.each { |cl| - namespace(cl.cppname) { - struct("ClassInfo") { - genl "static const uint8_t CODE=#{cl.code};" - genl "static const char* NAME;" - } - cl.commands.each { |c| action_h c, "Command"} - cl.controls.each { |c| action_h c, "Control"} - } - } - } - } - end - - def gen_amqp_0_10_cpp - cpp_file("#{@filename}.cpp") { - include @filename - - namespace(@namespace) { - @amqp.classes.each { |cl| - namespace(cl.cppname) { - genl "static const char* ClassInfo::NAME=\"#{cl.name};" - } - } - } - } - end - - def generate - gen_amqp_0_10_h - gen_amqp_0_10_cpp - end -end - -Amqp_0_10.new(Outdir, Amqp).generate(); - diff --git a/qpid/cpp/rubygen/templates/constants.rb b/qpid/cpp/rubygen/templates/constants.rb deleted file mode 100755 index 5fbbefe218..0000000000 --- a/qpid/cpp/rubygen/templates/constants.rb +++ /dev/null @@ -1,82 +0,0 @@ -#!/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/qpid/cpp/rubygen/templates/frame_body_lists.rb b/qpid/cpp/rubygen/templates/frame_body_lists.rb deleted file mode 100644 index 634001ab14..0000000000 --- a/qpid/cpp/rubygen/templates/frame_body_lists.rb +++ /dev/null @@ -1,31 +0,0 @@ -$: << ".." # 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 <