summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Conway <aconway@apache.org>2007-08-07 22:28:06 +0000
committerAlan Conway <aconway@apache.org>2007-08-07 22:28:06 +0000
commita45694048d1f26e0ed317f661b464bae862fb8fa (patch)
tree53f35cd73f71b9c4979907fd3dd148562c1b6bb0
parent0eb57a7b573a8948ed8bf7187a4a23907bc6c3d2 (diff)
downloadqpid-python-a45694048d1f26e0ed317f661b464bae862fb8fa.tar.gz
* Summary: new Frame type to replace AMQFrame. Instead of holding
a shared_ptr to a heap-allocated AMQBody subclass, it holds the body in-line in a boost::variant of all the concrete AMQBody subclasses. Actually there are nested variants, the compiler does not cope well with a single variant of 130-some types. Creating, encoding and decoding a local Frame doess 0 heap allocation apart from that done by the concrete AMQBody::encode/decode - e.g. method bodies with std::string fields. for method bodies All variants contain type boost::blank. This guarantees 0 heap alloocation by the variant and represents the "uninitialized" state. variant.h provides NoBlankVisitor to help write visitors for variants containing blank. * src/qpid/framing/MethodHolder.h, .cpp: Holds a variant containing a method body. * src/qpid/framing/Frame.h, .cpp: New Frame holds body in a variant rather than via heap allocation. * src/qpid/framing/variant.h: Utilities for using boost::variant. * src/qpid/framing/amqp_types.h: Added FrameType typedef. * src/qpid/framing/AMQMethodBody.h: Friends with MethodHolder. * src/Makefile.am: - Improved ruby generation rule. - Run method_variants template. - Added new source files - Pre-compiled header rule for method_variants.h * rubygen/templates/method_variants.rb: Generate variants to hold methods of each class, and MethodVariant to hold all the class variants. * rubygen/cppgen.rb: variant, tuple methods. * MethodBodyClass.h.tmpl: Added default constructor to method bodies. * amqpgen.rb (AmqpRoot::merge): fix bug in merge. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@563683 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--cpp/gentools/templ.cpp/MethodBodyClass.h.tmpl2
-rwxr-xr-xcpp/rubygen/amqpgen.rb2
-rwxr-xr-xcpp/rubygen/cppgen.rb15
-rwxr-xr-xcpp/rubygen/templates/method_variants.rb72
-rw-r--r--cpp/src/Makefile.am31
-rw-r--r--cpp/src/qpid/framing/AMQMethodBody.h1
-rw-r--r--cpp/src/qpid/framing/Frame.cpp119
-rw-r--r--cpp/src/qpid/framing/Frame.h72
-rw-r--r--cpp/src/qpid/framing/MethodHolder.cpp93
-rw-r--r--cpp/src/qpid/framing/MethodHolder.h80
-rw-r--r--cpp/src/qpid/framing/amqp_types.h1
-rw-r--r--cpp/src/qpid/framing/variant.h92
-rw-r--r--cpp/src/tests/Frame.cpp77
-rw-r--r--cpp/src/tests/Makefile.am9
14 files changed, 653 insertions, 13 deletions
diff --git a/cpp/gentools/templ.cpp/MethodBodyClass.h.tmpl b/cpp/gentools/templ.cpp/MethodBodyClass.h.tmpl
index aeabd8a256..605b09ac94 100644
--- a/cpp/gentools/templ.cpp/MethodBodyClass.h.tmpl
+++ b/cpp/gentools/templ.cpp/MethodBodyClass.h.tmpl
@@ -63,7 +63,7 @@ public:
${mb_constructor_with_initializers}
- ${CLASS}${METHOD}Body(ProtocolVersion version): ${mb_base_class}(version) {}
+ ${CLASS}${METHOD}Body(ProtocolVersion version=ProtocolVersion() ): ${mb_base_class}(version) {}
virtual ~${CLASS}${METHOD}Body() {}
// Attribute get methods
diff --git a/cpp/rubygen/amqpgen.rb b/cpp/rubygen/amqpgen.rb
index 0da1bfe824..31f7544011 100755
--- a/cpp/rubygen/amqpgen.rb
+++ b/cpp/rubygen/amqpgen.rb
@@ -155,7 +155,7 @@ class AmqpRoot < AmqpElement
from.elements.each { |from_child|
tag,name = from_child.name, from_child.attributes["name"]
to_child=to.elements["./#{tag}[@name='#{name}']"]
- to_child ? merge(to_child, from_child) : to.add(from_child.clone) }
+ to_child ? merge(to_child, from_child) : to.add(from_child.deep_clone) }
end
private :merge
diff --git a/cpp/rubygen/cppgen.rb b/cpp/rubygen/cppgen.rb
index d369d23da1..371cc7cd05 100755
--- a/cpp/rubygen/cppgen.rb
+++ b/cpp/rubygen/cppgen.rb
@@ -128,6 +128,7 @@ class CppGen < Generator
# Write a .cpp file.
def cpp_file(path)
+ path = (/\.cpp$/ === path ? path : path+".cpp")
file(path) do
gen Copyright
yield
@@ -157,14 +158,18 @@ class CppGen < Generator
genl
gen "#{type} #{name}"
gen ": #{bases.join(', ')}" unless bases.empty?
- genl "{"
- yield
- genl "};"
- genl
+ scope(" {","};", &block)
end
def struct(name, *bases, &block) struct_class("struct", name, bases, &block); end
def class_(name, *bases, &block) struct_class("class", name, bases, &block); end
- def typedef(type, name) genl "typedef #{type} #{name};" end
+
+ def typedef(type, name) genl "typedef #{type} #{name};\n" end
+
+ def variant(types) "boost::variant<#{types.join(", ")}>"; end
+ def variantl(types) "boost::variant<#{types.join(", \n")}>"; end
+ def blank_variant(types) variant(["boost::blank"]+types); end
+ def tuple(types) "boost::tuple<#{types.join(', ')}>"; end
+
end
diff --git a/cpp/rubygen/templates/method_variants.rb b/cpp/rubygen/templates/method_variants.rb
new file mode 100755
index 0000000000..0787657089
--- /dev/null
+++ b/cpp/rubygen/templates/method_variants.rb
@@ -0,0 +1,72 @@
+#!/usr/bin/env ruby
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+# Generate the full AMQP class/method model as C++ types.
+class AmqpCppModelGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ end
+
+ def gen_set_variant(varname, idtype, pairs, errmsg)
+ pairs.sort!
+ scope("inline void setVariant(#{varname}& var, #{idtype} id) {") {
+ scope("switch (id) {") {
+ pairs.each { |i,t|
+ genl "case #{i}: var=#{t}(); break;";
+ }
+ genl "default: THROW_QPID_ERROR(FRAMING_ERROR, (boost::format(\"#{errmsg}\")%id).str());"
+ }
+ }
+ genl
+ end
+
+ def gen_class(c)
+ varname="#{c.name.caps}Variant"
+ mtypes=c.methods.map { |m| m.body_name }
+ typedef(blank_variant(mtypes), varname)
+ genl
+ pairs=c.methods.map { |m| [m.index.to_i,m.body_name] }
+ gen_set_variant(varname, "MethodId", pairs,
+ "%d is not a valid method index in class #{c.name}")
+ mtypes.each { |t|
+ genl "template<> struct ClassVariant<#{t}> { typedef #{varname} value; };"
+ }
+ genl
+ end
+
+ def gen_all()
+ varname="MethodVariant"
+ types=@amqp.classes.map { |c| "#{c.name.caps}Variant" }
+ pairs=@amqp.classes.map { |c| [c.index.to_i,"#{c.name.caps}Variant"] }
+ typedef(blank_variant(types), varname)
+ genl
+ gen_set_variant(varname, "ClassId", pairs,
+ "%d is not a valid class index.")
+ end
+
+ def generate()
+ h_file("qpid/framing/method_variants.h") {
+ @amqp.methods.each { |m| include "qpid/framing/#{m.body_name}.h"}
+ include "qpid/framing/amqp_types.h"
+ include "qpid/QpidError.h"
+ include "qpid/framing/variant.h"
+ include "<boost/format.hpp>"
+ genl
+ namespace("qpid::framing") {
+ genl "// Metafunction returns class variant containing method T."
+ genl "template <class T> struct ClassVariant {};"
+ genl
+ @amqp.classes.each { |c| gen_class c }
+ }
+ namespace("qpid::framing") {
+ gen_all
+ genl
+ }
+ }
+ end
+end
+
+AmqpCppModelGen.new(Outdir, Amqp).generate();
+
diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am
index ad720754c1..c74c27d42a 100644
--- a/cpp/src/Makefile.am
+++ b/cpp/src/Makefile.am
@@ -43,13 +43,15 @@ maintainer-clean-local:
rgen_dir=$(top_srcdir)/rubygen
rgen_tdir=$(rgen_dir)/templates
-rgen_script=$(rgen_dir)/generate
-rgen_cmd=ruby -I $(rgen_dir) $(rgen_script)
+rgen_generator=$(rgen_dir)/generate $(rgen_dir)/amqpgen.rb $(rgen_dir)/cppgen.rb
-rgen_templates=$(rgen_tdir)/frame_body_lists.rb $(rgen_tdir)/Session.rb
+rgen_cmd=ruby -I $(rgen_dir) $(rgen_dir)/generate
+rgen_templates=$(rgen_tdir)/method_variants.rb $(rgen_tdir)/frame_body_lists.rb $(rgen_tdir)/Session.rb
-rubygen.mk: $(rgen_script) $(specs) $(rgen_templates)
- gen=`$(rgen_cmd) . $(specs) $(rgen_templates)` ; echo Generated $$gen; echo rgen_srcs=$$gen > $@
+rubygen.mk: $(rgen_generator) $(specs) $(rgen_templates)
+ { echo "rgen_srcs= \\"; \
+ for f in `$(rgen_cmd) . $(specs) $(rgen_templates) `; do echo " $$f \\"; done; \
+ echo; } > $@
$(rgen_srcs): rubygen.mk
@@ -172,7 +174,11 @@ libqpidcommon_la_SOURCES = \
qpid/log/Selector.h \
qpid/log/Statement.cpp \
qpid/log/Statement.h \
- qpid/memory.h
+ qpid/memory.h \
+ qpid/framing/Frame.cpp qpid/framing/Frame.h \
+ qpid/framing/method_variants.h \
+ qpid/framing/MethodHolder.cpp qpid/framing/MethodHolder.h
+
libqpidbroker_la_LIBADD = libqpidcommon.la -lboost_iostreams
libqpidbroker_la_SOURCES = \
@@ -410,3 +416,16 @@ nobase_include_HEADERS = \
# Force build of qpidd during dist phase so help2man will work.
dist-hook: qpidd
+
+# FIXME aconway 2007-08-06: Use of gch should be conditional on gcc version.
+
+# Pre compiled headers - use BUILT_SOURCES to get them built first.
+BUILT_SOURCES=qpid/framing/method_variants.h.gch
+sinclude qpid/framing/method_variants.h.gch.deps
+CLEANFILES=qpid/framing/method_variants.h.gch qpid/framing/method_variants.h.gch.deps
+
+SUFFIXES=.h.gch
+.h.h.gch:
+ rm -f $@
+ $(CXXCOMPILE) -MT $@ -MD -MP -MF $@.deps -fPIC -DPIC -x c++-header -c -o $@ $<
+
diff --git a/cpp/src/qpid/framing/AMQMethodBody.h b/cpp/src/qpid/framing/AMQMethodBody.h
index 2b46c6ea00..cbfd438b48 100644
--- a/cpp/src/qpid/framing/AMQMethodBody.h
+++ b/cpp/src/qpid/framing/AMQMethodBody.h
@@ -80,6 +80,7 @@ class AMQMethodBody : public AMQBody
virtual void printPrefix(std::ostream&) const {}
+ friend class MethodHolder;
};
diff --git a/cpp/src/qpid/framing/Frame.cpp b/cpp/src/qpid/framing/Frame.cpp
new file mode 100644
index 0000000000..7bdc0adf00
--- /dev/null
+++ b/cpp/src/qpid/framing/Frame.cpp
@@ -0,0 +1,119 @@
+/*
+ *
+ * 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 <boost/format.hpp>
+
+#include "Frame.h"
+#include "qpid/QpidError.h"
+#include "AMQRequestBody.h"
+#include "AMQResponseBody.h"
+
+
+namespace qpid {
+namespace framing {
+
+namespace {
+struct GetBodyVisitor : public NoBlankVisitor<AMQBody*> {
+ QPID_USING_NOBLANK(AMQBody*);
+ AMQBody* operator()(MethodHolder& h) const { return h.getMethod(); }
+ template <class T> AMQBody* operator()(T& t) const { return &t; }
+};
+}
+
+AMQBody* Frame::getBody() {
+ return boost::apply_visitor(GetBodyVisitor(), body);
+}
+
+const AMQBody* Frame::getBody() const {
+ return boost::apply_visitor(GetBodyVisitor(), const_cast<Variant&>(body));
+}
+
+void Frame::encode(Buffer& buffer)
+{
+ buffer.putOctet(getBody()->type());
+ buffer.putShort(channel);
+ buffer.putLong(getBody()->size());
+ getBody()->encode(buffer);
+ buffer.putOctet(0xCE);
+}
+
+uint32_t Frame::size() const{
+ return 1/*type*/ + 2/*channel*/ + 4/*body size*/ + getBody()->size()
+ + 1/*0xCE*/;
+}
+
+bool Frame::decode(Buffer& buffer)
+{
+ if(buffer.available() < 7)
+ return false;
+ buffer.record();
+ uint32_t frameSize = decodeHead(buffer);
+ if(buffer.available() < frameSize + 1){
+ buffer.restore();
+ return false;
+ }
+ decodeBody(buffer, frameSize);
+ uint8_t end = buffer.getOctet();
+ if(end != 0xCE) THROW_QPID_ERROR(FRAMING_ERROR, "Frame end not found");
+ return true;
+}
+
+uint32_t Frame::decodeHead(Buffer& buffer){
+ type = buffer.getOctet();
+ channel = buffer.getShort();
+ return buffer.getLong();
+}
+
+void Frame::decodeBody(Buffer& buffer, uint32_t size)
+{
+ switch(type)
+ {
+ case METHOD_BODY:
+ case REQUEST_BODY:
+ case RESPONSE_BODY: {
+ ClassId c=buffer.getShort();
+ MethodId m=buffer.getShort();
+ body = MethodHolder(c,m);
+ break;
+ }
+ case HEADER_BODY:
+ body = AMQHeaderBody();
+ break;
+ case CONTENT_BODY:
+ body = AMQContentBody();
+ break;
+ case HEARTBEAT_BODY:
+ body = AMQHeartbeatBody();
+ break;
+ default:
+ THROW_QPID_ERROR(
+ FRAMING_ERROR,
+ boost::format("Unknown frame type %d") % type);
+ }
+ getBody()->decode(buffer, size);
+}
+
+std::ostream& operator<<(std::ostream& out, const Frame& f)
+{
+ return out << "Frame[channel=" << f.getChannel() << "; " << f.body << "]";
+}
+
+
+}} // namespace qpid::framing
diff --git a/cpp/src/qpid/framing/Frame.h b/cpp/src/qpid/framing/Frame.h
new file mode 100644
index 0000000000..4a504a6b41
--- /dev/null
+++ b/cpp/src/qpid/framing/Frame.h
@@ -0,0 +1,72 @@
+#ifndef _AMQFrame_
+#define _AMQFrame_
+
+/*
+ *
+ * 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 "AMQDataBlock.h"
+#include "AMQHeaderBody.h"
+#include "AMQContentBody.h"
+#include "AMQHeartbeatBody.h"
+#include "MethodHolder.h"
+
+namespace qpid {
+namespace framing {
+
+class Frame : public AMQDataBlock {
+ public:
+ typedef boost::variant<boost::blank,
+ AMQHeaderBody,
+ AMQContentBody,
+ AMQHeartbeatBody,
+ MethodHolder> Variant;
+
+ Frame(ChannelId channel_=0, const Variant& body_=Variant())
+ : body(body_), channel(channel_) {}
+
+ void encode(Buffer& buffer);
+ bool decode(Buffer& buffer);
+ uint32_t size() const;
+
+ uint16_t getChannel() const { return channel; }
+
+ AMQBody* getBody();
+ const AMQBody* getBody() const;
+
+ template <class T> T* castBody() {
+ return boost::polymorphic_downcast<T*>(getBody());
+ }
+
+ Variant body;
+
+ private:
+ uint32_t decodeHead(Buffer& buffer);
+ void decodeBody(Buffer& buffer, uint32_t size);
+
+ uint8_t type;
+ uint16_t channel;
+};
+
+std::ostream& operator<<(std::ostream&, const Frame&);
+
+}} // namespace qpid::framing
+
+
+#endif
diff --git a/cpp/src/qpid/framing/MethodHolder.cpp b/cpp/src/qpid/framing/MethodHolder.cpp
new file mode 100644
index 0000000000..27046af43c
--- /dev/null
+++ b/cpp/src/qpid/framing/MethodHolder.cpp
@@ -0,0 +1,93 @@
+/*
+ *
+ * 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 "MethodHolder.h"
+#include "amqp_types.h"
+#include "qpid/framing/Buffer.h"
+#include "qpid/framing/variant.h"
+
+using namespace boost;
+
+namespace qpid {
+namespace framing {
+
+struct SetVariantVisitor : public NoBlankVisitor<> {
+ QPID_USING_NOBLANK();
+ MethodId id;
+ SetVariantVisitor(MethodId m) : id(m) {}
+ template <class T> void operator()(T& t) const { setVariant(t, id); }
+};
+
+inline void setVariant(MethodVariant& var, ClassId c, MethodId m) {
+ setVariant(var,c);
+ boost::apply_visitor(SetVariantVisitor(m), var);
+}
+
+void MethodHolder::setMethod(ClassId c, MethodId m) {
+ setVariant(method, c, m);
+}
+
+MethodHolder::MethodHolder(ClassId c, MethodId m) {
+ setMethod(c,m);
+}
+
+struct GetClassId : public NoBlankVisitor<ClassId> {
+ QPID_USING_NOBLANK(ClassId);
+ template <class T> ClassId operator()(const T&) const {
+ return T::CLASS_ID;
+ }
+};
+
+struct GetMethodId : public NoBlankVisitor<MethodId> {
+ QPID_USING_NOBLANK(ClassId);
+ template <class T> MethodId operator()(const T&) const {
+ return T::METHOD_ID;
+ }
+};
+
+void MethodHolder::encode(Buffer& b) const {
+ const AMQMethodBody* body = getMethod();
+ b.putShort(body->amqpClassId());
+ b.putShort(body->amqpMethodId());
+ body->encodeContent(b);
+}
+
+void MethodHolder::decode(Buffer& b) {
+ ClassId classId = b.getShort();
+ ClassId methodId = b.getShort();
+ setVariant(method, classId, methodId);
+ getMethod()->decodeContent(b);
+}
+
+uint32_t MethodHolder::size() const {
+ return sizeof(ClassId)+sizeof(MethodId)+getMethod()->size();
+}
+
+
+AMQMethodBody* MethodHolder::getMethod() {
+ return applyApplyVisitor(AddressVisitor<AMQMethodBody*>(), method);
+}
+
+const AMQMethodBody* MethodHolder::getMethod() const {
+ return const_cast<MethodHolder*>(this)->getMethod();
+}
+
+}} // namespace qpid::framing
diff --git a/cpp/src/qpid/framing/MethodHolder.h b/cpp/src/qpid/framing/MethodHolder.h
new file mode 100644
index 0000000000..b93c6b83b1
--- /dev/null
+++ b/cpp/src/qpid/framing/MethodHolder.h
@@ -0,0 +1,80 @@
+#ifndef QPID_FRAMING_METHODBODYHOLDER_H
+#define QPID_FRAMING_METHODBODYHOLDER_H
+
+/*
+ *
+ * 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 "qpid/framing/method_variants.h"
+
+#include <boost/type_traits/has_nothrow_copy.hpp>
+
+namespace qpid {
+namespace framing {
+
+class AMQMethodBody;
+
+/**
+ * Holder for arbitrary method body.
+ */
+class MethodHolder
+{
+ public:
+ MethodHolder() {}
+ MethodHolder(const MethodVariant& mv) : method(mv) {}
+
+ /** Construct from a concrete method body type.
+ * ClassVariant<T>::value is the containing class variant.
+ */
+ template <class T>
+ MethodHolder(const T& t)
+ : method(typename ClassVariant<T>::value(t)) {}
+
+ /** Construct from class/method ID, set the method accordingly. */
+ MethodHolder(ClassId, MethodId);
+
+ /** Set the method to the type corresponding to ClassId, MethodId */
+ void setMethod(ClassId, MethodId);
+
+ AMQMethodBody* getMethod();
+ const AMQMethodBody* getMethod() const;
+
+ MethodVariant method;
+
+ void encode(Buffer&) const;
+ void decode(Buffer&);
+ uint32_t size() const;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const MethodHolder& h) {
+ return out << h.method;
+}
+
+
+}} // namespace qpid::framing
+
+namespace boost {
+template<> struct has_nothrow_copy<qpid::framing::MethodHolder>
+ : public boost::true_type {};
+}
+
+
+
+#endif /*!QPID_FRAMING_METHODBODYHOLDER_H*/
diff --git a/cpp/src/qpid/framing/amqp_types.h b/cpp/src/qpid/framing/amqp_types.h
index ff75b28468..5ea08a69af 100644
--- a/cpp/src/qpid/framing/amqp_types.h
+++ b/cpp/src/qpid/framing/amqp_types.h
@@ -42,6 +42,7 @@ namespace qpid {
namespace framing {
using std::string;
+typedef uint8_t FrameType;
typedef uint16_t ChannelId;
typedef uint64_t RequestId;
typedef uint64_t ResponseId;
diff --git a/cpp/src/qpid/framing/variant.h b/cpp/src/qpid/framing/variant.h
new file mode 100644
index 0000000000..3cb8aece5d
--- /dev/null
+++ b/cpp/src/qpid/framing/variant.h
@@ -0,0 +1,92 @@
+#ifndef QPID_FRAMING_VARIANT_H
+#define QPID_FRAMING_VARIANT_H
+
+/*
+ * 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.
+ *
+ */
+
+/**@file Tools for using boost::variant */
+
+#include "qpid/QpidError.h"
+
+#include <boost/variant.hpp>
+
+namespace qpid {
+namespace framing {
+class Buffer;
+
+/** boost::static_visitor that throws exception if variant contains blank.
+ * Sublclasses need to have a using() declaration, can be generated
+ * with QPID_USING_BLANK_THROW(R)
+ */
+template <class R=void>
+struct NoBlankVisitor : public boost::static_visitor<R> {
+ R foundBlank() const {
+ assert(0);
+ THROW_QPID_ERROR(INTERNAL_ERROR, "Invalid variant value.");
+ }
+ R operator()(const boost::blank&) const { return foundBlank(); }
+ R operator()(boost::blank&) const { return foundBlank(); }
+};
+
+
+}} // qpid::framing
+
+
+/** Generate using statement needed in visitors inheriting NoBlankVisitor
+ * @param R return type.
+ */
+#define QPID_USING_NOBLANK(R) using ::qpid::framing::NoBlankVisitor<R>::operator()
+
+namespace qpid {
+namespace framing {
+
+/** Convert the variant value to type R. */
+template <class R> struct ConvertVisitor : public NoBlankVisitor<R> {
+ QPID_USING_NOBLANK(R);
+ template <class T> R operator()(T& t) const { return t; }
+};
+
+/** Convert address of variant value to type R. */
+template <class R> struct AddressVisitor : public NoBlankVisitor<R> {
+ QPID_USING_NOBLANK(R);
+ template <class T> R operator()(T& t) const { return &t; }
+};
+
+/** Apply a visitor to the nested variant in a variant of variants */
+template<class V>
+struct ApplyVisitor : public NoBlankVisitor<typename V::result_type> {
+ QPID_USING_NOBLANK(typename V::result_type);
+ const V& visitor;
+ ApplyVisitor(const V& v) : visitor(v) {}
+ template <class T> typename V::result_type operator()(T& t) const {
+ return boost::apply_visitor(visitor, t);
+ }
+};
+
+/** Convenience function to construct and apply an ApplyVisitor */
+template <class Visitor, class Visitable>
+typename Visitor::result_type applyApplyVisitor(const Visitor& visitor, Visitable& visitable) {
+ return boost::apply_visitor(ApplyVisitor<Visitor>(visitor), visitable);
+}
+
+}} // namespace qpid::framing
+
+
+#endif /*!QPID_FRAMING_VARIANT_H*/
diff --git a/cpp/src/tests/Frame.cpp b/cpp/src/tests/Frame.cpp
new file mode 100644
index 0000000000..f02564f5da
--- /dev/null
+++ b/cpp/src/tests/Frame.cpp
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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 "qpid/framing/Frame.h"
+
+#define BOOST_AUTO_TEST_MAIN // Must come before #include<boost/test/*>
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/lexical_cast.hpp>
+
+using namespace std;
+using namespace qpid::framing;
+using namespace boost;
+
+BOOST_AUTO_TEST_CASE(testContentBody) {
+ Frame f(42, AMQContentBody("foobar"));
+ AMQBody* body=f.getBody();
+ BOOST_CHECK(dynamic_cast<AMQContentBody*>(body));
+ Buffer b(f.size());
+ f.encode(b);
+ b.flip();
+ Frame g;
+ g.decode(b);
+ AMQContentBody* content=dynamic_cast<AMQContentBody*>(g.getBody());
+ BOOST_REQUIRE(content);
+ BOOST_CHECK_EQUAL(content->getData(), "foobar");
+}
+
+BOOST_AUTO_TEST_CASE(testMethodBody) {
+ FieldTable args;
+ args.setString("foo", "bar");
+ Frame f(
+ 42, QueueDeclareBody(ProtocolVersion(), 1, "q", "altex",
+ true, false, true, false, true, args));
+ BOOST_CHECK_EQUAL(f.getChannel(), 42);
+ Buffer b(f.size());
+ f.encode(b);
+ b.flip();
+ Frame g;
+ g.decode(b);
+ BOOST_CHECK_EQUAL(f.getChannel(), g.getChannel());
+ QueueDeclareBody* declare=dynamic_cast<QueueDeclareBody*>(g.getBody());
+ BOOST_REQUIRE(declare);
+ BOOST_CHECK_EQUAL(declare->getAlternateExchange(), "altex");
+ BOOST_CHECK_EQUAL(lexical_cast<string>(*f.getBody()), lexical_cast<string>(*g.getBody()));
+}
+
+BOOST_AUTO_TEST_CASE(testLoop) {
+ // Run in a loop so heap profiler can spot any allocations.
+ Buffer b(1024);
+ for (int i = 0; i < 100; ++i) {
+ Frame ctor(2, AccessRequestOkBody(ProtocolVersion(), 42));
+ Frame assign(3);
+ assign.body = AccessRequestOkBody(ProtocolVersion(), 42);
+ assign.encode(b);
+ b.flip();
+ Frame g;
+ g.decode(b);
+ BOOST_REQUIRE(dynamic_cast<AccessRequestOkBody*>(g.getBody())->getTicket() == 42);
+ }
+}
diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am
index bc173d61d6..503083af21 100644
--- a/cpp/src/tests/Makefile.am
+++ b/cpp/src/tests/Makefile.am
@@ -43,6 +43,15 @@ check_PROGRAMS+=Shlib
Shlib_SOURCES=Shlib.cpp
Shlib_LDADD=-lboost_unit_test_framework $(lib_common)
+TESTS+=Frame
+check_PROGRAMS+=Frame
+Frame_SOURCES=Frame.cpp
+Frame_LDADD=-lboost_unit_test_framework $(lib_common)
+
+
+
+# TODO aconway 2007-08-07: Why aren't these tests run automatically?
+
check_PROGRAMS+=ConcurrentQueue
ConcurrentQueue_SOURCES=ConcurrentQueue.cpp
ConcurrentQueue_LDADD=-lboost_test_exec_monitor $(lib_common)