diff options
Diffstat (limited to 'qpid/cpp/rubygen/framing.0-10')
-rwxr-xr-x | qpid/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb | 45 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb | 54 | ||||
-rw-r--r-- | qpid/cpp/rubygen/framing.0-10/MethodBodyFactory.rb | 59 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/framing.0-10/Operations.rb | 121 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/framing.0-10/OperationsInvoker.rb | 117 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/framing.0-10/Proxy.rb | 109 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/framing.0-10/Session.rb | 417 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/framing.0-10/all_method_bodies.rb | 39 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/framing.0-10/constants.rb | 208 | ||||
-rw-r--r-- | qpid/cpp/rubygen/framing.0-10/frame_body_lists.rb | 49 | ||||
-rwxr-xr-x | qpid/cpp/rubygen/framing.0-10/structs.rb | 615 |
11 files changed, 1833 insertions, 0 deletions
diff --git a/qpid/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb b/qpid/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb new file mode 100755 index 0000000000..d784e589df --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb @@ -0,0 +1,45 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +$: << ".." # 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/framing.0-10/MethodBodyDefaultVisitor.rb b/qpid/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb new file mode 100755 index 0000000000..4c58ff2bbb --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb @@ -0,0 +1,54 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +$: << ".." # 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" + include "qpid/CommonImportExport.h" + namespace(@namespace) { + genl "class AMQMethodBody;" + cpp_extern_class("QPID_COMMON_CLASS_EXTERN", @classname, "public MethodBodyConstVisitor") { + genl "public:" + genl "virtual void defaultVisit(const AMQMethodBody&) = 0;" + @amqp.methods_.each { |m| + genl "QPID_COMMON_EXTERN virtual void visit(const #{m.body_name}&);" } + }}} + + cpp_file(@filename) { + include(@filename) + include("qpid/framing/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/framing.0-10/MethodBodyFactory.rb b/qpid/cpp/rubygen/framing.0-10/MethodBodyFactory.rb new file mode 100644 index 0000000000..28a5d94e32 --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/MethodBodyFactory.rb @@ -0,0 +1,59 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +$: << ".." # Include .. in load path +require 'cppgen' + +class MethodBodyFactoryGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + @namespace="qpid::framing" + @classname="MethodBodyFactory" + @filename="qpid/framing/MethodBodyFactory" + end + + def generate() + cpp_file(@filename) { + include @filename + include "qpid/framing/BodyFactory" + @amqp.methods_.each { |m| include "qpid/framing/#{m.body_name}" } + include "qpid/Exception.h" + include "qpid/Msg.h" + genl + namespace(@namespace) { + scope("boost::intrusive_ptr<AMQMethodBody> #{@classname}::create(ClassId c, MethodId m) {") { + scope("switch (c) {") { + @amqp.classes.each { |c| + scope("case #{c.code}: switch(m) {") { + c.methods_.each { |m| + genl "case #{m.code}: return BodyFactory::create<#{m.body_name}>();" + } + 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)));" + } + } + }} + end +end + +MethodBodyFactoryGen.new($outdir, $amqp).generate(); diff --git a/qpid/cpp/rubygen/framing.0-10/Operations.rb b/qpid/cpp/rubygen/framing.0-10/Operations.rb new file mode 100755 index 0000000000..cd6a363c56 --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/Operations.rb @@ -0,0 +1,121 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Usage: output_directory xml_spec_file [xml_spec_file...] +# +$: << '..' +require 'cppgen' +require 'fileutils' +require 'etc' +require 'pathname' + +class OperationsGen < CppGen + + def initialize(chassis, outdir, amqp) + super(outdir, amqp) + @chassis=chassis + @classname="AMQP_#{@chassis.caps}Operations" + end + + def handler_method (m) + return_type = m.result ? m.result.cpptype.ret_by_val : "void" + gen "\nvirtual #{return_type} #{m.cppname}(" + gen m.signature.join(",\n") + gen ") = 0;\n" + end + + def handler_classname(c) c.name.caps+"Handler"; end + + def methods_on(parent, chassis) + chassis == "all" ? parent.methods_ : parent.methods_on(chassis) + end + + def handler_class(c) + m = methods_on(c,@chassis) + if (not m.empty?) + handlerclass=handler_classname c + gen <<EOS +// ==================== class #{handlerclass} ==================== +class #{handlerclass} { + // Constructors and destructors + public: + class Invoker; // Declared in #{@chassis.caps}Invoker + + #{handlerclass}(){}; + virtual ~#{handlerclass}() {} + // Protocol methods +EOS + m.each { |m| handler_method(m) if !m.content() } + gen <<EOS +}; // class #{handlerclass} + + +EOS + end + end + + def handler_get(c) + m = methods_on(c,@chassis) + if (not m.empty?) + handlerclass=handler_classname c + gen "virtual #{handlerclass}* get#{handlerclass}() = 0;\n" + end + end + + def generate() + h_file("qpid/framing/#{@classname}.h") { + gen <<EOS +#include <sstream> +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/framing/amqp_structs.h" + +namespace qpid { +namespace framing { + +class AMQMethodBody; + +class #{@classname} { + public: + class Invoker; // Declared in #{@chassis.caps}Invoker + + virtual ~#{@classname}() {} + + virtual ProtocolVersion getVersion() const = 0; + + // Inner classes +EOS + indent { @amqp.classes.each { |c| handler_class(c) } } + gen <<EOS + + // Method handler get methods + +EOS + indent { @amqp.classes.each { |c| handler_get(c) } } + gen <<EOS +}; /* class #{@classname} */ +}} +EOS +} + end +end + +OperationsGen.new("client",$outdir, $amqp).generate() +OperationsGen.new("server",$outdir, $amqp).generate() +OperationsGen.new("all",$outdir, $amqp).generate() + diff --git a/qpid/cpp/rubygen/framing.0-10/OperationsInvoker.rb b/qpid/cpp/rubygen/framing.0-10/OperationsInvoker.rb new file mode 100755 index 0000000000..f9b5ce58d8 --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/OperationsInvoker.rb @@ -0,0 +1,117 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Usage: output_directory xml_spec_file [xml_spec_file...] +# +$: << '..' +require 'cppgen' + +class OperationsInvokerGen < CppGen + def initialize(chassis, outdir, amqp) + super(outdir, amqp) + @chassis=chassis + @ops="AMQP_#{@chassis.caps}Operations" + @classname="#{@ops}::Invoker" + @filename="qpid/framing/#{@chassis.caps}Invoker" + end + + def methods_on(parent, chassis) + chassis == "all" ? parent.methods_ : parent.methods_on(chassis) + end + + def handler(c) "#{@ops}::#{c.cppname}Handler"; end + def getter(c) "get#{c.cppname}Handler"; end + def invoker(c) "#{handler(c)}::Invoker"; end + def visit_methods(c) methods_on(c, @chassis).select { |m| !m.content } end + + + def handler_visits_cpp(c) + visit_methods(c).each { |m| + scope("void #{invoker(c)}::visit(const #{m.body_name}& body) {") { + if (m.result) + genl "this->encode(body.invoke(target), result.result);" + else + genl "body.invoke(target);" + end + genl "result.handled=true;" + } + } + end + + def ops_visits_cpp() + @amqp.classes.each { |c| + visit_methods(c).each { |m| + scope("void #{@classname}::visit(const #{m.body_name}& body) {") { + genl "#{handler(c)}::Invoker invoker(*target.#{getter(c)}());" + genl "body.accept(invoker);" + genl "result=invoker.getResult();" + } + } + } + end + + def invoker_h(invoker, target, methods) + return if methods.empty? + genl + cpp_extern_class("QPID_COMMON_CLASS_EXTERN", invoker, "public qpid::framing::Invoker") { + genl "#{target}& target;" + public + genl("Invoker(#{target}& target_) : target(target_) {}") + genl "using MethodBodyDefaultVisitor::visit;" + methods.each { |m| genl "QPID_COMMON_EXTERN void visit(const #{m.body_name}& body);" } + } + end + + def generate() + h_file(@filename) { + include "qpid/framing/#{@ops}" + include "qpid/framing/Invoker.h" + include "qpid/CommonImportExport.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",$outdir, $amqp).generate() +OperationsInvokerGen.new("server",$outdir, $amqp).generate() +OperationsInvokerGen.new("all",$outdir, $amqp).generate() diff --git a/qpid/cpp/rubygen/framing.0-10/Proxy.rb b/qpid/cpp/rubygen/framing.0-10/Proxy.rb new file mode 100755 index 0000000000..3325616754 --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/Proxy.rb @@ -0,0 +1,109 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +$: << ".." # 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 methods_on(parent, chassis) + chassis == "all" ? parent.methods_ : parent.methods_on(chassis) + end + + def proxy_member(c) c.name.lcaps+"Proxy"; end + + def inner_class_decl(c) + cname=c.name.caps + cpp_extern_class("QPID_COMMON_CLASS_EXTERN", cname, "public Proxy") { + gen <<EOS +public: +#{cname}(FrameHandler& f) : Proxy(f) {} +static #{cname}& get(#{@classname}& proxy) { return proxy.get#{cname}(); } +EOS + methods_on(c, @chassis).each { |m| + genl "QPID_COMMON_EXTERN virtual void #{m.cppname}(#{m.signature.join(",\n ")});" + genl + }} + end + + def inner_class_defn(c) + cname=c.cppname + methods_on(c, @chassis).each { |m| + genl "void #{@classname}::#{cname}::#{m.cppname}(#{m.signature.join(", ")})" + scope { + params=(["getVersion()"]+m.param_names).join(", ") + genl "send(#{m.body_name}(#{params}));" + }} + end + + def generate + # .h file + h_file(@filename) { + include "qpid/framing/Proxy.h" + include "qpid/framing/Array.h" + include "qpid/framing/amqp_types.h" + include "qpid/framing/amqp_structs.h" + include "qpid/CommonImportExport.h" + + namespace("qpid::framing") { + cpp_extern_class("QPID_COMMON_CLASS_EXTERN", @classname, "public Proxy") { + public + genl "QPID_COMMON_EXTERN #{@classname}(FrameHandler& out);" + genl + @amqp.classes.each { |c| + inner_class_decl(c) + genl + genl "#{c.cppname}& get#{c.cppname}() { return #{proxy_member(c)}; }" + genl + } + private + @amqp.classes.each{ |c| gen c.cppname+" "+proxy_member(c)+";\n" } + }}} + + # .cpp file + cpp_file(@filename) { + include "<sstream>" + include "qpid/framing/#{@classname}.h" + include "qpid/framing/amqp_types_full.h" + methods_on(@amqp, @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; +ProxyGen.new("all", $outdir, $amqp).generate; + diff --git a/qpid/cpp/rubygen/framing.0-10/Session.rb b/qpid/cpp/rubygen/framing.0-10/Session.rb new file mode 100755 index 0000000000..e800df9b2e --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/Session.rb @@ -0,0 +1,417 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Usage: output_directory xml_spec_file [xml_spec_file...] +# +$: << '..' +require 'cppgen' + +class CppGen + def session_methods(sync_default) + excludes = ["connection", "session", "file", "stream"] + gen_methods=@amqp.methods_on(@chassis).reject { |m| + excludes.include? m.parent.name or m.body_name.include?("010") + } + gen_methods.each { |m| m.set_sync_default(sync_default) } + end + + + # Generates a doxygen comment for AmqpMethod m. + 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 + +# Sync vs. async APIs +module SyncAsync + def sync_prefix() @async ? "Async" : "" end + def sync_adjective() @async ? "asynchronous" : "synchronous" end + def sync_convert() @async ? "async" : "sync" end + + + def decl_ctor_opeq() + genl + genl "QPID_CLIENT_EXTERN #{@classname}();" + genl "QPID_CLIENT_INLINE_EXTERN #{@classname}(const #{@version_base}& other);" + genl "QPID_CLIENT_INLINE_EXTERN #{@classname}& operator=(const #{@version_base}& other);" + end + + def defn_ctor_opeq(inline="") + genl + genl "#{inline} #{@classname}::#{@classname}() {}" + scope("#{inline} #{@classname}::#{@classname}(const #{@version_base}& other) {") { + genl "*this = other;" + } + scope("#{inline} #{@classname}& #{@classname}::operator=(const #{@version_base}& other) {") { + genl "impl = static_cast<const #{@classname}&>(other).impl;" + genl "return *this;" + } + end + + def sync_default() !@async end +end + +class ContentField # For extra content parameters + def cppname() "content" end + def signature() "const Message& content" end + def sig_default() signature+"="+"Message(std::string())" end + def unpack() "p[arg::content|Message(std::string())]"; end + def doc() "Message content"; end +end + +class SyncField # For extra sync parameters + def initialize(default_value) @default_value=default_value ? "true" : "false" end + def cppname() "sync" end + def signature() "bool sync" end + def sig_default() signature+"="+@default_value end + def unpack() "p[arg::sync|#{@default_value}]"; end + def doc() "If true the broker will respond with completion status as soon as possible."; end +end + +class AmqpField + def unpack() "p[arg::#{cppname}|#{default_value}]"; end + def sig_default() signature+"="+default_value; end +end + +class AmqpMethod + def set_sync_default(sync_default) @sync_default=sync_default end + def fields_c() result = fields + (content ? [ContentField.new] : []) + [SyncField.new(@sync_default)] 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(async) + if (async) + return "TypedResult<qpid::framing::#{result.cpptype.ret_by_val}>" if (result) + return "Completion" + else + return "qpid::framing::#{result.cpptype.ret_by_val}" if (result) + return "void" + end + end + + def session_function() "#{parent.name.lcaps}#{name.caps}"; end +end + +class SessionNoKeywordGen < CppGen + include SyncAsync + + def initialize(outdir, amqp, async) + super(outdir, amqp) + @async=async + @chassis="server" + @namespace,@classname,@file= + parse_classname "qpid::client::no_keyword::#{sync_prefix}Session_#{@amqp.version.bars}" + @version_base="SessionBase_#{@amqp.major}_#{@amqp.minor}" + end + + def generate() + public_api("#{@file}.h") + h_file(@file) { + include "qpid/client/#{@version_base}.h" + include "qpid/client/ClientImportExport.h" + namespace(@namespace) { + doxygen_comment { + genl "AMQP #{@amqp.version} #{sync_adjective} session API." + genl @amqp.class_("session").doc + # FIXME aconway 2008-05-23: additional doc on sync/async use. + } + cpp_class(@classname, "public #{@version_base}") { + public + decl_ctor_opeq() + session_methods(sync_default).each { |m| + genl + doxygen(m) + args=m.sig_c_default.join(", ") + genl "QPID_CLIENT_EXTERN #{m.return_type(@async)} #{m.session_function}(#{args});" + } + } + }} + + cpp_file(@file) { + include "qpid/client/#{@classname}" + include "qpid/framing/all_method_bodies.h" + include "qpid/client/SessionImpl.h" + include "qpid/client/MessageImpl.h" + include "qpid/client/PrivateImplRef.h" + include "qpid/client/CompletionImpl.h" + include "<boost/intrusive_ptr.hpp>" + namespace(@namespace) { + genl "using namespace framing;" + session_methods(sync_default).each { |m| + genl + sig=m.signature_c.join(", ") + func="#{@classname}::#{m.session_function}" + scope("#{m.return_type(@async)} #{func}(#{sig}) {") { + args=(["ProtocolVersion(#{@amqp.major},#{@amqp.minor})"]+m.param_names).join(", ") + genl "#{m.body_name} body(#{args});"; + genl "body.setSync(sync);" + sendargs="body" + sendargs << ", *MessageImpl::get(content)" if m.content + async_retval="#{m.return_type(true)}(new CompletionImpl(impl->send(#{sendargs}), impl))" + if @async then + genl "return #{async_retval};" + else + if m.result + genl "return #{async_retval}.get();" + else + genl "#{async_retval}.wait();" + end + end + }} + defn_ctor_opeq() + }} + end +end + +class SessionGen < CppGen + include SyncAsync + + def initialize(outdir, amqp, async) + super(outdir, amqp) + @async=async + @chassis="server" + session="#{sync_prefix}Session_#{@amqp.version.bars}" + @base="no_keyword::#{session}" + @fqclass=FqClass.new "qpid::client::#{session}" + @classname=@fqclass.name + @fqbase=FqClass.new("qpid::client::#{@base}") + @version_base="SessionBase_#{@amqp.major}_#{@amqp.minor}" + end + + def gen_keyword_decl(m) + return if m.fields_c.empty? # Inherited function will do. + scope("BOOST_PARAMETER_MEMFUN(#{m.return_type(@async)}, #{m.session_function}, 0, #{m.fields_c.size}, #{m.argpack_name}) {") { + scope("return #{@base}::#{m.session_function}(",");") { + gen m.fields_c.map { |f| f.unpack() }.join(",\n") + } + } + genl + end + + def generate() + keyword_methods=session_methods(sync_default).reject { |m| m.fields_c.empty? } + max_arity = keyword_methods.map{ |m| m.fields_c.size }.max + + public_api("qpid/client/arg.h") + h_file("qpid/client/arg.h") { + # Generate keyword tag declarations. + genl "#define BOOST_PARAMETER_MAX_ARITY #{max_arity}" + include "<boost/parameter.hpp>" + namespace("qpid::client::arg") { + keyword_methods.map{ |m| m.param_names_c }.flatten.uniq.each { |k| + genl "BOOST_PARAMETER_KEYWORD(keyword_tags, #{k})" + }} + } + public_api("#{@fqclass.file}.h") + h_file(@fqclass.file) { + include @fqbase.file + include "qpid/client/arg" + include "qpid/client/ClientImportExport" + namespace("qpid::client") { + # Doxygen comment. + doxygen_comment { + genl "AMQP #{@amqp.version} session API with keyword arguments." + genl <<EOS +This class provides the same set of functions as #{@base}, but also +allows parameters be passed using keywords. The keyword is the +parameter name in the namespace "arg". + +For example given the normal function "foo(int x=0, int y=0, int z=0)" +you could call it in either of the following ways: + +@code +session.foo(1,2,3); // Normal no keywords +session.foo(arg::z=3, arg::x=1); // Keywords and a default +@endcode + +The keyword functions are easy to use but their declarations are hard +to read. You may find it easier to read the documentation for #{@base} +which provides the same set of functions using normal non-keyword +declarations. + +\\ingroup clientapi + + +\\details + +<h2>Publishing Messages</h2> +<ul> +<li><p>messageTransfer()</p> +<pre>session.messageTransfer(arg::content=message, arg::destination="amq.topic");</pre></li> +<li><p>messageTransfer() — asynchronous</p> +<pre>#include <qpid/client/AsyncSession.h> + +for (int i=0; i<10; i++) { + message.setData(message_data.str()); + async(session).messageTransfer(arg::content=message, arg::destination="amq.direct"); +} + +session.sync(); +</pre> +</li> +</ul> + +<h2>Exchanges</h2> +<ul> +<li><p>exchangeBind()</p> +<pre>session.exchangeBind(arg::exchange="amq.topic", arg::queue=queue, arg::bindingKey=routing_key);</pre> +</li> +<li><p>exchangeUnbind()</p> +<pre>session.exchangeUnBind(arg::exchange="amq.topic", arg::queue=queue, arg::bindingKey=routing_key);</pre></li> +<li><p>exchangeBound()</p> +<pre>if (session.exchangeBound(arg::exchange="amq.topic", arg::queue=queue, arg::bindingKey=rk)){...}</pre> +<pre>if (session.exchangeBound(arg::exchange="amq.topic", arg::queue=queue)){...}</pre> +</li> +<li><p>exchangeDeclare()</p> +<pre>session.exchangeDeclare(arg::exchange="my.topic", arg::type="topic");</pre> +<pre>session.exchangeDeclare(arg::exchange="xml", arg::type="xml");</pre> +</li> +<li><p>exchangeDelete()</p> +<pre>session.exchangeDeclare(arg::exchange="my.topic");</pre> +<pre>session.exchangeDeclare(arg::exchange="xml", arg::ifUnused=true);</pre> +</li> +<li><p>exchangeQuery()</p> +<pre>ExchangeQueryResult eqr = session.exchangeQuery(arg::exchange="my.topic");</pre></li> +</ul> + + +<h2>Configuring exchanges in session.exchangeDeclare</h2> + +<pre>arg::durable=true</pre> +<p>Default: false.</p> +<p>If durable=true, an exchange remains active even if the server is restarted. If durable=false, an exchange is purged when a server restarts.</p> + +<pre>arg::autoDelete=true</pre> +<p>Default: false.</p> +<p>If autoDelete=true, deleting the last binding for an exchange also deletes the exchange.</p> + +<pre>arg::alternatExchange="my.exchange"</pre> +<p>Default: none.</p> +<p>If an alternate exchange is specified, messages that can not be delivered to any queue are sent to the alternate exchange.</p> + +<h2>Queues</h2> +<ul> +<li><p>queueDeclare()</p> +<pre>session.queueDeclare(arg::queue="message_queue");</pre> +</li> +<li><p>queueDelete()</p> +<pre>session.queueDelete(arg::queue="message_queue");</pre></li> +<li><p>queuePurge()</p> +<pre>session.queuePurge(arg::queue="message_queue");</pre></li> +<li><p>queueQuery()</p> +<pre>QueueQueryResult qqr = session.queueQuery(arg::queue="message_queue");</pre></li> +</ul> + + +<h2>Configuring queues with session.queueDeclare</h2> +<pre>arg::durable=true</pre> +<p>Default: false.</p> +<p>If durable=true, a queue remains active if the server is restarted. If durable=false, a queue and its contents are lost when a server restarts.</p> +<br/> + +<pre>arg::autoDelete=true</pre> +<p>Default: false.</p> +<p>If autoDelete=true, the queue is deleted when the last active Subscription to the Queue is canceled.</p> +<br/> + +<pre>arg::exclusive=true</pre> +<p>Default: false.</p> +<p>If exclusive=true, only the Session that created a queue can access it.</p> +<br/> + +<pre>arg::alternateExchange="my.exchange"</pre> +<p>Default: none. </p> +<p>If an alternate exchange is specified, messages are routed to it if (1) they are rejected by a client, or (2) they remain on the queue when it is deleted.</p> +<br/> + + +<h2>Accepting, Acquiring, Rejecting, or Releasing Messages</h2> +<ul> +<li><p>messageAccept() — acknowledges messages</p> +<pre>SequenceSet tobeAccepted; +toAccepted.add(msg.getId()); +session.messageAccept(toBeAccepted);</pre> +</li> +<li><p>messageAcquire()</p> +<pre>SequenceSet tobeAcquired; +toBeAcquired.add(msg.getId()); +session.messageAcquire(toBeAcquired);</pre> +</li> +<li><p>messageReject()</p> +<pre>SequenceSet tobeRejected; +toRejected.add(msg.getId()); +session.messageReject(toBeRejected);</pre> +</li> +<li><p>messageRelease()</p> +<pre>SequenceSet tobeReleased; +toReleased.add(msg.getId()); +session.messageRelease(toBeReleased);</pre></li> +</ul> + +<h2>Transactions</h2> +<ul> +<li><p>txSelect()</p> +<pre>session.txSelect();</pre> +</li> +<li><p>txCommit()</p> +<pre>session.txSelect();</pre></li> +<li><p>txRollback()</p> +<pre>session.txRollback();</pre></li> +</ul> + + +EOS + } + # Session class. + cpp_class(@classname,"public #{@base}") { + public + decl_ctor_opeq() + private + keyword_methods.each { |m| typedef m.argpack_type, m.argpack_name } + genl "friend class Connection;" + public + keyword_methods.each { |m| gen_keyword_decl(m) } + } + genl "/** Conversion to #{@classname} from another session type */" + genl "inline #{@classname} #{sync_convert}(const #{@version_base}& other) { return #{@clasname}(other); }" + defn_ctor_opeq("inline") + }} + end +end + +SessionNoKeywordGen.new($outdir, $amqp, true).generate() +SessionNoKeywordGen.new($outdir, $amqp, false).generate() +SessionGen.new($outdir, $amqp, true).generate() +SessionGen.new($outdir, $amqp, false).generate() + diff --git a/qpid/cpp/rubygen/framing.0-10/all_method_bodies.rb b/qpid/cpp/rubygen/framing.0-10/all_method_bodies.rb new file mode 100755 index 0000000000..4c7fccfff5 --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/all_method_bodies.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +$: << ".." # 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/framing.0-10/constants.rb b/qpid/cpp/rubygen/framing.0-10/constants.rb new file mode 100755 index 0000000000..85bfb96ac0 --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/constants.rb @@ -0,0 +1,208 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +$: << ".." # 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() + public_api("#{@dir}/constants.h") + h_file("#{@dir}/constants.h") { + namespace(@namespace) { + # Constants for class/method names. + scope("enum AmqpConstant {","};") { + l=[] + l.concat @amqp.constants.map { |c| "#{c.name.shout}=#{c.value}" } + @amqp.classes.each { |c| + l << "#{c.name.shout}_CLASS_ID=#{c.code}" + l.concat c.methods_.map { |m| + "#{c.name.shout}_#{m.name.shout}_METHOD_ID=#{m.code}" } + } + genl l.join(",\n") + } + } + } + end + + def typecode_enum(t) "TYPE_CODE_#{t.name.shout}" end + + def typecode_h_cpp + path="#{@dir}/TypeCode" + public_api(path+".h") + h_file(path) { + include("<iosfwd>") + include("\"qpid/sys/IntegerTypes.h\"") + namespace(@namespace) { + scope("enum TypeCode {", "};") { + genl @amqp.types.map { |t| "#{typecode_enum t} = #{t.code}" if t.code }.compact.join(",\n") + } + genl <<EOS + +/** True if t is a valid TypeCode value */ +bool isTypeCode(uint8_t t); + +/** Throw exception if not a valid TypeCode */ +TypeCode typeCode(uint8_t); + +/**@return 0 if t is not a valid enum TypeCode value. */ +const char* typeName(TypeCode t); + +std::ostream& operator<<(std::ostream&, TypeCode); +EOS + } + } + + cpp_file(path) { + include(path); + include("qpid/Exception.h") + include("qpid/Msg.h") + include("<ostream>") + namespace(@namespace) { + scope("const char* typeName(TypeCode t) {") { + scope("switch (t) {") { + @amqp.types.each { |t| genl "case #{typecode_enum t}: return \"#{t.name}\";" if t.code } + genl "default: break;" + } + genl "return 0;"; + } + genl <<EOS + +bool isTypeCode(uint8_t t) { return typeName(TypeCode(t)); } + +TypeCode typeCode(uint8_t t) { + if (!isTypeCode(t)) throw Exception(QPID_MSG("Invalid TypeCode " << t)); + return TypeCode(t); +} + +std::ostream& operator<<(std::ostream& o, TypeCode t) { + if (isTypeCode(t)) return o << typeName(t); + else return o << "Invalid TypeCode " << t; +} +EOS + } + } + end + + def enum_h() + public_api("#{@dir}/enum.h") + h_file("#{@dir}/enum.h") { + # Constants for enum domains. + namespace(@namespace) { + @amqp.domains.each { |d| declare_enum(d.enum) if d.enum } + @amqp.classes.each { |c| + enums=c.collect_all(AmqpEnum) + if !enums.empty? then + namespace(c.nsname) { enums.each { |e| declare_enum(e) } } + end + } + } + } + end + + def declare_enum(enum) + # Generated like this: enum containing_class::Foo { FOO_X, FOO_Y; } + name="#{enum.parent.name.caps}" + prefix=enum.parent.name.shout+"_" + scope("enum #{name} {","};") { + genl enum.choices.collect { |c| "#{prefix}#{c.name.shout}=#{c.value}" }.join(",\n") + } + end + + def declare_exception(c, base, package, enum) + name=c.name.caps+"Exception" + value="#{package}::#{enum.parent.name.shout}_#{c.name.shout}" + genl + doxygen_comment { genl c.doc } + struct(c.name.caps+"Exception", base) { + genl "std::string getPrefix() const { return \"#{c.name}\"; }" + genl "#{c.name.caps}Exception(const std::string& msg=std::string()) : #{base}(#{value}, \"\"+msg) {}" + } + end + + def declare_exceptions(class_name, domain_name, base) + enum = @amqp.class_(class_name).domain(domain_name).enum + enum.choices.each { |c| declare_exception(c, base, class_name, enum) unless c.name == "normal" } + genl + genl "QPID_COMMON_EXTERN sys::ExceptionHolder create#{base}(int code, const std::string& text);" + end + + def create_exception(class_name, domain_name, base, invalid) + scope("sys::ExceptionHolder create#{base}(int code, const std::string& text) {") { + genl "sys::ExceptionHolder holder;" + scope("switch (code) {") { + enum = @amqp.class_(class_name).domain(domain_name).enum + enum.choices.each { |c| + assign = "holder = new #{c.name.caps}Exception(text); " unless c.name == "normal" + genl "case #{c.value}: #{assign}break;" + } + genl "default: holder = new #{invalid}(QPID_MSG(\"Bad #{enum.parent.name}: \" << code << \": \" << text));" + } + genl "return holder;" + } + end + + def reply_exceptions_h() + public_api("#{@dir}/reply_exceptions.h") + h_file("#{@dir}/reply_exceptions.h") { + include "qpid/Exception" + include "qpid/sys/ExceptionHolder" + include "qpid/framing/enum" + include "qpid/CommonImportExport.h" + namespace(@namespace) { + declare_exceptions("execution", "error-code", "SessionException") + declare_exceptions("connection", "close-code", "ConnectionException") + declare_exceptions("session", "detach-code", "ChannelException") + } + } + end + + def reply_exceptions_cpp() + cpp_file("#{@dir}/reply_exceptions") { + include "#{@dir}/reply_exceptions" + include "qpid/Msg.h" + include "<sstream>" + include "<assert.h>" + namespace("qpid::framing") { + create_exception("execution", "error-code", "SessionException", "InvalidArgumentException") + # FIXME aconway 2008-10-07: there are no good exception codes in 0-10 for an invalid code. + # The following choices are arbitrary. + create_exception("connection", "close-code", "ConnectionException", "FramingErrorException") + create_exception("session", "detach-code", "ChannelException", "NotAttachedException") + } + } + end + + def generate() + constants_h + enum_h + reply_exceptions_h + reply_exceptions_cpp + typecode_h_cpp + end +end + +ConstantsGen.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/framing.0-10/frame_body_lists.rb b/qpid/cpp/rubygen/framing.0-10/frame_body_lists.rb new file mode 100644 index 0000000000..4f1b976032 --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/frame_body_lists.rb @@ -0,0 +1,49 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +$: << ".." # Include .. in load path +require 'cppgen' + +class FrameBodyListsGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp); + end + + def generate + h_file("qpid/framing/frame_body_lists.h") { + gen <<EOS +/**@file + * Macro lists of frame body classes, used to generate Visitors + */ +EOS + gen "#define METHOD_BODIES() " + @amqp.methods_.each { |m| gen "\\\n (#{m.body_name}) " } + gen <<EOS + + +#define OTHER_BODIES() (AMQContentBody)(AMQHeaderBody)(AMQHeartbeatBody)) + +EOS + } + end +end + +FrameBodyListsGen.new($outdir, $amqp).generate; + + diff --git a/qpid/cpp/rubygen/framing.0-10/structs.rb b/qpid/cpp/rubygen/framing.0-10/structs.rb new file mode 100755 index 0000000000..62b33ce773 --- /dev/null +++ b/qpid/cpp/rubygen/framing.0-10/structs.rb @@ -0,0 +1,615 @@ +#!/usr/bin/env ruby +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Usage: output_directory xml_spec_file [xml_spec_file...] +# +$: << '..' +require 'cppgen' + +class StructGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + end + + SizeMap={ + "Octet"=>1, + "Short"=>2, + "Long"=>4, + "LongLong"=>8, + "int8"=>1, + "int16"=>2, + "int32"=>4, + "int64"=>8, + "uint8"=>1, + "uint16"=>2, + "uint32"=>4, + "uint64"=>8, + "timestamp"=>8 + } + + StringSizeMap={ + "LongString"=>4, + "MediumString"=>2, + "ShortString"=>1 + } + + SizeType={ + 1=>"Octet", + 2=>"Short", + 4=>"Long", + 8=>"LongLong" + } + + ValueTypes=["uint8_t", "uint16_t", "uint32_t", "uint64_t"] + + def is_packed(s) s.pack and s.pack != "0" end + + def execution_header?(s) + s.is_a? AmqpMethod and not s.parent.control? + # s.kind_of? AmqpMethod and s.parent.name.include?("010") and not s.parent.control? + end + + def has_bitfields_only(s) + s.fields.select {|f| f.type_ != "bit"}.empty? + end + + def default_initialisation(s) + params = s.fields.select {|f| ValueTypes.include?(f.cpptype.name) || (!is_packed(s) && f.type_ == "bit")} + strings = params.collect {|f| "#{f.cppname}(#{f.default_value})"} + strings << "flags(0)" if (is_packed(s)) + if strings.empty? + return "" + else + return " : " + strings.join(", ") + end + end + + def printable_form(f) + if (f.cpptype.name == "uint8_t") + return "(int) " + f.cppname + elsif (f.type_ == "bit") + return "get#{f.name.caps}()" + else + return f.cppname + end + end + + def flag_mask(s, i) + pos = s.pack.to_i*8 - 8 - (i/8)*8 + (i % 8) + return "(1 << #{pos})" + end + + def encode_packed_struct(s) + genl s.cpp_pack_type.encode('flags', 'buffer') + process_packed_fields(s) { |f, i| encode_packed_field(s, f, i) unless f.type_ == "bit" } + end + + def decode_packed_struct(s) + genl "#{s.cpp_pack_type.decode('flags', 'buffer')}" + process_packed_fields(s) { |f, i| decode_packed_field(s, f, i) unless f.type_ == "bit" } + end + + def size_packed_struct(s) + genl "total += #{s.pack};" + process_packed_fields(s) { |f, i| size_packed_field(s, f, i) unless f.type_ == "bit" } + end + + def print_packed_struct(s) + process_packed_fields(s) { |f, i| print_packed_field(s, f, i) } + end + + def encode_packed_field(s, f, i) + genl "if (flags & #{flag_mask(s, i)})" + indent { genl f.cpptype.encode(f.cppname,"buffer") } + end + + def decode_packed_field(s, f, i) + genl "if (flags & #{flag_mask(s, i)})" + indent { genl f.cpptype.decode(f.cppname,"buffer") } + end + + def size_packed_field(s, f, i) + genl "if (flags & #{flag_mask(s, i)})" + indent { generate_size(f, []) } + end + + def print_packed_field(s, f, i) + classname = s.cppname + if (s.kind_of? AmqpMethod) + classname = s.body_name + end + genl "if (flags & #{flag_mask(s, i)})" + indent { + unless (classname == "ConnectionStartOkBody" && f.name == "response") + genl "out << \"#{f.name}=\" << #{printable_form(f)} << \"; \";" + else + genl "out << \"response=\" << \"xxxxxx\" << \"; \";" + end + } + end + + def generate_encode(f, combined) + if (f.type_ == "bit") + genl "uint8_t #{f.cppname}_bits = #{f.cppname};" + count = 0 + combined.each { |c| genl "#{f.cppname}_bits |= #{c.cppname} << #{count += 1};" } + genl "buffer.putOctet(#{f.cppname}_bits);" + else + genl f.cpptype.encode(f.cppname,"buffer") + end + end + + def generate_decode(f, combined) + if (f.type_ == "bit") + genl "uint8_t #{f.cppname}_bits = buffer.getOctet();" + genl "#{f.cppname} = 1 & #{f.cppname}_bits;" + count = 0 + combined.each { |c| genl "#{c.cppname} = (1 << #{count += 1}) & #{f.cppname}_bits;" } + else + genl f.cpptype.decode(f.cppname,"buffer") + end + end + + def generate_size(f, combined) + if (f.type_ == "bit") + names = ([f] + combined).collect {|g| g.cppname} + genl "total += 1;//#{names.join(", ")}" + else + size = SizeMap[f.cpptype.encoded] + if (size) + genl "total += #{size};//#{f.cppname}" + elsif (f.cpptype.name == "SequenceNumberSet") + genl "total += #{f.cppname}.encodedSize();" + elsif (size = StringSizeMap[f.cpptype.encoded]) + genl "total += #{size} + #{f.cppname}.size();" + else + genl "total += #{f.cppname}.encodedSize();" + end + end + end + + def process_packed_fields(s) + s.fields.each { |f| yield f, s.fields.index(f) } + end + + def process_fields(s) + last = nil + count = 0 + bits = [] + s.fields.each { + |f| if (last and last.bit? and f.bit? and count < 7) + count += 1 + bits << f + else + if (last and last.bit?) + yield last, bits + count = 0 + bits = [] + end + if (not f.bit?) + yield f + end + last = f + end + } + if (last and last.bit?) + yield last, bits + end + end + + def all_fields_via_accessors(s) + s.fields.collect { |f| "get#{f.name.caps}()" }.join(", ") + end + + def methodbody_extra_defs(s) + if (s.parent.control?) + genl "virtual uint8_t type() const { return 0;/*control segment*/ }" + end + + + gen <<EOS + typedef #{s.result ? s.result.cpptype.name : 'void'} ResultType; + + template <class T> ResultType invoke(T& invocable) const { + return invocable.#{s.cppname}(#{all_fields_via_accessors(s)}); + } + + using AMQMethodBody::accept; + void accept(MethodBodyConstVisitor& v) const { v.visit(*this); } + boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); } + + ClassId amqpClassId() const { return CLASS_ID; } + MethodId amqpMethodId() const { return METHOD_ID; } + bool isContentBearing() const { return #{s.content ? "true" : "false" }; } + bool resultExpected() const { return #{s.result ? "true" : "false"}; } + bool responseExpected() const { return #{s.responses().empty? ? "false" : "true"}; } +EOS + end + + def define_constructor(name, s) + if (s.fields.size > 0) + genl "#{name}(" + if (s.kind_of? AmqpMethod) + indent {gen "ProtocolVersion, "} + end + indent { gen s.fields.collect { |f| "#{f.cpptype.param} _#{f.cppname}" }.join(",\n") } + genl ") : " + if (is_packed(s)) + initialisers = s.fields.select { |f| f.type_ != "bit"}.collect { |f| "#{f.cppname}(_#{f.cppname})"} + + initialisers << "flags(0)" + indent { gen initialisers.join(",\n") } + genl "{" + indent { + process_packed_fields(s) { |f, i| genl "set#{f.name.caps}(_#{f.cppname});" if f.type_ == "bit"} + process_packed_fields(s) { |f, i| genl "flags |= #{flag_mask(s, i)};" unless f.type_ == "bit"} + } + genl "}" + else + indent { gen s.fields.collect { |f| " #{f.cppname}(_#{f.cppname})" }.join(",\n") } + genl "{}" + end + end + #default constructors: + if (s.kind_of? AmqpMethod) + genl "#{name}(ProtocolVersion=ProtocolVersion()) #{default_initialisation(s)} {}" + end + if (s.kind_of? AmqpStruct) + genl "#{name}() #{default_initialisation(s)} {}" + end + end + + def define_packed_field_accessors(s, f, i) + if (s.kind_of? AmqpMethod) + define_packed_field_accessors_for_method(s, f, i) + else + define_packed_field_accessors_for_struct(s, f, i) + end + end + + def define_packed_field_accessors_for_struct(s, f, i) + if (f.type_ == "bit") + genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" + indent { + genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};" + genl "else flags &= ~#{flag_mask(s, i)};" + } + genl "}" + genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" + else + genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" + indent { + genl "#{f.cppname} = _#{f.cppname};" + genl "flags |= #{flag_mask(s, i)};" + } + genl "}" + genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return #{f.cppname}; }" + if (f.cpptype.name == "FieldTable") + genl "#{f.cpptype.name}& #{s.cppname}::get#{f.name.caps}() {" + indent { + genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set' + genl "return #{f.cppname};" + } + genl "}" + end + genl "bool #{s.cppname}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" + genl "void #{s.cppname}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }" + end + genl "" + end + + def define_packed_field_accessors_for_method(s, f, i) + if (f.type_ == "bit") + genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" + indent { + genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};" + genl "else flags &= ~#{flag_mask(s, i)};" + } + genl "}" + genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" + else + genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" + indent { + genl "#{f.cppname} = _#{f.cppname};" + genl "flags |= #{flag_mask(s, i)};" + } + genl "}" + genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return #{f.cppname}; }" + if (f.cpptype.name == "FieldTable") + genl "#{f.cpptype.name}& #{s.body_name}::get#{f.name.caps}() {" + indent { + genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set' + genl "return #{f.cppname};" + } + genl "}" + end + genl "bool #{s.body_name}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" + genl "void #{s.body_name}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }" + end + genl "" + end + + def define_packed_accessors(s) + process_packed_fields(s) { |f, i| define_packed_field_accessors(s, f, i) } + end + + def declare_packed_accessors(f) + genl "QPID_COMMON_EXTERN void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname});"; + genl "QPID_COMMON_EXTERN #{f.cpptype.ret} get#{f.name.caps}() const;" + if (f.cpptype.name == "FieldTable") + genl "QPID_COMMON_EXTERN #{f.cpptype.name}& get#{f.name.caps}();" + end + if (f.type_ != "bit") + #extra 'accessors' for packed fields: + genl "QPID_COMMON_EXTERN bool has#{f.name.caps}() const;" + genl "QPID_COMMON_EXTERN 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 + else + public_api("qpid/framing/#{classname}.h") # Non-method structs are public + end + + h_file("qpid/framing/#{classname}.h") { + if (s.kind_of? AmqpMethod) + gen <<EOS +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/MethodBodyConstVisitor.h" +EOS + end + include "qpid/framing/ModelMethod.h" if (execution_header?(s)) + + s.fields.each { |f| include "qpid/framing/#{f.cpptype.name}" if f.struct?} + + gen <<EOS + +#include <ostream> +#include "qpid/framing/amqp_types_full.h" +#include "qpid/CommonImportExport.h" + +namespace qpid { +namespace framing { + +class QPID_COMMON_CLASS_EXTERN #{classname} #{inheritance} { +EOS + if (is_packed(s)) + indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" unless f.type_ == "bit"} } + indent { + genl "#{s.cpp_pack_type.name} flags;" + } + else + indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" } } + end + genl "public:" + if (s.kind_of? AmqpMethod) + indent { genl "static const ClassId CLASS_ID = #{s.parent.code};" } + indent { genl "static const MethodId METHOD_ID = #{s.code};" } + end + + if (s.kind_of? AmqpStruct) + if (s.code) + indent { genl "static const uint16_t TYPE = #{s.full_code};" } + end + end + + indent { + define_constructor(classname, s) + genl "" + if (is_packed(s)) + s.fields.each { |f| declare_packed_accessors(f) } + else + s.fields.each { |f| define_accessors(f) } + end + } + if (s.kind_of? AmqpMethod) + methodbody_extra_defs(s) + end + if (s.kind_of? AmqpStruct) + indent {genl "QPID_COMMON_EXTERN friend std::ostream& operator<<(std::ostream&, const #{classname}&);" } + end + + gen <<EOS + QPID_COMMON_EXTERN void encode(Buffer&) const; + QPID_COMMON_EXTERN void decode(Buffer&, uint32_t=0); + QPID_COMMON_EXTERN void encodeStructBody(Buffer&) const; + QPID_COMMON_EXTERN void decodeStructBody(Buffer&, uint32_t=0); + QPID_COMMON_EXTERN uint32_t encodedSize() const; + QPID_COMMON_EXTERN uint32_t bodySize() const; + QPID_COMMON_EXTERN void print(std::ostream& out) const; +}; /* class #{classname} */ + +}} +EOS + } + cpp_file("qpid/framing/#{classname}.cpp") { + if (is_packed(s) || s.fields.size > 0 || execution_header?(s)) + buffer = "buffer" + else + buffer = "/*buffer*/" + end + gen <<EOS +#include "qpid/framing/#{classname}.h" +#include "qpid/framing/reply_exceptions.h" + +using namespace qpid::framing; + +EOS + + if (is_packed(s)) + define_packed_accessors(s) + end + gen <<EOS +void #{classname}::encodeStructBody(Buffer& #{buffer}) const +{ +EOS + if (execution_header?(s)) + genl "encodeHeader(buffer);" + end + + if (is_packed(s)) + indent {encode_packed_struct(s)} + else + indent { process_fields(s) { |f, combined| generate_encode(f, combined) } } + end + gen <<EOS +} + +void #{classname}::encode(Buffer& buffer) const +{ +EOS + indent { + if (s.kind_of? AmqpStruct) + if (s.code) + genl "buffer.put#{SizeType[s.size.to_i]}(bodySize() + 2/*typecode*/);" if s.size and s.size.to_i != 0 + genl "buffer.putShort(TYPE);" + else + genl "buffer.put#{SizeType[s.size.to_i]}(bodySize());" if s.size and s.size.to_i != 0 + end + end + genl "encodeStructBody(buffer);" + } + gen <<EOS +} + +void #{classname}::decodeStructBody(Buffer& #{buffer}, uint32_t /*size*/) +{ +EOS + if (execution_header?(s)) + genl "decodeHeader(buffer);" + end + + if (is_packed(s)) + indent {decode_packed_struct(s)} + else + indent { process_fields(s) { |f, combined| generate_decode(f, combined) } } + end + gen <<EOS +} + +void #{classname}::decode(Buffer& buffer, uint32_t /*size*/) +{ +EOS + indent { + if (s.kind_of? AmqpStruct) + genl "buffer.get#{SizeType[s.size.to_i]}();" if s.size and s.size.to_i != 0 + genl "if (TYPE != buffer.getShort()) throw FramingErrorException(\"Bad type code for struct\");" if s.code + end + genl "decodeStructBody(buffer);" + } + gen <<EOS +} + +uint32_t #{classname}::bodySize() const +{ + uint32_t total = 0; +EOS + if (execution_header?(s)) + genl "total += headerSize();" + end + + if (is_packed(s)) + indent {size_packed_struct(s)} + else + indent { process_fields(s) { |f, combined| generate_size(f, combined) } } + end + gen <<EOS + return total; +} + +uint32_t #{classname}::encodedSize() const { + uint32_t total = bodySize(); +EOS + if (s.kind_of? AmqpStruct) + genl "total += #{s.size}/*size field*/;" if s.size + genl "total += 2/*typecode*/;" if s.code + end + gen <<EOS + return total; +} + +void #{classname}::print(std::ostream& out) const +{ + out << "{#{classname}: "; +EOS + if (is_packed(s)) + indent {print_packed_struct(s)} + else + copy = Array.new(s.fields) + f = copy.shift + + indent { + genl "out << \"#{f.name}=\" << #{printable_form(f)};" if f + copy.each { |f| genl "out << \"; #{f.name}=\" << #{printable_form(f)};" } + } + end + gen <<EOS + out << "}"; +} +EOS + + if (s.kind_of? AmqpStruct) + gen <<EOS +namespace qpid{ +namespace framing{ + + std::ostream& operator<<(std::ostream& out, const #{classname}& s) + { + s.print(out); + return out; + } + +} +} +EOS + end +} + end + + def generate() + structs = @amqp.collect_all(AmqpStruct).select { |s| not ["command-fragment"].include?(s.name) } + structs.each { |s| define_struct(s) } + @amqp.methods_.each { |m| define_struct(m) } + #generate a single include file containing the list of structs for convenience + public_api("qpid/framing/amqp_structs.h") + h_file("qpid/framing/amqp_structs.h") { structs.each { |s| genl "#include \"qpid/framing/#{s.cppname}.h\"" } } + end +end + +StructGen.new($outdir, $amqp).generate() + |