diff options
author | Alan Conway <aconway@apache.org> | 2008-02-29 22:07:40 +0000 |
---|---|---|
committer | Alan Conway <aconway@apache.org> | 2008-02-29 22:07:40 +0000 |
commit | 6dd59f62185ab8547cc1eec0a57731e0ab5a8645 (patch) | |
tree | 25539ade95b792d9e8228eb5b24d5a65340bc5d6 /cpp | |
parent | 3fb6c758d2d7d7c1822e216e3c22db4630eb19e0 (diff) | |
download | qpid-python-6dd59f62185ab8547cc1eec0a57731e0ab5a8645.tar.gz |
Template visitors for amqp_0_10::Command, Control and Struct.
Serialization for all str/vbin types.
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@632457 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp')
-rwxr-xr-x | cpp/rubygen/0-10/specification.rb | 76 | ||||
-rwxr-xr-x | cpp/rubygen/amqpgen.rb | 9 | ||||
-rwxr-xr-x | cpp/rubygen/cppgen.rb | 22 | ||||
-rw-r--r-- | cpp/src/Makefile.am | 3 | ||||
-rw-r--r-- | cpp/src/qpid/Serializer.h | 80 | ||||
-rw-r--r-- | cpp/src/qpid/amqp_0_10/Codec.h | 134 | ||||
-rw-r--r-- | cpp/src/qpid/amqp_0_10/Decimal.h | 5 | ||||
-rw-r--r-- | cpp/src/qpid/amqp_0_10/apply.h | 77 | ||||
-rw-r--r-- | cpp/src/qpid/amqp_0_10/built_in_types.h | 60 | ||||
-rw-r--r-- | cpp/src/qpid/amqp_0_10/helpers.cpp | 3 | ||||
-rw-r--r-- | cpp/src/qpid/amqp_0_10/helpers.h | 7 | ||||
-rw-r--r-- | cpp/src/qpid/amqp_0_10/visitors.h | 15 | ||||
-rw-r--r-- | cpp/src/qpid/framing/Uuid.h | 2 | ||||
-rw-r--r-- | cpp/src/tests/Makefile.am | 2 | ||||
-rw-r--r-- | cpp/src/tests/apply.cpp | 92 | ||||
-rw-r--r-- | cpp/src/tests/serialize.cpp | 30 |
16 files changed, 385 insertions, 232 deletions
diff --git a/cpp/rubygen/0-10/specification.rb b/cpp/rubygen/0-10/specification.rb index d4ecfe98ed..acf9e4e6ed 100755 --- a/cpp/rubygen/0-10/specification.rb +++ b/cpp/rubygen/0-10/specification.rb @@ -19,10 +19,8 @@ class Specification < CppGen genl d.enum.choices.map { |c| "#{c.name.constname} = #{c.value}" }.join(",\n") } - elsif (d.type_ == "array") - genl "typedef Array<#{ArrayTypes[d.name].amqp2cpp}> #{typename};" else - genl "typedef #{d.type_.amqp2cpp} #{typename};" + genl "typedef #{d.amqp2cpp} #{typename};" end end @@ -41,7 +39,7 @@ class Specification < CppGen def action_struct_h(x, base, consts, &block) genl struct(x.classname, "public #{base}") { - x.fields.each { |f| genl "#{f.type_.amqp2cpp} #{f.cppname};" } + x.fields.each { |f| genl "#{f.amqp2cpp} #{f.cppname};" } genl genl "static const char* NAME;" consts.each { |c| genl "static const uint8_t #{c.upcase}=#{x.send c or 0};"} @@ -58,10 +56,11 @@ class Specification < CppGen genl genl "const char* #{x.classname}::NAME=\"#{x.fqname}\";" genl - ctor_defn(x.classname) {} - ctor_defn(x.classname, x.parameters, x.initializers) {} if not x.fields.empty? - function_defn("void #{x.classname}::accept", ["Visitor&"], "const") { - genl "// FIXME aconway 2008-02-27: todo" + ctor=x.classname+"::"+x.classname + ctor_defn(ctor) {} + ctor_defn(ctor, x.parameters, x.initializers) {} if not x.fields.empty? + function_defn("void #{x.classname}::accept", ["Visitor& v"], "const") { + genl "v.visit(*this);" } end @@ -107,20 +106,21 @@ class Specification < CppGen # they are used by other definitions: each_class_ns { |c| class_h c - c.domains.each { |d| domain_h d if pregenerate? d } - c.structs.each { |s| struct_h s if pregenerate? s } + c.collect_all(AmqpDomain).each { |d| domain_h d if pregenerate? d } + c.collect_all(AmqpStruct).each { |s| struct_h s if pregenerate? s } } # Now dependent domains/structs and actions each_class_ns { |c| - c.domains.each { |d| domain_h d if not pregenerate? d } - c.structs.each { |s| struct_h s if not pregenerate? s } - c.actions.each { |a| action_h a } + c.collect_all(AmqpDomain).each { |d| domain_h d unless pregenerate? d} + c.collect_all(AmqpStruct).each { |s| struct_h s unless pregenerate? s} + c.collect_all(AmqpAction).each { |a| action_h a } } } } cpp_file("#{@dir}/specification") { include "#{@dir}/specification" + ["Command","Control","Struct"].each { |x| include "#{@dir}/Apply#{x}" } namespace(@ns) { each_class_ns { |c| class_cpp c @@ -156,10 +156,58 @@ class Specification < CppGen } } end - + + def gen_visitor(base, subs) + h_file("#{@dir}/#{base}Visitor.h") { + include "#{@dir}/specification" + namespace("#{@ns}") { + genl + genl "/** Visitor interface for #{base} subclasses. */" + struct("#{base}::Visitor") { + genl "virtual ~Visitor() {}" + genl "typedef #{base} BaseType;" + subs.each{ |s| + genl "virtual void visit(const #{s.fqclassname}&) = 0;" + }}}} + + h_file("#{@dir}/Apply#{base}.h") { + include "#{@dir}/#{base}Visitor.h" + include "#{@dir}/apply.h" + namespace("#{@ns}") { + genl + genl "/** apply() support for #{base} subclasses */" + genl "template <class F>" + struct("ApplyVisitor<#{base}::Visitor, F>", + ["public FunctionAndResult<F>", "public #{base}::Visitor"]) { + subs.each{ |s| + function_defn("virtual void visit", ["const #{s.fqclassname}& x"]) { + genl "this->invoke(x);" + }}}}} + end + + def gen_visitors() + end + + def holder(base, derived) + name=base.caps+"Holder" + h_file("#{@dir}/#{name}") { + include "#{@dir}/specification" + include "qpid/framing/Blob" + namespace(@ns){ + # TODO aconway 2008-02-29: + } + } + end + def gen_holders() + + end + def generate gen_specification gen_proxy + gen_visitor("Command", @amqp.collect_all(AmqpCommand)) + gen_visitor("Control", @amqp.collect_all(AmqpControl)) + gen_visitor("Struct", @amqp.collect_all(AmqpStruct)) end end diff --git a/cpp/rubygen/amqpgen.rb b/cpp/rubygen/amqpgen.rb index 2edc573d00..67b4b1c73c 100755 --- a/cpp/rubygen/amqpgen.rb +++ b/cpp/rubygen/amqpgen.rb @@ -131,6 +131,12 @@ class AmqpElement @children.each { |c| c.each_descendant(&block) } end + def collect_all(amqp_type) + collect=[] + each_descendant { |d| collect << d if d.is_a? amqp_type } + collect + end + # Look up child of type elname with attribute name. def child(elname, name) @cache_child[[elname,name]] ||= children(elname).find { |c| c.name==name } @@ -386,6 +392,7 @@ class AmqpRoot < AmqpElement def methods_() classes.map { |c| c.methods_ }.flatten; end + #preview # Return all methods on chassis for all classes. def methods_on(chassis) @methods_on ||= { } @@ -394,8 +401,6 @@ class AmqpRoot < AmqpElement def fqname() nil; end - # TODO aconway 2008-02-21: methods by role. - private # Merge contents of elements. diff --git a/cpp/rubygen/cppgen.rb b/cpp/rubygen/cppgen.rb index 5d2695c77a..df4ba49ca8 100755 --- a/cpp/rubygen/cppgen.rb +++ b/cpp/rubygen/cppgen.rb @@ -57,6 +57,7 @@ class String def cppsafe() CppMangle.include?(self) ? self+"_" : self; end def amqp2cpp() + throw 'Invalid "array".amqp2cpp' if self=="array" path=split(".") name=path.pop return name.typename if path.empty? @@ -112,21 +113,28 @@ class CppType def to_s() name; end; end +class AmqpElement + # convert my amqp type_ attribute to a C++ type. + def amqp2cpp() + return "Array<#{ArrayTypes[name].amqp2cpp}> " if type_=="array" + return type_.amqp2cpp + end +end + class AmqpField def cppname() name.lcaps.cppsafe; end def cpptype() domain.cpptype; end def bit?() domain.type_ == "bit"; end def signature() cpptype.param+" "+cppname; end - # FIXME aconway 2008-02-27: qualified - def paramtype() - fqtype=type_ - unless type_.index(".") + def fqtypename() + unless type_.index(".") c=containing_class return c.domain(type_).fqtypename if c.domain(type_) return c.struct(type_).fqclassname if c.struct(type_) end - "call_traits<#{fqtype.amqp2cpp}>::param_type"; + return amqp2cpp end + def paramtype() "call_traits<#{fqtypename}>::param_type"; end end class AmqpMethod @@ -337,8 +345,8 @@ class CppGen < Generator def ctor_decl(name, params=[]) function_decl(name, params); end def ctor_defn(name, params=[], inits=[]) - signature(name+"::"+name, params) - scope(":","") { genl inits.join(",\n")} if not inits.empty? + signature(name, params, inits.empty? ? "" : " :") + indent { gen inits.join(",\n") } if not inits.empty? scope() { yield } end diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am index becccb4224..a3609667d7 100644 --- a/cpp/src/Makefile.am +++ b/cpp/src/Makefile.am @@ -104,10 +104,10 @@ libqpidcommon_la_SOURCES = \ $(rgen_common_cpp) \ $(platform_src) \ qpid/amqp_0_10/helpers.cpp \ - qpid/Serializer.h \ qpid/amqp_0_10/built_in_types.h \ qpid/amqp_0_10/Codec.h \ qpid/amqp_0_10/Decimal.h \ + qpid/Serializer.h \ qpid/framing/AccumulatedAck.cpp \ qpid/framing/AMQBody.cpp \ qpid/framing/AMQMethodBody.cpp \ @@ -254,6 +254,7 @@ libqpidclient_la_SOURCES = \ nobase_include_HEADERS = \ $(platform_hdr) \ qpid/amqp_0_10/helpers.h \ + qpid/amqp_0_10/apply.h \ qpid/assert.h \ qpid/DataDir.h \ qpid/Exception.h \ diff --git a/cpp/src/qpid/Serializer.h b/cpp/src/qpid/Serializer.h index a2fbf944ae..95cc2d5875 100644 --- a/cpp/src/qpid/Serializer.h +++ b/cpp/src/qpid/Serializer.h @@ -1,5 +1,5 @@ -#ifndef QPID_SERIALIZERBASE_H -#define QPID_SERIALIZERBASE_H +#ifndef QPID_SERIALIZER_H +#define QPID_SERIALIZER_H /* * @@ -22,88 +22,36 @@ * */ -#include <boost/cast.hpp> -#include <boost/array.hpp> +#include <boost/type_traits/remove_const.hpp> #include <boost/utility/enable_if.hpp> -#include <boost/type_traits/is_class.hpp> +#include <boost/type_traits/is_const.hpp> +#include <boost/type_traits/add_const.hpp> +#include <algorithm> namespace qpid { /** - * Base template for serializers, provides generic serialization for - * conmpound types and common encode/decode/size functions. - * - * Derived template must provide - * - Derived& op()(T) for primitive types. - * - Derived& raw(void*, size_t) for raw binary data - * - Derived& byte(char) for single bytes. - * - * Derived templatse may override any of the functions provided by - * this base class. - * - * This class provides templates to break down compound types - * into primitive types and delegate to the derived class. - * + * Base class for serializers. */ template <class Derived> class Serializer { public: - - /** Call T::serialize() for classes that have their own serialize function */ - template <class T> - typename boost::enable_if<boost::is_class<T>, Derived>::type - operator()(T& t) { t.serialize(self()); return self(); } - - template <class T, size_t N> - Derived& operator()(boost::array<T,N>& a) { - std::for_each(a.begin(), a.end(), self()); - return self(); - } - - Derived& operator()(char& x) { return self().byte((char&)x); } - Derived& operator()(int8_t& x) { return self().byte((char&)x); } - Derived& operator()(uint8_t& x) { return self().byte((char&)x); } - - protected: - template <class T> Derived& raw(T& t) { - return self().raw(&t, sizeof(T)); - } - - private: - Derived& self() { return *static_cast<Derived*>(this); } -}; - -/** Like Serializer but does not modify the values passed to it. */ -template <class Derived> class ConstSerializer { - public: template <class T> - typename boost::enable_if<boost::is_class<T>, Derived>::type - operator()(const T& t) { - // Const cast so we don't have to write 2 serialize() functions - // for every class. - const_cast<T&>(t).serialize(self()); + typename boost::enable_if<boost::is_class<T>, Derived&>::type + operator()(T& t) { + // const_cast so we don't need 2 serialize() members for every class. + const_cast<typename boost::remove_const<T>::type&>(t).serialize(self()); return self(); } - template <class T, size_t N> - Derived& operator()(const boost::array<T,N>& a) { - std::for_each(a.begin(), a.end(), self()); + template <class Iter> Derived& iterate(Iter begin, Iter end) { + std::for_each(begin, end, self()); return self(); } - Derived& operator()(char x) { return self().byte(x); } - Derived& operator()(int8_t x) { return self().byte(x); } - Derived& operator()(uint8_t x) { return self().byte(x); } - - protected: - template <class T> Derived& raw(const T& t) { - return self().raw(&t, sizeof(T)); - } - private: Derived& self() { return *static_cast<Derived*>(this); } }; - } // namespace qpid -#endif /*!QPID_SERIALIZERBASE_H*/ +#endif /*!QPID_SERIALIZER_H*/ diff --git a/cpp/src/qpid/amqp_0_10/Codec.h b/cpp/src/qpid/amqp_0_10/Codec.h index e7f35e9288..acfc1e9c81 100644 --- a/cpp/src/qpid/amqp_0_10/Codec.h +++ b/cpp/src/qpid/amqp_0_10/Codec.h @@ -32,70 +32,47 @@ namespace qpid { namespace amqp_0_10 { - /** * AMQP 0-10 encoding and decoding. */ -struct Codec -{ - template <class T> - static inline void endianize(T& value) { - -#ifdef BOOST_LITTLE_ENDIAN - std::reverse((char*)&value, (char*)&value+sizeof(value)); -#else - (void)value; // Avoid unused var warnings. -#endif - } - static inline void endianize(char&) {} - static inline void endianize(uint8_t&) {} - static inline void endianize(int8_t&) {} - - - template <class Out> struct Encode : public ConstSerializer<Encode<Out> > { - Out out; +class Codec { + public: + /** Encode to an output byte iterator */ + template <class OutIter> + class Encode : public Serializer<Encode<OutIter> > { + public: + Encode(OutIter o) : out(o) {} - Encode(Out o) : out(o) {} + using Serializer<Encode<OutIter> >::operator(); - using ConstSerializer<Encode<Out> >::operator(); - using ConstSerializer<Encode<Out> >::raw; - - template <class T> + template <class T> typename boost::enable_if<boost::is_integral<T>, Encode&>::type - operator()(const T& x) { T xx(x); endianize(xx); return raw(xx); } + operator()(T x) { + endianize(x); + raw(&x, sizeof(x)); + return *this; + } - // FIXME aconway 2008-02-20: correct float encoading + // FIXME aconway 2008-02-20: correct float encoading? template <class T> typename boost::enable_if<boost::is_float<T>, Encode&>::type - operator()(const T& x) { return raw(x); } - + operator()(const T& x) { raw(&x, sizeof(x)); return *this; } - template<class T, class SizeType> - Encode& operator()(const CodableString<T,SizeType>& str) { - (*this)(SizeType(str.size())); - std::for_each(str.begin(), str.end(), *this); - return *this; + void raw(const void* p, size_t n) { + std::copy((const char*)p, (const char*)p+n, out); } - + private: - friend class ConstSerializer<Encode<Out> >; - - Encode& raw(const void* vp, size_t s) { - char* p = (char*) vp; - std::copy(p, p+s, out); - return *this; - } - - Encode& byte(char x) { out++ = x; return *this; } + OutIter out; }; - template <class In> struct Decode : public Serializer<Decode<In> > { - In in; - Decode(In i) : in(i) {} - - using Serializer<Decode<In> >::operator(); - using Serializer<Decode<In> >::raw; - + template <class InIter> + class Decode : public Serializer<Decode<InIter> > { + public: + Decode(InIter i) : in(i) {} + + using Serializer<Decode<InIter> >::operator(); + template <class T> typename boost::enable_if<boost::is_integral<T>, Decode&>::type operator()(T& x) { @@ -106,10 +83,10 @@ struct Codec template <class T> typename boost::enable_if<boost::is_float<T>, Decode&>::type - operator()(T& x) { return raw(&x, sizeof(x)); } + operator()(T& x) { raw(&x, sizeof(x)); return *this; } template<class T, class SizeType> - Decode& operator()(CodableString<T,SizeType>& str) { + Decode& operator()(SerializableString<T,SizeType>& str) { SizeType n; (*this)(n); str.resize(n); @@ -117,54 +94,45 @@ struct Codec return *this; } - private: - friend class Serializer<Decode<In> >; - - Decode& raw(void* vp, size_t s) { - char* p=(char*)vp; - std::copy(in, in+s, p); - return *this; + void raw(void *p, size_t n) { + // FIXME aconway 2008-02-29: requires random access iterator, + // does this optimize to memcpy? Is there a better way? + std::copy(in, in+n, (char*)p); + in += n; } - Decode& byte(char& x) { x = *in++; return *this; } + private: + InIter in; }; - struct Size : public ConstSerializer<Size> { + + class Size : public Serializer<Size> { + public: Size() : size(0) {} - size_t size; + operator size_t() const { return size; } - using ConstSerializer<Size>::operator(); - using ConstSerializer<Size>::raw; + using Serializer<Size>::operator(); template <class T> typename boost::enable_if<boost::is_arithmetic<T>, Size&>::type operator()(const T&) { size += sizeof(T); return *this; } - template <class T, size_t N> - Size& operator()(const boost::array<T,N>&) { - size += sizeof(boost::array<T,N>); - return *this; - } - template<class T, class SizeType> - Size& operator()(const CodableString<T,SizeType>& str) { + Size& operator()(const SerializableString<T,SizeType>& str) { size += sizeof(SizeType) + str.size()*sizeof(T); return *this; } + void raw(const void*, size_t n){ size += n; } private: - friend class ConstSerializer<Size>; - - Size& raw(void*, size_t s) { size += s; return *this; } - - Size& byte(char) { ++size; return *this; } + size_t size; }; template <class Out, class T> static void encode(Out o, const T& x) { - Encode<Out>encode(o); + Encode<Out> encode(o); encode(x); } @@ -180,6 +148,18 @@ struct Codec sz(x); return sz; } + + private: + template <class T> static inline void endianize(T& value) { +#ifdef BOOST_LITTLE_ENDIAN + std::reverse((char*)&value, (char*)&value+sizeof(value)); +#else + (void)value; // Avoid unused var warnings. +#endif + } + static inline void endianize(char&) {} + static inline void endianize(uint8_t&) {} + static inline void endianize(int8_t&) {} }; }} // namespace qpid::amqp_0_10 diff --git a/cpp/src/qpid/amqp_0_10/Decimal.h b/cpp/src/qpid/amqp_0_10/Decimal.h index 75cde94559..50fc457c76 100644 --- a/cpp/src/qpid/amqp_0_10/Decimal.h +++ b/cpp/src/qpid/amqp_0_10/Decimal.h @@ -30,7 +30,7 @@ template <class E, class M> struct Decimal { E exponent; M mantissa; - Decimal() : exponent(0), mantissa(0) {} + Decimal(E exp=0, M man=0) : exponent(exp), mantissa(man) {} bool operator==(const Decimal& d) const { return exponent == d.exponent && mantissa == d.mantissa; @@ -44,8 +44,7 @@ template <class E, class M> struct Decimal { template<class E, class M> inline std::ostream& operator<<(std::ostream& o, const Decimal<E,M>& d) { - M pow10=10^d.exponent; - return o << d.mantissa/pow10 << "." << d.mantissa%pow10; + return o << "Decimal{" << d.mantissa << "/10^" << (int)d.exponent << "}"; } }} diff --git a/cpp/src/qpid/amqp_0_10/apply.h b/cpp/src/qpid/amqp_0_10/apply.h new file mode 100644 index 0000000000..e1bd9c3aa6 --- /dev/null +++ b/cpp/src/qpid/amqp_0_10/apply.h @@ -0,0 +1,77 @@ +#ifndef QPID_AMQP_0_10_APPLY_H +#define QPID_AMQP_0_10_APPLY_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 <boost/optional.hpp> + +namespace qpid { +namespace amqp_0_10 { + +template <class F, class R=typename F::result_type> struct FunctionAndResult { + F* functor; + boost::optional<R> result; + + FunctionAndResult() : functor(0) {} + template <class T> void invoke(T t) { result=(*functor)(t); } + R getResult() { return *result; } +}; + +// void result is special case. +template <class F> struct FunctionAndResult<F, void> { + F* functor; + + FunctionAndResult() : functor(0) {} + template <class T> void invoke(T t) { (*functor)(t); } + void getResult() {} +}; + +template <class V, class F> +struct ApplyVisitorBase : public V, public FunctionAndResult<F> { + using V::visit; +}; + +// Specialize for each visitor type +template <class V, class F> struct ApplyVisitor; + +/** Apply a functor to a visitable object. + * The functor can have operator() overloads for each visitable type + * and/or templated operator(). + */ +template <class F, class Visitable> +typename F::result_type apply(F& functor, Visitable& visitable) { + ApplyVisitor<typename Visitable::Visitor, F> visitor; + visitor.functor=&functor; + visitable.accept(visitor); + return visitor.getResult(); +} + +template <class F, class Visitable> +typename F::result_type apply(const F& functor, Visitable& visitable) { + ApplyVisitor<typename Visitable::Visitor, const F> visitor; + visitor.functor=&functor; + visitable.accept(visitor); + return visitor.getResult(); +} + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_APPLY_H*/ diff --git a/cpp/src/qpid/amqp_0_10/built_in_types.h b/cpp/src/qpid/amqp_0_10/built_in_types.h index 445f07459c..13bcdf862e 100644 --- a/cpp/src/qpid/amqp_0_10/built_in_types.h +++ b/cpp/src/qpid/amqp_0_10/built_in_types.h @@ -1,6 +1,5 @@ #ifndef QPID_AMQP_0_10_BUILT_IN_TYPES_H #define QPID_AMQP_0_10_BUILT_IN_TYPES_H -// FIXME aconway 2008-02-20: separate _fwd.h from full include. /* * * Licensed to the Apache Software Foundation (ASF) under one @@ -29,6 +28,7 @@ #include <boost/array.hpp> #include <stdint.h> #include <string> +#include <ostream> #include <vector> /**@file Mapping from built-in AMQP types to C++ types */ @@ -53,15 +53,19 @@ typedef uint64_t Uint64; typedef uint8_t Bin8; typedef uint8_t Uint8; -typedef boost::array<uint8_t,128> Bin1024; -typedef boost::array<uint8_t,16> Bin128; -typedef boost::array<uint8_t,2> Bin16; -typedef boost::array<uint8_t,32> Bin256; -typedef boost::array<uint8_t,4> Bin32; -typedef boost::array<uint8_t,5> Bin40; -typedef boost::array<uint8_t,64> Bin512; -typedef boost::array<uint8_t,8> Bin64; -typedef boost::array<uint8_t,9> Bin72; +template <size_t N> struct Bin : public boost::array<char, N> { + template <class S> void serialize(S& s) { s.raw(this->begin(), this->size()); } +}; + +typedef Bin<128> Bin1024; +typedef Bin<16> Bin128; +typedef Bin<2> Bin16; +typedef Bin<32> Bin256; +typedef Bin<4> Bin32; +typedef Bin<5> Bin40; +typedef Bin<64> Bin512; +typedef Bin<8> Bin64; +typedef Bin<9> Bin72; typedef double Double; typedef float Float; @@ -75,25 +79,35 @@ typedef Decimal<Uint8, Int64> Dec64; /** Template for length-prefixed strings/arrays. */ template <class T, class SizeType> -struct CodableString : public std::basic_string<T> {}; +struct SerializableString : public std::basic_string<T> { + using std::basic_string<T>::operator=; + template <class S> void serialize(S& s) { + s(SizeType(this->size())).iterate(this->begin(), this->end()); + } +}; + +// TODO aconway 2008-02-29: separate ostream ops +template <class T, class SizeType> +std::ostream& operator<<(std::ostream& o, const SerializableString<T,SizeType>& s) { + const std::basic_string<T> str(s); + return o << str.c_str(); // TODO aconway 2008-02-29: why doesn't o<<str work? +} // Variable width types -typedef CodableString<Uint8, Uint8> Vbin8; -typedef CodableString<char, Uint8> Str8Latin; -typedef CodableString<char, Uint8> Str8; -typedef CodableString<Uint16, Uint8> Str8Utf16; - -typedef CodableString<Uint8, Uint16> Vbin16; -typedef CodableString<char, Uint16> Str16Latin; -typedef CodableString<char, Uint16> Str16; -typedef CodableString<Uint16, Uint16> Str16Utf16; +typedef SerializableString<Uint8, Uint8> Vbin8; +typedef SerializableString<char, Uint8> Str8Latin; +typedef SerializableString<char, Uint8> Str8; +typedef SerializableString<Uint16, Uint8> Str8Utf16; -typedef CodableString<Uint8, Uint32> Vbin32; +typedef SerializableString<Uint8, Uint16> Vbin16; +typedef SerializableString<char, Uint16> Str16Latin; +typedef SerializableString<char, Uint16> Str16; +typedef SerializableString<Uint16, Uint16> Str16Utf16; -// FIXME aconway 2008-02-26: array encoding -template <class T> struct Array : public std::vector<T> {}; +typedef SerializableString<Uint8, Uint32> Vbin32; // FIXME aconway 2008-02-26: Unimplemented types: +template <class T> struct Array : public std::vector<T> {}; struct ByteRanges {}; struct SequenceSet {}; struct Map {}; diff --git a/cpp/src/qpid/amqp_0_10/helpers.cpp b/cpp/src/qpid/amqp_0_10/helpers.cpp index 4333a2cd92..457abe2d5f 100644 --- a/cpp/src/qpid/amqp_0_10/helpers.cpp +++ b/cpp/src/qpid/amqp_0_10/helpers.cpp @@ -19,6 +19,9 @@ * */ #include "helpers.h" +#include "qpid/amqp_0_10/CommandVisitor.h" +#include "qpid/amqp_0_10/ControlVisitor.h" +#include "qpid/amqp_0_10/StructVisitor.h" namespace qpid { namespace amqp_0_10 { diff --git a/cpp/src/qpid/amqp_0_10/helpers.h b/cpp/src/qpid/amqp_0_10/helpers.h index 1769d374d9..fc9a3e16a4 100644 --- a/cpp/src/qpid/amqp_0_10/helpers.h +++ b/cpp/src/qpid/amqp_0_10/helpers.h @@ -24,7 +24,6 @@ n * "License"); you may not use this file except in compliance #include <string> namespace qpid { - namespace amqp_0_10 { // Look up names by code @@ -35,19 +34,19 @@ const char* getStructName(uint8_t classCode, uint8_t code); struct Command { virtual ~Command(); - class Visitor; + struct Visitor; virtual void accept(Visitor&) const = 0; }; struct Control { virtual ~Control(); - class Visitor; + struct Visitor; virtual void accept(Visitor&) const = 0; }; struct Struct { virtual ~Struct(); - class Visitor; + struct Visitor; virtual void accept(Visitor&) const = 0; }; diff --git a/cpp/src/qpid/amqp_0_10/visitors.h b/cpp/src/qpid/amqp_0_10/visitors.h deleted file mode 100644 index 3835f37f3e..0000000000 --- a/cpp/src/qpid/amqp_0_10/visitors.h +++ /dev/null @@ -1,15 +0,0 @@ -// Visitors -template <class Base> struct Visitor; -template <class Base, class F, class R> FunctorVisitor; - -/** Template base implementation for visitables. */ -template <class Base, class Derived> -struct VisitableBase : public Base { - virtual void accept(Visitor<Derived>& v) { - v.visit(static_cast<Derived>&(*this)); - } - virtual void accept(Visitor<Derived>& v) const { - v.visit(static_cast<const Derived>&(*this)); - } -}; - diff --git a/cpp/src/qpid/framing/Uuid.h b/cpp/src/qpid/framing/Uuid.h index 278a60c439..bce18f55b3 100644 --- a/cpp/src/qpid/framing/Uuid.h +++ b/cpp/src/qpid/framing/Uuid.h @@ -67,7 +67,7 @@ struct Uuid : public boost::array<uint8_t, 16> { std::string str() const; template <class S> void serialize(S& s) { - s(static_cast<boost::array<uint8_t, 16>&>(*this)); + s.raw(begin(), size()); } }; diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am index 4a47797350..d25378a519 100644 --- a/cpp/src/tests/Makefile.am +++ b/cpp/src/tests/Makefile.am @@ -39,7 +39,7 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \ ISList.cpp IList.cpp \ ClientSessionTest.cpp \ serialize.cpp \ - ProxyTemplate.cpp + ProxyTemplate.cpp apply.cpp # FIXME aconway 2008-02-20: removed RefCountedMap.cpp due to valgrind error. check_LTLIBRARIES += libshlibtest.la diff --git a/cpp/src/tests/apply.cpp b/cpp/src/tests/apply.cpp new file mode 100644 index 0000000000..553026a35c --- /dev/null +++ b/cpp/src/tests/apply.cpp @@ -0,0 +1,92 @@ +/* + * + * 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 "unit_test.h" +#include "qpid/amqp_0_10/specification.h" +#include "qpid/amqp_0_10/ApplyControl.h" + +QPID_AUTO_TEST_SUITE(VisitorTestSuite) + +using namespace qpid::amqp_0_10; + +struct GetCode { + typedef uint8_t result_type; + template <class T> uint8_t operator()(const T&) const { return T::CODE; } +}; + +struct TestFunctor { + typedef bool result_type; + bool operator()(const connection::Tune& tune) { + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); + return true; + } + template <class T> + bool operator()(const T&) { return false; } +}; + +BOOST_AUTO_TEST_CASE(testApply) { + connection::Tune tune(1,2,3,4); + Control* p = &tune; + + // boost oddity - without the cast we get undefined symbol errors. + BOOST_CHECK_EQUAL(apply(GetCode(), *p), (uint8_t)connection::Tune::CODE); + + TestFunctor tf; + BOOST_CHECK(apply(tf, *p)); + + connection::Start start; + p = &start; + BOOST_CHECK(!apply(tf, *p)); +} + +struct VoidTestFunctor { + typedef void result_type; + + int code; + VoidTestFunctor() : code() {} + + void operator()(const connection::Tune& tune) { + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); + code=connection::Tune::CODE; + } + template <class T> + void operator()(const T&) { code=0xFF; } +}; + +BOOST_AUTO_TEST_CASE(testApplyVoid) { + connection::Tune tune(1,2,3,4); + Control* p = &tune; + VoidTestFunctor tf; + apply(tf, *p); + BOOST_CHECK_EQUAL(uint8_t(connection::Tune::CODE), tf.code); + + connection::Start start; + p = &start; + apply(tf, *p); + BOOST_CHECK_EQUAL(0xFF, tf.code); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/cpp/src/tests/serialize.cpp b/cpp/src/tests/serialize.cpp index a120be6458..8de2d4ca58 100644 --- a/cpp/src/tests/serialize.cpp +++ b/cpp/src/tests/serialize.cpp @@ -31,7 +31,7 @@ #include <boost/mpl/empty_sequence.hpp> #include <iterator> #include <string> -#include <ostream> +#include <iostream> #include <netinet/in.h> // Missing operators needed for tests. @@ -52,15 +52,6 @@ std::ostream& operator<<(std::ostream& out, const AbsTime& t) { } } -namespace amqp_0_10 { -template <class T, class SizeType> -std::ostream& operator<<(std::ostream& out, const CodableString<T,SizeType>& str) { - std::ostream_iterator<T> o(out, " "); - std::copy(str.begin(), str.end(), o); - return out; -} -} - } // qpid @@ -90,34 +81,37 @@ typedef concat2<FixedSizeTypes, VariableSizeTypes>::type AllTypes; BOOST_AUTO_TEST_CASE(testNetworkByteOrder) { string data; - uint32_t l = 1234567890; + uint32_t l = 0x11223344; Codec::encode(std::back_inserter(data), l); uint32_t enc=reinterpret_cast<const uint32_t&>(*data.data()); uint32_t l2 = ntohl(enc); BOOST_CHECK_EQUAL(l, l2); data.clear(); - uint16_t s = 12345; + uint16_t s = 0x1122; Codec::encode(std::back_inserter(data), s); uint32_t s2 = ntohs(*reinterpret_cast<const uint32_t*>(data.data())); BOOST_CHECK_EQUAL(s, s2); } +// Assign test values to the various types. void testValue(bool& b) { b = true; } template <class T> typename boost::enable_if<boost::is_arithmetic<T> >::type testValue(T& n) { n=42; } -void testValue(long long& l) { l = 12345; } +void testValue(long long& l) { l = 0x012345; } void testValue(Datetime& dt) { dt = qpid::sys::now(); } void testValue(Uuid& uuid) { uuid=Uuid(true); } -template <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; d.mantissa=1234; } +template <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; d.mantissa=0x1122; } void testValue(SequenceNo& s) { s = 42; } -template <class T, size_t N> void testValue(boost::array<T,N>& a) { a.assign(42); } -template <class T, class SizeType> void testValue(CodableString<T, SizeType>& s) { +template <size_t N> void testValue(Bin<N>& a) { a.assign(42); } +template <class T, class S> void testValue(SerializableString<T, S>& s) { char msg[]="foobar"; s.assign(msg, msg+sizeof(msg)); } +void testValue(Str16& s) { s = "the quick brown fox jumped over the lazy dog"; } +void testValue(Str8& s) { s = "foobar"; } -// FIXME aconway 2008-02-20: test AllTypes -BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, FixedSizeTypes) +//typedef mpl::vector<Str8, Str16>::type TestTypes; +BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes) { string data; T t; |