diff options
author | Alan Conway <aconway@apache.org> | 2008-04-10 12:36:58 +0000 |
---|---|---|
committer | Alan Conway <aconway@apache.org> | 2008-04-10 12:36:58 +0000 |
commit | 599ff4a59af78af75639a05a3e87a29f81a29e2b (patch) | |
tree | fc7bf12213abb6c2cdbfa8dd658cbc2c62f47741 | |
parent | 0e16f6997fe87a5241d0cbbd6ad6b0df1bfffc8f (diff) | |
download | qpid-python-599ff4a59af78af75639a05a3e87a29f81a29e2b.tar.gz |
Invocation handlers, see src/tests/amqp_0_10/handlers.cpp for example.
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@646778 13f79535-47bb-0310-9956-ffa450edef68
-rwxr-xr-x | cpp/rubygen/0-10/handlers.rb | 29 | ||||
-rwxr-xr-x | cpp/rubygen/0-10/specification.rb | 25 | ||||
-rwxr-xr-x | cpp/rubygen/cppgen.rb | 1 | ||||
-rw-r--r-- | cpp/src/qpid/amqp_0_10/Holder.h | 50 | ||||
-rw-r--r-- | cpp/src/qpid/amqp_0_10/Unit.h | 9 | ||||
-rw-r--r-- | cpp/src/tests/Makefile.am | 3 | ||||
-rw-r--r-- | cpp/src/tests/amqp_0_10/handlers.cpp | 125 |
7 files changed, 220 insertions, 22 deletions
diff --git a/cpp/rubygen/0-10/handlers.rb b/cpp/rubygen/0-10/handlers.rb new file mode 100755 index 0000000000..c23eb5faf4 --- /dev/null +++ b/cpp/rubygen/0-10/handlers.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class GenHandlers < CppGen + def initialize(outdir, amqp) + super(outdir, amqp) + @ns="qpid::amqp_#{@amqp.version.bars}" + @dir="qpid/amqp_#{@amqp.version.bars}" + end + + def action_handler(type, actions) + genl + bases=actions.map { |a| "public #{a.fqclassname}::Handler" } + struct("#{type}Handler", *bases) { } + end + + def generate() + h_file("#{@dir}/handlers.h") { + include "specification" + namespace("#{@ns}") { + action_handler "Command", @amqp.collect_all(AmqpCommand) + action_handler "Control", @amqp.collect_all(AmqpControl) + } + } + end +end + +GenHandlers.new($outdir, $amqp).generate() diff --git a/cpp/rubygen/0-10/specification.rb b/cpp/rubygen/0-10/specification.rb index abc754d1ef..020cb63c89 100755 --- a/cpp/rubygen/0-10/specification.rb +++ b/cpp/rubygen/0-10/specification.rb @@ -69,7 +69,7 @@ class Specification < CppGen genl "bool operator==(const #{x.classname}&, const #{x.classname}&);" end - def action_struct_cpp(x) + def action_struct_cpp(x, &block) genl genl "const char* #{x.classname}::NAME=\"#{x.fqname}\";" genl "const char* #{x.classname}::CLASS_NAME=#{x.containing_class.nsname}::NAME;" @@ -92,6 +92,7 @@ class Specification < CppGen genl "o << \"]\";" genl "return o;" } + yield if block end # structs @@ -103,13 +104,28 @@ class Specification < CppGen def action_h(a) action_struct_h(a, a.base, ["code"]) { - function_defn("template <class T> void invoke", ["T& target"]) { - genl "target.#{a.funcname}(#{a.values.join(', ')});" + struct("Handler") { + scope("void #{a.funcname}(", ");") { + genl a.parameters.join(",\n") + } + } + function_defn("template <class T> void invoke", ["T& target"], "const") { + genl "target.#{a.funcname}(#{a.values.join(', ')} );" } } end - def action_cpp(a) action_struct_cpp(a); end + def action_cpp(a) + action_struct_cpp(a) { + scope("void #{a.classname}::Handler::#{a.funcname}(", ")") { + genl a.unused_parameters.join(",\n") + } + scope { + genl "assert(0);" + genl "throw NotImplementedException(QPID_MSG(\"#{a.fqname} not implemented.\"));" + } + } + end # Types that must be generated early because they are used by other types. def pregenerate?(x) not @amqp.used_by[x.fqname].empty?; end @@ -172,6 +188,7 @@ class Specification < CppGen cpp_file("#{@dir}/specification") { include "#{@dir}/specification" + include "#{@dir}/exceptions" include "<iostream>" # FIXME aconway 2008-03-04: add Struct visitors. ["Command","Control"].each { |x| include "#{@dir}/Apply#{x}" } diff --git a/cpp/rubygen/cppgen.rb b/cpp/rubygen/cppgen.rb index d0f31a8ba8..0c17b68335 100755 --- a/cpp/rubygen/cppgen.rb +++ b/cpp/rubygen/cppgen.rb @@ -152,6 +152,7 @@ end module AmqpHasFields def parameters() fields.map { |f| "#{f.paramtype} #{f.cppname}_"} end + def unused_parameters() fields.map { |f| "#{f.paramtype} /*#{f.cppname}_*/"} end def arguments() fields.map { |f| "#{f.cppname}_"} end def values() fields.map { |f| "#{f.cppname}"} end def initializers() fields.map { |f| "#{f.cppname}(#{f.cppname}_)"} end diff --git a/cpp/src/qpid/amqp_0_10/Holder.h b/cpp/src/qpid/amqp_0_10/Holder.h index 4c1fccd44f..1664afcc8f 100644 --- a/cpp/src/qpid/amqp_0_10/Holder.h +++ b/cpp/src/qpid/amqp_0_10/Holder.h @@ -27,29 +27,36 @@ namespace qpid { namespace amqp_0_10 { +using framing::in_place; + +template <class Invokable> struct InvokeVisitor { + typedef void result_type; + Invokable& target; + InvokeVisitor(Invokable& i) : target(i) {} + + template <class Action> + void operator()(const Action& action) { action.invoke(target); } +}; + template <class DerivedHolder, class BaseHeld, size_t Size> -struct Holder : public framing::Blob<Size, BaseHeld> { +class Holder : public framing::Blob<Size, BaseHeld> { typedef framing::Blob<Size, BaseHeld> Base; - - struct Assign : public ApplyFunctor<void> { - Holder& holder; - Assign(Holder& x) : holder(x) {} - template <class T> void operator()(const T& rhs) { holder=rhs; } - }; + public: + Holder() {} - Holder(const BaseHeld& x) { *this=x; } - template <class T> Holder(const T& value) : Base(value) {} + template <class T> explicit Holder(const T& value) : Base(value) {} using Base::operator=; - Holder& operator=(const BaseHeld& rhs) { - Assign assign(*this); - apply(assign, rhs); - return *this; - } + Holder& operator=(const BaseHeld& rhs); uint8_t getCode() const { return this->get()->getCode(); } uint8_t getClassCode() const { return this->get()->getClassCode(); } + + template <class Invokable> void invoke(Invokable& i) const { + InvokeVisitor<Invokable> v(i); + apply(v, *this->get()); + } template <class S> void encode(S& s) const { s(getClassCode())(getCode()); @@ -65,8 +72,23 @@ struct Holder : public framing::Blob<Size, BaseHeld> { s.split(*this); apply(s, *this->get()); } + + private: + struct Assign : public ApplyFunctor<void> { + Holder& holder; + Assign(Holder& x) : holder(x) {} + template <class T> void operator()(const T& rhs) { holder=rhs; } + }; }; +template <class D, class B, size_t S> +Holder<D,B,S>& Holder<D,B,S>::operator=(const B& rhs) { + Assign assign(*this); + apply(assign, rhs); + return *this; +} + + }} // namespace qpid::amqp_0_10 diff --git a/cpp/src/qpid/amqp_0_10/Unit.h b/cpp/src/qpid/amqp_0_10/Unit.h index bcba36e35a..0229e07419 100644 --- a/cpp/src/qpid/amqp_0_10/Unit.h +++ b/cpp/src/qpid/amqp_0_10/Unit.h @@ -34,15 +34,12 @@ namespace qpid { namespace amqp_0_10 { - /** * A Unit contains a frame header and associated value. * For all types except BODY the frame header is for a complete segment. */ class Unit { public: - typedef boost::variant<ControlHolder, CommandHolder, Header, Body> Variant; - explicit Unit(const FrameHeader& h=FrameHeader()) : header(h) { updateVariant(); } /** @@ -57,12 +54,18 @@ class Unit { template<class T> const T* get() const { return boost::get<T>(&variant); } template<class T> T* get() { return boost::get<T>(&variant); } template<class T> Unit& operator=(const T& t) { variant=t; return *this; } + + template <class V> typename V::result_type applyVisitor(V& v) const { + variant.apply_visitor(v); + } template <class S> void serialize(S& s) { variant.apply_visitor(s); s.split(*this); } template <class S> void encode(S&) const {} template <class S> void decode(S&) { updateHeader(header.getFlags()); } private: + typedef boost::variant<ControlHolder, CommandHolder, Header, Body> Variant; + void updateHeader(uint8_t flags); void updateVariant(); diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am index 3f49d2c6af..7eddd9932e 100644 --- a/cpp/src/tests/Makefile.am +++ b/cpp/src/tests/Makefile.am @@ -43,7 +43,8 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \ amqp_0_10/ProxyTemplate.cpp \ amqp_0_10/apply.cpp \ IncompleteMessageList.cpp \ - amqp_0_10/Map.cpp + amqp_0_10/Map.cpp \ + amqp_0_10/handlers.cpp check_LTLIBRARIES += libshlibtest.la libshlibtest_la_LDFLAGS = -module -rpath $(abs_builddir) diff --git a/cpp/src/tests/amqp_0_10/handlers.cpp b/cpp/src/tests/amqp_0_10/handlers.cpp new file mode 100644 index 0000000000..035429a15d --- /dev/null +++ b/cpp/src/tests/amqp_0_10/handlers.cpp @@ -0,0 +1,125 @@ +/* + * + * 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/Exception.h" +#include "qpid/amqp_0_10/Unit.h" +#include "qpid/amqp_0_10/ControlHolder.h" +#include "qpid/amqp_0_10/CommandHolder.h" +#include "qpid/amqp_0_10/handlers.h" +#include "qpid/amqp_0_10/specification.h" + +QPID_AUTO_TEST_SUITE(handler_tests) + +using namespace qpid::amqp_0_10; +using namespace std; + +string called; // Set by called handler function + +// Note on handlers: +// +// Control and Command handlers are separate, both behave the same way, +// so substitute "control or command" for command in the following. +// +// Command handlers derive from CommandHandler and implement functions +// for all the commands they handle. Handling an unimplemented command +// will raise NotImplementedException. +// +// Using virtual inheritance from CommandHandler allows multiple +// handlers to be aggregated into one with multiple inheritance, +// See test code for example. +// +// E.g. the existing broker model would have two control handlers: +// - ConnectionHandler: ControlHandler for connection controls. +// - SessionHandler: ControlHandler for session controls. +// It would have class-command handlers for each AMQP class: +// - QueueHandler, MessageHandler etc.. handle each class. +// And an aggregate handler in place of BrokerAdapter +// - BrokerCommandHandler: public QueueHandler, MessageHandler ... +// +// In other applications (e.g. cluster) any combination of commands +// can be handled by a given handler. It _might_ simplify the code +// to collaps ConnectionHandler and SessionHandler into a single +// ControlHandler (or it might not.) + +struct TestExecutionHandler : public virtual CommandHandler { + void executionSync() { called = "executionSync"; } + // ... etc. for all execution commands +}; + +struct TestMessageHandler : public virtual CommandHandler { + void messageCancel(const Str8&) { called="messageCancel"; } + // ... etc. +}; + +// Aggregate handler for all recognised commands. +struct TestCommandHandler : + public TestExecutionHandler, + public TestMessageHandler + // ... etc. handlers for all command classes. +{}; // Nothing to do. + + +// Sample unit handler, written as a static_visitor. +// Note it could equally be written with if/else statements +// in handle. +// +struct TestUnitHandler : public boost::static_visitor<void> { + TestCommandHandler handler; + void handle(const Unit& u) { u.applyVisitor(*this); } + + void operator()(const Body&) { called="Body"; } + void operator()(const Header&) { called="Header"; } + void operator()(const ControlHolder&) { throw qpid::Exception("I don't do controls."); } + void operator()(const CommandHolder& c) { c.invoke(handler); } +}; + +BOOST_AUTO_TEST_CASE(testHandlers) { + TestUnitHandler handler; + Unit u; + + u = Body(); + handler.handle(u); + BOOST_CHECK_EQUAL("Body", called); + + u = Header(); + handler.handle(u); + BOOST_CHECK_EQUAL("Header", called); + + // in_place<Foo>(...) is equivalent to Foo(...) but + // constructs Foo directly in the holder, avoiding + // a copy. + + u = CommandHolder(in_place<execution::Sync>()); + handler.handle(u); + BOOST_CHECK_EQUAL("executionSync", called); + + u = ControlHolder(in_place<connection::Start>(Map(), Str16Array(), Str16Array())); + try { + handler.handle(u); + } catch (const qpid::Exception&) {} + + u = CommandHolder(in_place<message::Cancel>(Str8())); + handler.handle(u); + BOOST_CHECK_EQUAL("messageCancel", called); +} + +QPID_AUTO_TEST_SUITE_END() |