PROTON-865: cpp encode/decode support for complex types. Support for streaming complex AMQP types element by element and for inserting/extracting C++ containers as AMQP containers.
See examples/cpp/encode_decode.cpp for examples of use. Described types are not yet fully supported. Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/693752d3 Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/693752d3 Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/693752d3 Branch: refs/heads/cjansen-cpp-client Commit: 693752d37c918aed97f4b5bde25716f00ff27a8d Parents: c99be7a Author: Alan Conway <[email protected]> Authored: Fri Jun 12 13:47:48 2015 -0400 Committer: Alan Conway <[email protected]> Committed: Thu Jun 18 17:28:44 2015 -0400 ---------------------------------------------------------------------- examples/cpp/CMakeLists.txt | 12 +- examples/cpp/encode_decode.cpp | 257 ++++++++++++++++ examples/cpp/example_test.py | 41 ++- examples/cpp/simple_recv.cpp | 2 +- proton-c/CMakeLists.txt | 5 +- proton-c/bindings/cpp/CMakeLists.txt | 8 +- proton-c/bindings/cpp/include/proton/cpp/Data.h | 4 + .../bindings/cpp/include/proton/cpp/Decoder.h | 172 +++++++---- .../bindings/cpp/include/proton/cpp/Duration.h | 2 +- .../bindings/cpp/include/proton/cpp/Encoder.h | 84 ++++- .../bindings/cpp/include/proton/cpp/Exception.h | 49 --- .../cpp/include/proton/cpp/MessagingHandler.h | 2 +- .../bindings/cpp/include/proton/cpp/Value.h | 63 ++-- .../bindings/cpp/include/proton/cpp/Values.h | 56 ++++ .../bindings/cpp/include/proton/cpp/types.h | 246 ++++++++++----- proton-c/bindings/cpp/src/ConnectionImpl.h | 2 +- proton-c/bindings/cpp/src/ContainerImpl.cpp | 8 - proton-c/bindings/cpp/src/Data.cpp | 4 +- proton-c/bindings/cpp/src/Decoder.cpp | 83 ++++- proton-c/bindings/cpp/src/Encoder.cpp | 74 ++++- proton-c/bindings/cpp/src/Handler.cpp | 6 +- proton-c/bindings/cpp/src/MessagingAdapter.cpp | 10 +- proton-c/bindings/cpp/src/MessagingHandler.cpp | 2 +- proton-c/bindings/cpp/src/Msg.h | 22 +- proton-c/bindings/cpp/src/ProtonEvent.cpp | 3 +- proton-c/bindings/cpp/src/ProtonHandler.cpp | 2 +- proton-c/bindings/cpp/src/Transport.cpp | 2 +- proton-c/bindings/cpp/src/Value.cpp | 111 +++++-- proton-c/bindings/cpp/src/Values.cpp | 39 +++ proton-c/bindings/cpp/src/interop_test.cpp | 8 +- proton-c/bindings/cpp/src/proton_bits.cpp | 8 + proton-c/bindings/cpp/src/proton_bits.h | 5 + proton-c/bindings/cpp/src/types.cpp | 47 ++- proton-c/bindings/go/README.md | 5 - .../proton/go/amqp/interop_test.go | 308 ------------------- proton-c/docs/api/index.md | 6 +- proton-c/docs/api/user.doxygen.in | 2 +- proton-c/include/proton/codec.h | 9 +- proton-c/src/codec/codec.c | 10 +- proton-c/src/codec/encoder.c | 10 +- 40 files changed, 1106 insertions(+), 683 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/examples/cpp/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index b1e4a1f..bafcd38 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -17,11 +17,19 @@ # under the License. # -include_directories ("${CMAKE_SOURCE_DIR}/proton-c/bindings/cpp/include") +include_directories("${CMAKE_SOURCE_DIR}/proton-c/bindings/cpp/include") -foreach(example broker helloworld helloworld_blocking helloworld_direct simple_recv simple_send) +foreach(example + broker + helloworld + helloworld_blocking + helloworld_direct + simple_recv + simple_send + encode_decode) add_executable(${example} ${example}.cpp) target_link_libraries(${example} qpid-proton-cpp) + set_source_files_properties(${example}.cpp PROPERTIES COMPILE_FLAGS "${CXX_WARNING_FLAGS}") endforeach() add_test( http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/examples/cpp/encode_decode.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/encode_decode.cpp b/examples/cpp/encode_decode.cpp new file mode 100644 index 0000000..5096c40 --- /dev/null +++ b/examples/cpp/encode_decode.cpp @@ -0,0 +1,257 @@ +/* + * 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 <proton/cpp/Values.h> +#include <proton/cpp/Value.h> +#include <algorithm> +#include <iostream> +#include <iterator> +#include <map> +#include <sstream> +#include <stdexcept> +#include <vector> + +using namespace std; +using namespace proton::reactor; + +// Examples of how to use the Encoder and Decoder to create and examine AMQP values. +// + +// Print is defined at the end as an example of how to query and extract complex +// values in terms of their simple components. +void print(Values& values); + +// Inserting and extracting simple C++ values. +void simple_insert_extract() { + Values values; + cout << endl << "== Simple values: int, string, bool" << endl; + values << 42 << "foo" << true; + print(values); + int i; + std::string s; + bool b; + values.rewind(); + values >> i >> s >> b; + cout << "Extracted: " << i << ", " << s << ", " << b << endl; + cout << "Encoded as AMQP in " << values.encode().size() << " bytes" << endl; +} + +// Inserting values as a specific AMQP type +void simple_insert_extract_exact_type() { + Values values; + cout << endl << "== Specific AMQP types: byte, long, symbol" << endl; + values << Byte('x') << Long(123456789123456789) << Symbol("bar"); + print(values); + values.rewind(); + // Check that we encoded the correct types, but note that decoding will + // still convert to standard C++ types, in particular any AMQP integer type + // can be converted to a long-enough C++ integer type.. + int64_t i1, i2; + std::string s; + values >> i1 >> i2 >> s; + cout << "Extracted (with conversion) " << i1 << ", " << i2 << ", " << s << endl; + + // Now use the as() function to fail unless we extract the exact AMQP type expected. + values.rewind(); // Byte(1) << Long(2) << Symbol("bar"); + Long l; + // Fails, extracting Byte as Long + try { values >> as<LONG>(l); throw logic_error("expected error"); } catch (Decoder::Error) {} + Byte b; + values >> as<BYTE>(b) >> as<LONG>(l); // OK, extract Byte as Byte, Long as Long. + std::string str; + // Fails, extracting Symbol as String. + try { values >> as<STRING>(str); throw logic_error("expected error"); } catch (Decoder::Error) {} + values >> as<SYMBOL>(str); // OK, extract Symbol as Symbol + cout << "Extracted (exact) " << b << ", " << l << ", " << str << endl; +} + +// Some helper templates to print map and vector results. +namespace std { +template<class T, class U> ostream& operator<<(ostream& o, const pair<T,U>& p) { + return o << p.first << ":" << p.second; +} +template<class T> ostream& operator<<(ostream& o, const vector<T>& v) { + o << "[ "; + ostream_iterator<T> oi(o, " "); + copy(v.begin(), v.end(), oi); + return o << "]"; +} +template<class K, class T> ostream& operator<<(ostream& o, const map<K, T>& m) { + o << "{ "; + ostream_iterator<pair<K,T> > oi(o, " "); + copy(m.begin(), m.end(), oi); + return o << "}"; +} +} + +// Insert/extract C++ containers. +void insert_extract_containers() { + cout << endl << "== Array, list and map." << endl; + + vector<int> a; + a.push_back(1); + a.push_back(2); + a.push_back(3); + vector<int> l; + l.push_back(4); + l.push_back(5); + map<string, int> m; + m["one"] = 1; + m["two"] = 2; + + Values values; + values << as<ARRAY>(a) << as<LIST>(l) << as<MAP>(m); + print(values); + + vector<int> a1, l1; + map<string, int> m1; + values.rewind(); + values >> as<ARRAY>(a1) >> as<LIST>(l1) >> as<MAP>(m1); + cout << "Extracted: " << a1 << ", " << l1 << ", " << m1 << endl; +} + +// Containers with mixed types, use Value to represent arbitrary AMQP types. +void mixed_containers() { + cout << endl << "== List and map of mixed type values." << endl; + vector<Value> l; + l.push_back(Value(42)); + l.push_back(Value(String("foo"))); + map<Value, Value> m; + m[Value("five")] = Value(5); + m[Value(4)] = Value("four"); + Values values; + values << as<LIST>(l) << as<MAP>(m); + print(values); + + vector<Value> l1; + map<Value, Value> m1; + values.rewind(); + values >> as<LIST>(l1) >> as<MAP>(m1); + cout << "Extracted: " << l1 << ", " << m1 << endl; +} + +// Insert using stream operators (see printNext for example of extracting with stream ops.) +void insert_extract_stream_operators() { + cout << endl << "== Insert with stream operators." << endl; + Values values; + // Note: array elements must be encoded with the exact type, they are not + // automaticlly converted. Mismatched types for array elements will not + // be detected until values.encode() is called. + values << Start::array(INT) << Int(1) << Int(2) << Int(3) << finish(); + print(values); + + values.clear(); + values << Start::list() << Int(42) << false << Symbol("x") << finish(); + print(values); + + values.clear(); + values << Start::map() << "k1" << Int(42) << Symbol("k2") << false << finish(); + print(values); +} + +int main(int, char**) { + try { + simple_insert_extract(); + simple_insert_extract_exact_type(); + insert_extract_containers(); + mixed_containers(); + insert_extract_stream_operators(); + } catch (const exception& e) { + cerr << endl << "error: " << e.what() << endl; + return 1; + } +} + +// printNext prints the next value from Values by recursively descending into complex values. +// +// NOTE this is for example puroses only: There is a built in ostream operator<< for Values. +// +// +void printNext(Values& values) { + TypeId type = values.type(); + Start start; + switch (type) { + case ARRAY: { + values >> start; + cout << "array<" << start.element; + if (start.isDescribed) { + cout << ", descriptor="; + printNext(values); + } + cout << ">["; + for (size_t i = 0; i < start.size; ++i) { + if (i) cout << ", "; + printNext(values); + } + cout << "]"; + values >> finish(); + break; + } + case LIST: { + values >> start; + cout << "list["; + for (size_t i = 0; i < start.size; ++i) { + if (i) cout << ", "; + printNext(values); + } + cout << "]"; + values >> finish(); + break; + } + case MAP: { + values >> start; + cout << "map{"; + for (size_t i = 0; i < start.size/2; ++i) { + if (i) cout << ", "; + printNext(values); + cout << ":"; // key:value + printNext(values); + } + cout << "}"; + values >> finish(); + break; + } + case DESCRIBED: { + values >> start; + cout << "described("; + printNext(values); // Descriptor + printNext(values); // Value + values >> finish(); + break; + } + default: + // A simple type. We could continue the switch for all AMQP types but + // instead we us the `Value` type which can hold and print any AMQP + // value. + Value v; + values >> v; + cout << type << "(" << v << ")"; + } +} + +// Print all the values with printNext +void print(Values& values) { + values.rewind(); + cout << "Values: "; + while (values.more()) { + printNext(values); + if (values.more()) cout << ", "; + } + cout << endl; +} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/examples/cpp/example_test.py ---------------------------------------------------------------------- diff --git a/examples/cpp/example_test.py b/examples/cpp/example_test.py index 9a8ac67..3da1ddb 100644 --- a/examples/cpp/example_test.py +++ b/examples/cpp/example_test.py @@ -20,7 +20,7 @@ import unittest import os, sys, socket, time from random import randrange -from subprocess import Popen, check_output, PIPE +from subprocess import Popen, check_output, PIPE, STDOUT NULL = open(os.devnull, 'w') @@ -58,7 +58,7 @@ class Broker(object): class ExampleTest(unittest.TestCase): - """Test examples""" + """Run the examples, verify they behave as expected.""" @classmethod def tearDownClass(self): @@ -89,6 +89,8 @@ class ExampleTest(unittest.TestCase): recv_expect += "".join(['[%d]: b"some arbitrary binary data"\n' % (i+1) for i in range(n)]) self.assertEqual(recv_expect, recv) + # FIXME aconway 2015-06-16: bug when receiver is started before sender, messages + # are not delivered to receiver. def FIXME_test_simple_recv_send(self): """Start receiver first, then run sender""" b = Broker.get() @@ -101,5 +103,40 @@ class ExampleTest(unittest.TestCase): out, err = recv.communicate() self.assertEqual(recv_expect, out) + def call(self, *cmd): + p = Popen(cmd, stdout=PIPE, stderr=STDOUT) + out, err = p.communicate() + self.assertEqual(0, p.returncode, + "%s exit code %s, output:\n%s\n---- end of %s exit code %s" % ( + cmd, p.returncode, out, cmd, p.returncode)) + return out + + def test_encode_decode(self): + expect=""" +== Simple values: int, string, bool +Values: int(42), string("foo"), bool(true) +Extracted: 42, foo, 1 +Encoded as AMQP in 8 bytes + +== Specific AMQP types: byte, long, symbol +Values: byte(120), long(123456789123456789), symbol(:bar) +Extracted (with conversion) 120, 123456789123456789, bar +Extracted (exact) x, 123456789123456789, bar + +== Array, list and map. +Values: array<int>[int(1), int(2), int(3)], list[int(4), int(5)], map{string("one"):int(1), string("two"):int(2)} +Extracted: [ 1 2 3 ], [ 4 5 ], { one:1 two:2 } + +== List and map of mixed type values. +Values: list[int(42), string("foo")], map{int(4):string("four"), string("five"):int(5)} +Extracted: [ 42 "foo" ], { 4:"four" "five":5 } + +== Insert with stream operators. +Values: array<int>[int(1), int(2), int(3)] +Values: list[int(42), bool(false), symbol(:x)] +Values: map{string("k1"):int(42), symbol(:"k2"):bool(false)} +""" + self.maxDiff = None + self.assertMultiLineEqual(expect, self.call("./encode_decode")) if __name__ == "__main__": unittest.main() http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/examples/cpp/simple_recv.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/simple_recv.cpp b/examples/cpp/simple_recv.cpp index 42c561b..6612b16 100644 --- a/examples/cpp/simple_recv.cpp +++ b/examples/cpp/simple_recv.cpp @@ -46,7 +46,7 @@ class Recv : public MessagingHandler { } void onMessage(Event &e) { - uint64_t id = 0; + int64_t id = 0; Message msg = e.getMessage(); if (msg.getIdType() == PN_ULONG) { id = msg.getId(); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt index 049755e..9276a74 100644 --- a/proton-c/CMakeLists.txt +++ b/proton-c/CMakeLists.txt @@ -213,6 +213,8 @@ if (CMAKE_COMPILER_IS_GNUCC) set (WERROR "-Werror") endif (ENABLE_WARNING_ERROR) set (COMPILE_WARNING_FLAGS "${WERROR} -Wall -pedantic-errors") + # C++ allow "%z" format specifier and variadic macros + set (CXX_WARNING_FLAGS "${COMPILE_WARNING_FLAGS} -Wno-format -Wno-variadic-macros" CACHE STRING "C++ warning flags") if (NOT BUILD_WITH_CXX) set (COMPILE_WARNING_FLAGS "${COMPILE_WARNING_FLAGS} -Wstrict-prototypes") set (COMPILE_LANGUAGE_FLAGS "-std=c99") @@ -227,8 +229,7 @@ if (CMAKE_COMPILER_IS_GNUCC) set (COMPILE_WARNING_FLAGS "${COMPILE_WARNING_FLAGS} -Wc++-compat -Wvla -Wsign-compare -Wwrite-strings") endif (${GCC_VERSION} VERSION_LESS "4.3.0") else (NOT BUILD_WITH_CXX) - # allow "%z" format specifier and variadic macros - set (COMPILE_WARNING_FLAGS "${COMPILE_WARNING_FLAGS} -Wno-format -Wno-variadic-macros") + set (COMPILE_WARNING_FLAGS "${CXX_WARNING_FLAGS}") endif (NOT BUILD_WITH_CXX) if (ENABLE_UNDEFINED_ERROR) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/CMakeLists.txt b/proton-c/bindings/cpp/CMakeLists.txt index cc1c74a..18533ec 100644 --- a/proton-c/bindings/cpp/CMakeLists.txt +++ b/proton-c/bindings/cpp/CMakeLists.txt @@ -22,8 +22,7 @@ include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/src") include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/include") -add_library ( - qpid-proton-cpp SHARED +set(qpid-proton-cpp-source src/Acceptor.cpp src/Acking.cpp src/Connection.cpp @@ -54,6 +53,7 @@ add_library ( src/Transport.cpp src/Url.cpp src/Value.cpp + src/Values.cpp src/proton_bits.cpp src/blocking/BlockingConnection.cpp src/blocking/BlockingConnectionImpl.cpp @@ -63,6 +63,10 @@ add_library ( src/types.cpp ) +set_source_files_properties(${qpid-proton-cpp-source} PROPERTIES COMPILE_FLAGS "${CXX_WARNING_FLAGS}") + +add_library(qpid-proton-cpp SHARED ${qpid-proton-cpp-source}) + target_link_libraries (qpid-proton-cpp ${PLATFORM_LIBS} qpid-proton) set_target_properties ( http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/include/proton/cpp/Data.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/cpp/Data.h b/proton-c/bindings/cpp/include/proton/cpp/Data.h index 2204e8f..ef78a13 100644 --- a/proton-c/bindings/cpp/include/proton/cpp/Data.h +++ b/proton-c/bindings/cpp/include/proton/cpp/Data.h @@ -22,6 +22,10 @@ #include "proton/cpp/ImportExport.h" #include <iosfwd> +/**@file + * Base for classes that hold AMQP data. + * @internal + */ struct pn_data_t; namespace proton { http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/include/proton/cpp/Decoder.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/cpp/Decoder.h b/proton-c/bindings/cpp/include/proton/cpp/Decoder.h index 542315e..9b6df6e 100644 --- a/proton-c/bindings/cpp/include/proton/cpp/Decoder.h +++ b/proton-c/bindings/cpp/include/proton/cpp/Decoder.h @@ -29,43 +29,40 @@ namespace reactor { class Value; +/**@file + * Stream-like decoder from AMQP bytes to C++ values. + * @ingroup cpp + */ + /** +@ingroup cpp + Stream-like decoder from AMQP bytes to a stream of C++ values. -@see types.h defines C++ typedefs and types for AMQP each type. These types can -all be extracted from the corresponding AMQP type. In additon operator>> will do -the following conversions from AMQP to C++ types: - -+-----------------------------------------+--------------------------------------------------+ -|Target C++ type |Allowed AMQP types | -+=========================================+==================================================+ -|bool |Bool | -|-----------------------------------------+--------------------------------------------------| -|signed integer type |Byte,Short,Int,Long [1] | -+-----------------------------------------+--------------------------------------------------+ -|unsigned integer type |UByte,UShort,UInt,ULong [1] | -+-----------------------------------------+--------------------------------------------------+ -|float or double |Float or Double | -+-----------------------------------------+--------------------------------------------------+ -|Value |Any type | -+-----------------------------------------+--------------------------------------------------+ -|std::string |String, Binary, Symbol | -+-----------------------------------------+--------------------------------------------------+ -|wchar_t |Char | -+-----------------------------------------+--------------------------------------------------+ -|std::map<K, T> |Map with keys that convert to K and data that | -| |converts to T | -+-----------------------------------------+--------------------------------------------------+ -|Map |Map may have mixed keys and data types | -+-----------------------------------------+--------------------------------------------------+ -|std::vector<T> |List or Array if data converts to T | -+-----------------------------------------+--------------------------------------------------+ -|List |List, may have mixed types and datas | -+-----------------------------------------+--------------------------------------------------+ - -You can disable conversions and force an exact type match using @see exact() +types.h defines C++ types corresponding to AMQP types. + +Decoder operator>> will extract AMQP types into corresponding C++ types, and do +simple conversions, e.g. from AMQP integer types to corresponding or larger C++ +integer types. + +You can require an exact AMQP type using the `as<type>(value)` helper. E.g. + + Int i; + decoder >> as<INT>(i): // Will throw if decoder does not contain an INT + +You can also use the `as` helper to extract an AMQP list, array or map into C++ containers. + + std::vector<Int> v; + decoder >> as<LIST>(v); // Extract a list of INT. + +AMQP maps can be inserted/extracted to any container with pair<X,Y> as +value_type, which includes std::map and std::unordered_map but also for +example std::vector<std::pair<X,Y> >. This allows you to perserve order when +extracting AMQP maps. + +You can also extract container values element-by-element, see the Start class. */ -class Decoder : public virtual Data { +PN_CPP_EXTERN class Decoder : public virtual Data { public: /** Raised if a Decoder operation fails */ struct Error : public ProtonException { @@ -95,10 +92,12 @@ class Decoder : public virtual Data { */ PN_CPP_EXTERN TypeId type() const; - /** @defgroup decoder_simple_types Extract simple types, @see Decoder for details. - *@throw Error if the Decoder is empty or the current value has an incompatible type. - *@{ + /** @name Extract simple types + * Overloads to extract simple types. + * @throw Error if the Decoder is empty or the current value has an incompatible type. + * @{ */ + PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, Null); PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, Bool&); PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, Ubyte&); PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, Byte&); @@ -120,14 +119,59 @@ class Decoder : public virtual Data { PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, Value&); ///@} - ///@internal - template <class T> struct ExactRef { T& value; ExactRef(T& ref) : value(ref) {} }; + /** Extract and return a value of type T. */ + template <class T> T get() { T value; *this >> value; return value; } + + /** Extract and return a value of type T, as AMQP type. */ + template <class T, TypeId A> T getAs() { T value; *this >> as<A>(value); return value; } - /** @see exact() */ - template <class T> friend Decoder& operator>>(Decoder&, ExactRef<T>); + /** Call Decoder::start() in constructor, Decoder::finish in destructor() */ + struct Scope : public Start { + Decoder& decoder; + Scope(Decoder& d) : decoder(d) { d >> *this; } + ~Scope() { decoder >> finish(); } + }; + + template <TypeId A, class T> friend Decoder& operator>>(Decoder& d, Ref<T, A> ref) { + d.checkType(A); + d >> ref.value; + return d; + } + + /** start extracting a container value, one of array, list, map, described. + * The basic pattern is: + * + * Start s; + * decoder >> s; + * // check s.type() to see if this is an ARRAY, LIST, MAP or DESCRIBED type. + * if (s.described) extract the descriptor... + * for (size_t i = 0; i < s.size(); ++i) Extract each element... + * decoder >> finish(); + * + * The first value of an ARRAY is a descriptor if Start::descriptor is true, + * followed by Start::size elemets of type Start::element. + * + * A LIST has Start::size elements which may be of mixed type. + * + * A MAP has Start::size elements which alternate key, value, key, value... + * and may be of mixed type. + * + * A DESCRIBED contains a descriptor and a single element, so it always has + * Start::described=true and Start::size=1. + * + * Note Scope automatically calls finish() in its destructor. + * + *@throw decoder::error if the curent value is not a container type. + */ + PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, Start&); + + /** Finish extracting a container value. */ + PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, Finish); + + /** Skip a value */ + PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, Skip); private: - PN_CPP_EXTERN Decoder(pn_data_t*); template <class T> Decoder& extract(T& value); void checkType(TypeId); @@ -139,26 +183,36 @@ class Decoder : public virtual Data { friend class Encoder; }; -/** - * exact() disables the conversions allowed by Decoder operator>> and requires exact type match. - * - * For example the following will throw Decode::Error unless decoder conntains - * an AMQP bool and an AMQP ULong. - * - * @code - * Bool b; - * ULong ul; - * decoder >> exact(b) >> exact(ul) - * @code - */ -template <class T> Decoder::ExactRef<T> exact(T& value) { - return Decoder::ExactRef<T>(value); +template <class T> Decoder& operator>>(Decoder& d, Ref<T, ARRAY> ref) { + Decoder::Scope s(d); + if (s.isDescribed) d >> skip(); + ref.value.clear(); + ref.value.resize(s.size); + for (typename T::iterator i = ref.value.begin(); i != ref.value.end(); ++i) { + d >> *i; + } + return d; +} + +template <class T> Decoder& operator>>(Decoder& d, Ref<T, LIST> ref) { + Decoder::Scope s(d); + ref.value.clear(); + ref.value.resize(s.size); + for (typename T::iterator i = ref.value.begin(); i != ref.value.end(); ++i) + d >> *i; + return d; } -///@see exact() -template <class T> Decoder& operator>>(Decoder& d, Decoder::ExactRef<T> ref) { - d.checkType(TypeIdOf<T>::value); - d >> ref.value; +template <class T> Decoder& operator>>(Decoder& d, Ref<T, MAP> ref) { + Decoder::Scope m(d); + ref.value.clear(); + for (size_t i = 0; i < m.size/2; ++i) { + typename T::key_type k; + typename T::mapped_type v; + d >> k >> v; + ref.value[k] = v; + } + return d; } }} // namespace proton::reactor http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/include/proton/cpp/Duration.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/cpp/Duration.h b/proton-c/bindings/cpp/include/proton/cpp/Duration.h index 08aaf3f..bb2a063 100644 --- a/proton-c/bindings/cpp/include/proton/cpp/Duration.h +++ b/proton-c/bindings/cpp/include/proton/cpp/Duration.h @@ -28,7 +28,7 @@ namespace proton { namespace reactor { -/** \ingroup C++ +/** @ingroup cpp * A duration is a time in milliseconds. */ class Duration http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/include/proton/cpp/Encoder.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/cpp/Encoder.h b/proton-c/bindings/cpp/include/proton/cpp/Encoder.h index 460bea4..8e92881 100644 --- a/proton-c/bindings/cpp/include/proton/cpp/Encoder.h +++ b/proton-c/bindings/cpp/include/proton/cpp/Encoder.h @@ -31,13 +31,28 @@ namespace reactor { class Value; +/**@file + * Stream-like encoder from C++ values to AMQP bytes. + * @ingroup cpp +*/ + /** -Stream-like encoder from C++ values to AMQP bytes. +@ingroup cpp -@see types.h defines C++ typedefs and types for AMQP each type. These types +types.h defines C++ typedefs and types for AMQP each type. These types insert as the corresponding AMQP type. Normal C++ conversion rules apply if you insert any other type. +C++ containers can be inserted as AMQP containers with the as() helper functions. E.g. + + std::vector<Symbol> v; encoder << as<List>(v); + +AMQP maps can be inserted/extracted to any container with pair<X,Y> as +value_type, which includes std::map and std::unordered_map but also for +example std::vector<std::pair<X,Y> >. This allows you to perserve order when +extracting AMQP maps. + +You can also insert containers element-by-element, see the Start class. */ class Encoder : public virtual Data { public: @@ -68,9 +83,10 @@ class Encoder : public virtual Data { /** Encode the current values into a std::string. Clears the encoder. */ PN_CPP_EXTERN std::string encode(); - /** @defgroup encoder_simple_types Insert simple types. + /** @name Insert simple types. *@{ */ + PN_CPP_EXTERN friend Encoder& operator<<(Encoder&, Null); PN_CPP_EXTERN friend Encoder& operator<<(Encoder&, Bool); PN_CPP_EXTERN friend Encoder& operator<<(Encoder&, Ubyte); PN_CPP_EXTERN friend Encoder& operator<<(Encoder&, Byte); @@ -94,8 +110,25 @@ class Encoder : public virtual Data { PN_CPP_EXTERN friend Encoder& operator<<(Encoder&, const Value&); ///@} + /** Start a container type. See the Start class. */ + PN_CPP_EXTERN friend Encoder& operator<<(Encoder&, const Start&); + + /** Finish a container type. */ + PN_CPP_EXTERN friend Encoder& operator<<(Encoder& e, Finish); + + + /**@name Insert values returned by the as<TypeId> helper. + *@{ + */ + template <class T, TypeId A> friend Encoder& operator<<(Encoder&, CRef<T, A>); + template <class T> friend Encoder& operator<<(Encoder&, CRef<T, ARRAY>); + template <class T> friend Encoder& operator<<(Encoder&, CRef<T, LIST>); + template <class T> friend Encoder& operator<<(Encoder&, CRef<T, MAP>); + // TODO aconway 2015-06-16: DESCRIBED. + ///@} + private: - PN_CPP_EXTERN Encoder(pn_data_t* pd); // Does not own. + PN_CPP_EXTERN Encoder(pn_data_t* pd); // Not implemented Encoder(const Encoder&); @@ -104,5 +137,48 @@ class Encoder : public virtual Data { friend class Value; }; +/** Encode const char* as string */ +inline Encoder& operator<<(Encoder& e, const char* s) { return e << String(s); } + +/** Encode char* as string */ +inline Encoder& operator<<(Encoder& e, char* s) { return e << String(s); } + +/** Encode std::string as string */ +inline Encoder& operator<<(Encoder& e, const std::string& s) { return e << String(s); } + +//@internal Convert a Ref to a CRef. +template <class T, TypeId A> Encoder& operator<<(Encoder& e, Ref<T, A> ref) { + return e << CRef<T,A>(ref); +} + +// TODO aconway 2015-06-16: described array insertion. + +template <class T> Encoder& operator<<(Encoder& e, CRef<T, ARRAY> a) { + e << Start::array(TypeIdOf<typename T::value_type>::value); + for (typename T::const_iterator i = a.value.begin(); i != a.value.end(); ++i) + e << *i; + e << finish(); + return e; +} + +template <class T> Encoder& operator<<(Encoder& e, CRef<T, LIST> l) { + e << Start::list(); + for (typename T::const_iterator i = l.value.begin(); i != l.value.end(); ++i) + e << *i; + e << finish(); + return e; +} + +template <class T> Encoder& operator<<(Encoder& e, CRef<T, MAP> m){ + e << Start::map(); + for (typename T::const_iterator i = m.value.begin(); i != m.value.end(); ++i) { + e << i->first; + e << i->second; + } + e << finish(); + return e; +} + + }} #endif // ENCODER_H http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/include/proton/cpp/Exception.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/cpp/Exception.h b/proton-c/bindings/cpp/include/proton/cpp/Exception.h deleted file mode 100644 index d24c8ef..0000000 --- a/proton-c/bindings/cpp/include/proton/cpp/Exception.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef PROTON_CPP_EXCEPTIONS_H -#define PROTON_CPP_EXCEPTIONS_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 <stdexcept> - -namespace proton { -namespace reactor { - -class Exception : public std::runtime_error -{ - public: - explicit Exception(const std::string& msg) throw() : std::runtime_error(msg) {} -}; - -class MessageReject : public Exception -{ - public: - explicit MessageReject(const std::string& msg) throw() : Exception(msg) {} -}; - -class MessageRelease : public Exception -{ - public: - explicit MessageRelease(const std::string& msg) throw() : Exception(msg) {} -}; - -}} // namespace proton::reactor - -#endif /*!PROTON_CPP_EXCEPTIONS_H*/ http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/include/proton/cpp/MessagingHandler.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/cpp/MessagingHandler.h b/proton-c/bindings/cpp/include/proton/cpp/MessagingHandler.h index e6c0341..07b0dde 100644 --- a/proton-c/bindings/cpp/include/proton/cpp/MessagingHandler.h +++ b/proton-c/bindings/cpp/include/proton/cpp/MessagingHandler.h @@ -78,8 +78,8 @@ class PN_CPP_EXTERN MessagingHandler : public ProtonHandler , public Acking virtual void onTransportClosed(Event &e); protected: int prefetch; - bool autoSettle; bool autoAccept; + bool autoSettle; bool peerCloseIsError; MessagingAdapter *messagingAdapter; Handler *flowController; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/include/proton/cpp/Value.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/cpp/Value.h b/proton-c/bindings/cpp/include/proton/cpp/Value.h index 65ca7ec..9555f29 100644 --- a/proton-c/bindings/cpp/include/proton/cpp/Value.h +++ b/proton-c/bindings/cpp/include/proton/cpp/Value.h @@ -19,66 +19,61 @@ * under the License. */ -#include "proton/cpp/Encoder.h" -#include "proton/cpp/Decoder.h" -#include <iosfwd> +#include "proton/cpp/Values.h" +/**@file + * Holder for an AMQP value. + * @ingroup cpp + */ namespace proton { namespace reactor { -/** Holds a sequence of AMQP values, allows inserting and extracting. - * - * After inserting values, call rewind() to extract them. - */ -class Values : public Encoder, public Decoder { - public: - Values(); - Values(const Values&); - ~Values(); - - /** Copy data from another Values */ - Values& operator=(const Values&); - - PN_CPP_EXTERN void rewind(); - - private: - friend class Value; -}; - /** Holds a single AMQP value. */ -class Value { +PN_CPP_EXTERN class Value { public: PN_CPP_EXTERN Value(); PN_CPP_EXTERN Value(const Value&); + /** Converting constructor from any settable value */ + template <class T> explicit Value(const T& v); PN_CPP_EXTERN ~Value(); - PN_CPP_EXTERN Value& operator=(const Value&); + TypeId type() const; - /** Set the value */ + /** Set the value. */ template<class T> void set(const T& value); - /** Get the value */ + /** Get the value. */ template<class T> void get(T& value) const; /** Get the value */ template<class T> T get() const; /** Assignment sets the value */ template<class T> Value& operator=(const T& value); + /** Conversion operator gets the value */ template<class T> operator T() const; - /** Insert a value into an Encoder. */ + /** insert a value into an Encoder. */ PN_CPP_EXTERN friend Encoder& operator<<(Encoder&, const Value&); /** Extract a value from a decoder. */ - PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, const Value&); + PN_CPP_EXTERN friend Decoder& operator>>(Decoder&, Value&); + + /** Human readable format */ + PN_CPP_EXTERN friend std::ostream& operator<<(std::ostream&, const Value&); - friend Decoder& operator>>(Decoder&, Value&); - friend Encoder& operator<<(Encoder&, const Value&); + bool operator==(const Value&) const; + bool operator !=(const Value& v) const{ return !(*this == v); } - private: - Values values; + /** operator < makes Value valid for use as a std::map key. */ + bool operator<(const Value&) const; + bool operator>(const Value& v) const { return v < *this; } + bool operator<=(const Value& v) const { return !(*this > v); } + bool operator>=(const Value& v) const { return !(*this < v); } + + private: + mutable Values values; }; template<class T> void Value::set(const T& value) { @@ -88,8 +83,7 @@ template<class T> void Value::set(const T& value) { template<class T> void Value::get(T& value) const { Values& v = const_cast<Values&>(values); - v.rewind(); - v >> value; + v.rewind() >> value; } template<class T> T Value::get() const { T value; get(value); return value; } @@ -98,6 +92,7 @@ template<class T> Value& Value::operator=(const T& value) { set(value); return * template<class T> Value::operator T() const { return get<T>(); } +template<class T> Value::Value(const T& value) { set(value); } }} #endif // VALUE_H http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/include/proton/cpp/Values.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/cpp/Values.h b/proton-c/bindings/cpp/include/proton/cpp/Values.h new file mode 100644 index 0000000..5f62dd9 --- /dev/null +++ b/proton-c/bindings/cpp/include/proton/cpp/Values.h @@ -0,0 +1,56 @@ +#ifndef VALUES_H +#define VALUES_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 <proton/cpp/Encoder.h> +#include <proton/cpp/Decoder.h> + +/**@file + * Holder for a sequence of AMQP values. + * @ingroup cpp + */ + +namespace proton { +namespace reactor { + +/** Holds a sequence of AMQP values, allows inserting and extracting. + * + * After inserting values, call rewind() to extract them. + */ +PN_CPP_EXTERN class Values : public Encoder, public Decoder { + public: + Values(); + Values(const Values&); + ~Values(); + + /** Copy data from another Values */ + Values& operator=(const Values&); + + PN_CPP_EXTERN Values& rewind(); + + private: + friend class Value; +}; + +PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const Values&); + +}} + +#endif // VALUES_H http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/include/proton/cpp/types.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/cpp/types.h b/proton-c/bindings/cpp/include/proton/cpp/types.h index 963a330..edd95b9 100644 --- a/proton-c/bindings/cpp/include/proton/cpp/types.h +++ b/proton-c/bindings/cpp/include/proton/cpp/types.h @@ -19,61 +19,68 @@ * under the License. */ +#include <proton/codec.h> +#include "proton/cpp/ImportExport.h" +#include <algorithm> +#include <bitset> #include <string> #include <stdint.h> -#include <proton/codec.h> - -namespace proton { -namespace reactor { +#include <memory.h> /**@file - * - * C++ types representing simple AMQP types. - * + * C++ types representing AMQP types. + * @ingroup cpp */ +namespace proton { +namespace reactor { -/** Convert pn_bytes_t to string */ -std::string str(const pn_bytes_t&); - -/** Convert string to pn_bytes_t */ -pn_bytes_t bytes(const std::string&); - -/** Identifies an AMQP type */ +/** TypeId identifies an AMQP type */ enum TypeId { - NULL_=PN_NULL, - BOOL=PN_BOOL, - UBYTE=PN_UBYTE, - BYTE=PN_BYTE, - USHORT=PN_USHORT, - SHORT=PN_SHORT, - UINT=PN_UINT, - INT=PN_INT, - CHAR=PN_CHAR, - ULONG=PN_ULONG, - LONG=PN_LONG, - TIMESTAMP=PN_TIMESTAMP, - FLOAT=PN_FLOAT, - DOUBLE=PN_DOUBLE, - DECIMAL32=PN_DECIMAL32, - DECIMAL64=PN_DECIMAL64, - DECIMAL128=PN_DECIMAL128, - UUID=PN_UUID, - BINARY=PN_BINARY, - STRING=PN_STRING, - SYMBOL=PN_SYMBOL, - DESCRIBED=PN_DESCRIBED, - ARRAY=PN_ARRAY, - LIST=PN_LIST, - MAP=PN_MAP + NULL_=PN_NULL, ///< The null type, contains no data. + BOOL=PN_BOOL, ///< Boolean true or false. + UBYTE=PN_UBYTE, ///< Unsigned 8 bit integer. + BYTE=PN_BYTE, ///< Signed 8 bit integer. + USHORT=PN_USHORT, ///< Unsigned 16 bit integer. + SHORT=PN_SHORT, ///< Signed 16 bit integer. + UINT=PN_UINT, ///< Unsigned 32 bit integer. + INT=PN_INT, ///< Signed 32 bit integer. + CHAR=PN_CHAR, ///< 32 bit unicode character. + ULONG=PN_ULONG, ///< Unsigned 64 bit integer. + LONG=PN_LONG, ///< Signed 64 bit integer. + TIMESTAMP=PN_TIMESTAMP, ///< Signed 64 bit milliseconds since the epoch. + FLOAT=PN_FLOAT, ///< 32 bit binary floating point. + DOUBLE=PN_DOUBLE, ///< 64 bit binary floating point. + DECIMAL32=PN_DECIMAL32, ///< 32 bit decimal floating point. + DECIMAL64=PN_DECIMAL64, ///< 64 bit decimal floating point. + DECIMAL128=PN_DECIMAL128, ///< 128 bit decimal floating point. + UUID=PN_UUID, ///< 16 byte UUID. + BINARY=PN_BINARY, ///< Variable length sequence of bytes. + STRING=PN_STRING, ///< Variable length utf8-encoded string. + SYMBOL=PN_SYMBOL, ///< Variable length encoded string. + DESCRIBED=PN_DESCRIBED, ///< A descriptor and a value. + ARRAY=PN_ARRAY, ///< A sequence of values of the same type. + LIST=PN_LIST, ///< A sequence of values, may be of mixed types. + MAP=PN_MAP ///< A sequence of key:value pairs, may be of mixed types. }; -/** @defgroup types C++ type definitions for AMQP types. - * +///@internal +template <class T> struct Comparable {}; +template<class T> bool operator<(const Comparable<T>& a, const Comparable<T>& b) { + return static_cast<const T&>(a) < static_cast<const T&>(b); // operator < provided by type T +} +template<class T> bool operator>(const Comparable<T>& a, const Comparable<T>& b) { return b < a; } +template<class T> bool operator<=(const Comparable<T>& a, const Comparable<T>& b) { return !(a > b); } +template<class T> bool operator>=(const Comparable<T>& a, const Comparable<T>& b) { return !(a < b); } +template<class T> bool operator==(const Comparable<T>& a, const Comparable<T>& b) { return a <= b && b <= a; } +template<class T> bool operator!=(const Comparable<T>& a, const Comparable<T>& b) { return !(a == b); } + +/** + * @name C++ types representing AMQP types. + * @{ + * @ingroup cpp * These types are all distinct for overloading purposes and will insert as the * corresponding AMQP type with Encoder operator<<. - * - * @{ */ struct Null {}; typedef bool Bool; @@ -90,11 +97,14 @@ typedef float Float; typedef double Double; ///@internal +pn_bytes_t pn_bytes(const std::string&); + +///@internal #define STRING_LIKE(NAME) \ - struct NAME : public std::string{ \ + PN_CPP_EXTERN struct NAME : public std::string{ \ NAME(const std::string& s=std::string()) : std::string(s) {} \ - NAME(const pn_bytes_t& b) : std::string(str(b)) {} \ - operator pn_bytes_t() const { return bytes(*this); } \ + NAME(const pn_bytes_t& b) : std::string(b.start, b.size) {} \ + operator pn_bytes_t() const { return pn_bytes(*this); } \ } /** UTF-8 encoded string */ @@ -104,58 +114,136 @@ STRING_LIKE(Symbol); /** Binary data */ STRING_LIKE(Binary); +///@internal +pn_uuid_t pn_uuid(const std::string&); + +/** UUID is represented as a string but treated as if it always has 16 bytes. */ +PN_CPP_EXTERN struct Uuid : public std::string{ + Uuid(const std::string& s=std::string()) : std::string(s) {} + Uuid(const pn_uuid_t& u) : std::string(&u.bytes[0], sizeof(pn_uuid_t::bytes)) {} + operator pn_uuid_t() const { return pn_uuid(*this); } +}; + // TODO aconway 2015-06-11: alternative representation of variable-length data // as pointer to existing buffers. -template <class T> struct Decimal { - T value; - Decimal(T v) : value(v) {} - Decimal& operator=(T v) { value = v; } - operator T() const { return value; } +// TODO aconway 2015-06-16: usable representation of decimal types. +template <class T> struct Decimal : public Comparable<Decimal<T> > { + char value[sizeof(T)]; + Decimal() { ::memset(value, 0, sizeof(T)); } + Decimal(const T& v) { ::memcpy(value, &v, sizeof(T)); } + operator T() const { return *reinterpret_cast<const T*>(value); } + bool operator<(const Decimal<T>& x) { + return std::lexicographical_compare(value, value+sizeof(T), x.value, x.value+sizeof(T)); + } }; typedef Decimal<pn_decimal32_t> Decimal32; typedef Decimal<pn_decimal64_t> Decimal64; typedef Decimal<pn_decimal128_t> Decimal128; -struct Timestamp { +PN_CPP_EXTERN struct Timestamp { pn_timestamp_t milliseconds; ///< Since the epoch 00:00:00 (UTC), 1 January 1970. - Timestamp(int64_t ms) : milliseconds(ms) {} + Timestamp(int64_t ms=0) : milliseconds(ms) {} operator pn_timestamp_t() const { return milliseconds; } + bool operator<(const Timestamp& x) { return milliseconds < x.milliseconds; } }; -typedef pn_uuid_t Uuid; - ///@} -/** Meta-function to get the type-id from a class */ template <class T> struct TypeIdOf {}; -template<> struct TypeIdOf<Null> { static const TypeId value; }; -template<> struct TypeIdOf<Bool> { static const TypeId value; }; -template<> struct TypeIdOf<Ubyte> { static const TypeId value; }; -template<> struct TypeIdOf<Byte> { static const TypeId value; }; -template<> struct TypeIdOf<Ushort> { static const TypeId value; }; -template<> struct TypeIdOf<Short> { static const TypeId value; }; -template<> struct TypeIdOf<Uint> { static const TypeId value; }; -template<> struct TypeIdOf<Int> { static const TypeId value; }; -template<> struct TypeIdOf<Char> { static const TypeId value; }; -template<> struct TypeIdOf<Ulong> { static const TypeId value; }; -template<> struct TypeIdOf<Long> { static const TypeId value; }; -template<> struct TypeIdOf<Timestamp> { static const TypeId value; }; -template<> struct TypeIdOf<Float> { static const TypeId value; }; -template<> struct TypeIdOf<Double> { static const TypeId value; }; -template<> struct TypeIdOf<Decimal32> { static const TypeId value; }; -template<> struct TypeIdOf<Decimal64> { static const TypeId value; }; -template<> struct TypeIdOf<Decimal128> { static const TypeId value; }; -template<> struct TypeIdOf<Uuid> { static const TypeId value; }; -template<> struct TypeIdOf<Binary> { static const TypeId value; }; -template<> struct TypeIdOf<String> { static const TypeId value; }; -template<> struct TypeIdOf<Symbol> { static const TypeId value; }; +template<> struct TypeIdOf<Null> { static const TypeId value=NULL_; }; +template<> struct TypeIdOf<Bool> { static const TypeId value=BOOL; }; +template<> struct TypeIdOf<Ubyte> { static const TypeId value=UBYTE; }; +template<> struct TypeIdOf<Byte> { static const TypeId value=BYTE; }; +template<> struct TypeIdOf<Ushort> { static const TypeId value=USHORT; }; +template<> struct TypeIdOf<Short> { static const TypeId value=SHORT; }; +template<> struct TypeIdOf<Uint> { static const TypeId value=UINT; }; +template<> struct TypeIdOf<Int> { static const TypeId value=INT; }; +template<> struct TypeIdOf<Char> { static const TypeId value=CHAR; }; +template<> struct TypeIdOf<Ulong> { static const TypeId value=ULONG; }; +template<> struct TypeIdOf<Long> { static const TypeId value=LONG; }; +template<> struct TypeIdOf<Timestamp> { static const TypeId value=TIMESTAMP; }; +template<> struct TypeIdOf<Float> { static const TypeId value=FLOAT; }; +template<> struct TypeIdOf<Double> { static const TypeId value=DOUBLE; }; +template<> struct TypeIdOf<Decimal32> { static const TypeId value=DECIMAL32; }; +template<> struct TypeIdOf<Decimal64> { static const TypeId value=DECIMAL64; }; +template<> struct TypeIdOf<Decimal128> { static const TypeId value=DECIMAL128; }; +template<> struct TypeIdOf<Uuid> { static const TypeId value=UUID; }; +template<> struct TypeIdOf<Binary> { static const TypeId value=BINARY; }; +template<> struct TypeIdOf<String> { static const TypeId value=STRING; }; +template<> struct TypeIdOf<Symbol> { static const TypeId value=SYMBOL; }; + +template<class T, TypeId A> struct TypePair { + typedef T CppType; + TypeId type; +}; + +template<class T, TypeId A> struct Ref : public TypePair<T, A> { + Ref(T& v) : value(v) {} + T& value; +}; + +template<class T, TypeId A> struct CRef : public TypePair<T, A> { + CRef(const T& v) : value(v) {} + CRef(const Ref<T,A>& ref) : value(ref.value) {} + const T& value; +}; + +/** Create a reference to value as AMQP type A for decoding. For example to decode an array of Int: + * + * std::vector<Int> v; + * decoder >> as<ARRAY>(v); + */ +template <TypeId A, class T> Ref<T, A> as(T& value) { return Ref<T, A>(value); } + +/** Create a const reference to value as AMQP type A for encoding. */ +template <TypeId A, class T> CRef<T, A> as(const T& value) { return CRef<T, A>(value); } + +///@} + +// TODO aconway 2015-06-16: described types. /** Return the name of a type. */ -std::string typeName(TypeId); +PN_CPP_EXTERN std::string typeName(TypeId); + +/** Print the name of a type */ +PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, TypeId); /** Return the name of a type from a class. */ -template<class T> std::string typeName() { return typeName(TypeIdOf<T>::value); } +PN_CPP_EXTERN template<class T> std::string typeName() { return typeName(TypeIdOf<T>::value); } + +/** Information needed to start extracting or inserting a container type. + * + * With a decoder you can use `Start s = decoder.start()` or `Start s; decoder > s` + * to get the Start for the current container. + * + * With an encoder use one of the member functions startArray, startList, startMap or startDescribed + * to create an appropriate Start value, e.g. `encoder << startList() << ...` + */ +PN_CPP_EXTERN struct Start { + Start(TypeId type=NULL_, TypeId element=NULL_, bool described=false, size_t size=0); + TypeId type; ///< The container type: ARRAY, LIST, MAP or DESCRIBED. + TypeId element; ///< the element type for array only. + bool isDescribed; ///< true if first value is a descriptor. + size_t size; ///< the element count excluding the descriptor (if any) + + /** Return a Start for an array */ + static Start array(TypeId element, bool described=false); + /** Return a Start for a list */ + static Start list(); + /** Return a Start for a map */ + static Start map(); + /** Return a Start for a described type */ + static Start described(); +}; + +/** Finish insterting or extracting a container value. */ +PN_CPP_EXTERN struct Finish {}; +inline Finish finish() { return Finish(); } + +/** Skip a value */ +PN_CPP_EXTERN struct Skip{}; +inline Skip skip() { return Skip(); } }} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/ConnectionImpl.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/ConnectionImpl.h b/proton-c/bindings/cpp/src/ConnectionImpl.h index 7add9a0..f16c862 100644 --- a/proton-c/bindings/cpp/src/ConnectionImpl.h +++ b/proton-c/bindings/cpp/src/ConnectionImpl.h @@ -41,7 +41,7 @@ class ConnectionImpl : public Endpoint public: PN_CPP_EXTERN ConnectionImpl(Container &c, pn_connection_t &pnConn); PN_CPP_EXTERN ConnectionImpl(Container &c, Handler *h = 0); - PN_CPP_EXTERN ~ConnectionImpl(); + PN_CPP_EXTERN virtual ~ConnectionImpl(); PN_CPP_EXTERN Transport &getTransport(); PN_CPP_EXTERN Handler *getOverride(); PN_CPP_EXTERN void setOverride(Handler *h); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/ContainerImpl.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/ContainerImpl.cpp b/proton-c/bindings/cpp/src/ContainerImpl.cpp index 61fc860..989bd00 100644 --- a/proton-c/bindings/cpp/src/ContainerImpl.cpp +++ b/proton-c/bindings/cpp/src/ContainerImpl.cpp @@ -47,10 +47,6 @@ ConnectionImpl *getImpl(const Connection &c) { return PrivateImplRef<Connection>::get(c); } -ContainerImpl *getImpl(const Container &c) { - return PrivateImplRef<Container>::get(c); -} - } // namespace @@ -253,8 +249,6 @@ Sender ContainerImpl::createSender(Connection &connection, std::string &addr, Ha pn_record_set_handler(record, wrapHandler(h)); } snd.open(); - - ConnectionImpl *connImpl = getImpl(connection); return snd; } @@ -266,8 +260,6 @@ Sender ContainerImpl::createSender(std::string &urlString) { Sender snd = session.createSender(containerId + '-' + path); pn_terminus_set_address(pn_link_target(snd.getPnLink()), path.c_str()); snd.open(); - - ConnectionImpl *connImpl = getImpl(conn); return snd; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/Data.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/Data.cpp b/proton-c/bindings/cpp/src/Data.cpp index 6cfc09b..790cecb 100644 --- a/proton-c/bindings/cpp/src/Data.cpp +++ b/proton-c/bindings/cpp/src/Data.cpp @@ -41,8 +41,6 @@ void Data::clear() { pn_data_clear(data); } bool Data::empty() const { return pn_data_size(data) == 0; } -std::ostream& operator<<(std::ostream& o, const Data& d) { - o << Object(d.data); -} +std::ostream& operator<<(std::ostream& o, const Data& d) { return o << Object(d.data); } }} // namespace proton::reactor http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/Decoder.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/Decoder.cpp b/proton-c/bindings/cpp/src/Decoder.cpp index 4170378..503db81 100644 --- a/proton-c/bindings/cpp/src/Decoder.cpp +++ b/proton-c/bindings/cpp/src/Decoder.cpp @@ -18,8 +18,10 @@ */ #include "proton/cpp/Decoder.h" +#include "proton/cpp/Value.h" #include <proton/codec.h> #include "proton_bits.h" +#include "Msg.h" namespace proton { namespace reactor { @@ -44,15 +46,28 @@ struct SaveState { ~SaveState() { if (data) pn_data_restore(data, handle); } void cancel() { data = 0; } }; + +struct Narrow { + pn_data_t* data; + Narrow(pn_data_t* d) : data(d) { pn_data_narrow(d); } + ~Narrow() { pn_data_widen(data); } +}; + +template <class T> T check(T result) { + if (result < 0) + throw Decoder::Error("decode: " + errorStr(result)); + return result; +} + +std::string str(const pn_bytes_t& b) { return std::string(b.start, b.size); } + } void Decoder::decode(const char* i, size_t size) { SaveState ss(data); const char* end = i + size; while (i < end) { - int result = pn_data_decode(data, i, end - i); - if (result < 0) throw Decoder::Error("decode: " + errorStr(result)); - i += result; + i += check(pn_data_decode(data, i, end - i)); } } @@ -89,11 +104,65 @@ template <class T, class U> void extract(pn_data_t* data, T& value, U (*get)(pn_ } +void Decoder::checkType(TypeId want) { + TypeId got = type(); + if (want != got) badType(want, got); +} + TypeId Decoder::type() const { SaveState ss(data); return preGet(data); } +Decoder& operator>>(Decoder& d, Start& s) { + SaveState ss(d.data); + s.type = preGet(d.data); + switch (s.type) { + case ARRAY: + s.size = pn_data_get_array(d.data); + s.element = TypeId(pn_data_get_array_type(d.data)); + s.isDescribed = pn_data_is_array_described(d.data); + break; + case LIST: + s.size = pn_data_get_list(d.data); + break; + case MAP: + s.size = pn_data_get_map(d.data); + break; + case DESCRIBED: + s.isDescribed = true; + s.size = 1; + break; + default: + throw Decoder::Error(MSG("decode: " << s.type << " is not a container type")); + } + pn_data_enter(d.data); + ss.cancel(); + return d; +} + +Decoder& operator>>(Decoder& d, Finish) { pn_data_exit(d.data); return d; } + +Decoder& operator>>(Decoder& d, Skip) { pn_data_next(d.data); return d; } + +Decoder& operator>>(Decoder& d, Value& v) { + if (d.data == v.values.data) throw Decoder::Error("decode: extract into self"); + pn_data_clear(v.values.data); + { + Narrow n(d.data); + check(pn_data_appendn(v.values.data, d.data, 1)); + } + if (!pn_data_next(d.data)) throw Decoder::Error("decode: no more data"); + return d; +} + + +Decoder& operator>>(Decoder& d, Null) { + SaveState ss(d.data); + badType(NULL_, preGet(d.data)); + return d; +} + Decoder& operator>>(Decoder& d, Bool& value) { extract(d.data, value, pn_data_get_bool); return d; @@ -112,7 +181,7 @@ Decoder& operator>>(Decoder& d, Ubyte& value) { Decoder& operator>>(Decoder& d, Byte& value) { SaveState ss(d.data); switch (preGet(d.data)) { - case BYTE: value = pn_data_get_ubyte(d.data); break; + case BYTE: value = pn_data_get_byte(d.data); break; default: badType(BYTE, TypeId(TypeId(pn_data_type(d.data)))); } ss.cancel(); @@ -256,10 +325,4 @@ Decoder& operator>>(Decoder& d, std::string& value) { return d; } -void Decoder::checkType(TypeId want) { - TypeId got = type(); - if (want != got) badType(want, got); -} - - }} // namespace proton::reactor http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/Encoder.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/Encoder.cpp b/proton-c/bindings/cpp/src/Encoder.cpp index 81fa365..9182400 100644 --- a/proton-c/bindings/cpp/src/Encoder.cpp +++ b/proton-c/bindings/cpp/src/Encoder.cpp @@ -18,8 +18,10 @@ */ #include "proton/cpp/Encoder.h" +#include "proton/cpp/Value.h" #include <proton/codec.h> #include "proton_bits.h" +#include "Msg.h" namespace proton { namespace reactor { @@ -33,27 +35,30 @@ struct SaveState { pn_handle_t handle; SaveState(pn_data_t* d) : data(d), handle(pn_data_point(d)) {} ~SaveState() { if (data) pn_data_restore(data, handle); } + void cancel() { data = 0; } }; -template <class T> T check(T result) { +void check(int result, pn_data_t* data) { if (result < 0) - throw Encoder::Error("encode: " + errorStr(result)); - return result; + throw Encoder::Error("encode: " + errorStr(pn_data_error(data), result)); } } bool Encoder::encode(char* buffer, size_t& size) { SaveState ss(data); // In case of error - pn_data_rewind(data); ssize_t result = pn_data_encode(data, buffer, size); if (result == PN_OVERFLOW) { - size = pn_data_encoded_size(data); - return false; + result = pn_data_encoded_size(data); + if (result >= 0) { + size = result; + return false; + } } - check(result); + check(result, data); size = result; - ss.data = 0; // Don't restore state, all is well. + ss.cancel(); // Don't restore state, all is well. pn_data_clear(data); + return true; } void Encoder::encode(std::string& s) { @@ -70,16 +75,35 @@ std::string Encoder::encode() { return s; } +Encoder& operator<<(Encoder& e, const Start& s) { + switch (s.type) { + case ARRAY: pn_data_put_array(e.data, s.isDescribed, pn_type_t(s.element)); break; + case MAP: pn_data_put_map(e.data); break; + case LIST: pn_data_put_list(e.data); break; + case DESCRIBED: pn_data_put_described(e.data); break; + default: + throw Encoder::Error(MSG("encode: " << s.type << " is not a container type")); + } + pn_data_enter(e.data); + return e; +} + +Encoder& operator<<(Encoder& e, Finish) { + pn_data_exit(e.data); + return e; +} + namespace { template <class T, class U> Encoder& insert(Encoder& e, pn_data_t* data, T& value, int (*put)(pn_data_t*, U)) { SaveState ss(data); // Save state in case of error. - check(put(data, value)); - ss.data = 0; // Don't restore state, all is good. + check(put(data, value), data); + ss.cancel(); // Don't restore state, all is good. return e; } } +Encoder& operator<<(Encoder& e, Null) { pn_data_put_null(e.data); return e; } Encoder& operator<<(Encoder& e, Bool value) { return insert(e, e.data, value, pn_data_put_bool); } Encoder& operator<<(Encoder& e, Ubyte value) { return insert(e, e.data, value, pn_data_put_ubyte); } Encoder& operator<<(Encoder& e, Byte value) { return insert(e, e.data, value, pn_data_put_byte); } @@ -101,4 +125,34 @@ Encoder& operator<<(Encoder& e, String value) { return insert(e, e.data, value, Encoder& operator<<(Encoder& e, Symbol value) { return insert(e, e.data, value, pn_data_put_symbol); } Encoder& operator<<(Encoder& e, Binary value) { return insert(e, e.data, value, pn_data_put_binary); } +// Meta-function to get the class from the type ID. +template <TypeId A> struct ClassOf {}; +template<> struct ClassOf<NULL_> { typedef Null ValueType; }; +template<> struct ClassOf<BOOL> { typedef Bool ValueType; }; +template<> struct ClassOf<UBYTE> { typedef Ubyte ValueType; }; +template<> struct ClassOf<BYTE> { typedef Byte ValueType; }; +template<> struct ClassOf<USHORT> { typedef Ushort ValueType; }; +template<> struct ClassOf<SHORT> { typedef Short ValueType; }; +template<> struct ClassOf<UINT> { typedef Uint ValueType; }; +template<> struct ClassOf<INT> { typedef Int ValueType; }; +template<> struct ClassOf<CHAR> { typedef Char ValueType; }; +template<> struct ClassOf<ULONG> { typedef Ulong ValueType; }; +template<> struct ClassOf<LONG> { typedef Long ValueType; }; +template<> struct ClassOf<TIMESTAMP> { typedef Timestamp ValueType; }; +template<> struct ClassOf<FLOAT> { typedef Float ValueType; }; +template<> struct ClassOf<DOUBLE> { typedef Double ValueType; }; +template<> struct ClassOf<DECIMAL32> { typedef Decimal32 ValueType; }; +template<> struct ClassOf<DECIMAL64> { typedef Decimal64 ValueType; }; +template<> struct ClassOf<DECIMAL128> { typedef Decimal128 ValueType; }; +template<> struct ClassOf<UUID> { typedef Uuid ValueType; }; +template<> struct ClassOf<BINARY> { typedef Binary ValueType; }; +template<> struct ClassOf<STRING> { typedef String ValueType; }; +template<> struct ClassOf<SYMBOL> { typedef Symbol ValueType; }; + +Encoder& operator<<(Encoder& e, const Value& v) { + if (e.data == v.values.data) throw Encoder::Error("encode: cannot insert into self"); + check(pn_data_appendn(e.data, v.values.data, 1), e.data); + return e; +} + }} // namespace proton::reactor http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/Handler.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/Handler.cpp b/proton-c/bindings/cpp/src/Handler.cpp index 4d1b581..5c37c8d 100644 --- a/proton-c/bindings/cpp/src/Handler.cpp +++ b/proton-c/bindings/cpp/src/Handler.cpp @@ -24,10 +24,10 @@ namespace proton { namespace reactor { -Handler::Handler(){}; -Handler::~Handler(){}; +Handler::Handler() {} +Handler::~Handler() {} -void Handler::onUnhandled(Event &e){}; +void Handler::onUnhandled(Event &e) {} void Handler::addChildHandler(Handler &e) { childHandlers.push_back(&e); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/MessagingAdapter.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/MessagingAdapter.cpp b/proton-c/bindings/cpp/src/MessagingAdapter.cpp index 625485e..f137397 100644 --- a/proton-c/bindings/cpp/src/MessagingAdapter.cpp +++ b/proton-c/bindings/cpp/src/MessagingAdapter.cpp @@ -35,10 +35,10 @@ namespace reactor { MessagingAdapter::MessagingAdapter(MessagingHandler &delegate_) : MessagingHandler(true, delegate_.prefetch, delegate_.autoSettle, delegate_.autoAccept, delegate_.peerCloseIsError), delegate(delegate_) -{}; +{} -MessagingAdapter::~MessagingAdapter(){}; +MessagingAdapter::~MessagingAdapter(){} void MessagingAdapter::onReactorInit(Event &e) { @@ -126,7 +126,7 @@ void MessagingAdapter::onDelivery(Event &e) { MessagingEvent mevent(PN_MESSAGING_ACCEPTED, *pe); delegate.onAccepted(mevent); } - else if (rstate = PN_REJECTED) { + else if (rstate == PN_REJECTED) { MessagingEvent mevent(PN_MESSAGING_REJECTED, *pe); delegate.onRejected(mevent); } @@ -164,10 +164,6 @@ bool isRemoteOpen(pn_state_t state) { return state & PN_REMOTE_ACTIVE; } -bool isRemoteClosed(pn_state_t state) { - return state & PN_REMOTE_CLOSED; -} - } // namespace void MessagingAdapter::onLinkRemoteClose(Event &e) { http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/MessagingHandler.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/MessagingHandler.cpp b/proton-c/bindings/cpp/src/MessagingHandler.cpp index 925186a..6e3d2bd 100644 --- a/proton-c/bindings/cpp/src/MessagingHandler.cpp +++ b/proton-c/bindings/cpp/src/MessagingHandler.cpp @@ -83,7 +83,7 @@ void MessagingHandler::createHelpers() { MessagingHandler::~MessagingHandler(){ delete flowController; delete messagingAdapter; -}; +} void MessagingHandler::onAbort(Event &e) { onUnhandled(e); } void MessagingHandler::onAccepted(Event &e) { onUnhandled(e); } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/Msg.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/Msg.h b/proton-c/bindings/cpp/src/Msg.h index cd8f9e8..2b4c6da 100644 --- a/proton-c/bindings/cpp/src/Msg.h +++ b/proton-c/bindings/cpp/src/Msg.h @@ -44,30 +44,10 @@ struct Msg { Msg(const Msg& m) : os(m.str()) {} std::string str() const { return os.str(); } operator std::string() const { return str(); } - - Msg& operator<<(long n) { os << n; return *this; } - Msg& operator<<(unsigned long n) { os << n; return *this; } - Msg& operator<<(bool n) { os << n; return *this; } - Msg& operator<<(short n) { os << n; return *this; } - Msg& operator<<(unsigned short n) { os << n; return *this; } - Msg& operator<<(int n) { os << n; return *this; } - Msg& operator<<(unsigned int n) { os << n; return *this; } -#ifdef _GLIBCXX_USE_LONG_LONG - Msg& operator<<(long long n) { os << n; return *this; } - Msg& operator<<(unsigned long long n) { os << n; return *this; } -#endif - Msg& operator<<(double n) { os << n; return *this; } - Msg& operator<<(float n) { os << n; return *this; } - Msg& operator<<(long double n) { os << n; return *this; } - template <class T> Msg& operator<<(const T& t) { os <<t; return *this; } }; - - -inline std::ostream& operator<<(std::ostream& o, const Msg& m) { - return o << m.str(); -} +inline std::ostream& operator<<(std::ostream& o, const Msg& m) { return o << m.str(); } /** Construct a message using operator << and append (file:line) */ #define QUOTE_(x) #x http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/ProtonEvent.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/ProtonEvent.cpp b/proton-c/bindings/cpp/src/ProtonEvent.cpp index 16b92b6..0b0f1d0 100644 --- a/proton-c/bindings/cpp/src/ProtonEvent.cpp +++ b/proton-c/bindings/cpp/src/ProtonEvent.cpp @@ -70,11 +70,12 @@ Receiver ProtonEvent::getReceiver() { Link ProtonEvent::getLink() { pn_link_t *lnk = pn_event_link(getPnEvent()); - if (lnk) + if (lnk) { if (pn_link_is_sender(lnk)) return Sender(lnk); else return Receiver(lnk); + } throw ProtonException(MSG("No link context for this event")); } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/ProtonHandler.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/ProtonHandler.cpp b/proton-c/bindings/cpp/src/ProtonHandler.cpp index 83d9087..f96f5a6 100644 --- a/proton-c/bindings/cpp/src/ProtonHandler.cpp +++ b/proton-c/bindings/cpp/src/ProtonHandler.cpp @@ -24,7 +24,7 @@ namespace proton { namespace reactor { -ProtonHandler::ProtonHandler(){}; +ProtonHandler::ProtonHandler(){} // Everything goes to onUnhandled() unless overriden by subclass http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/Transport.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/Transport.cpp b/proton-c/bindings/cpp/src/Transport.cpp index 7f22b84..91eb8d9 100644 --- a/proton-c/bindings/cpp/src/Transport.cpp +++ b/proton-c/bindings/cpp/src/Transport.cpp @@ -27,7 +27,7 @@ namespace proton { namespace reactor { -Transport::Transport() : connection(0), pnTransport(pn_transport()) {} ; +Transport::Transport() : connection(0), pnTransport(pn_transport()) {} Transport::~Transport() { pn_decref(pnTransport); } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/Value.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/Value.cpp b/proton-c/bindings/cpp/src/Value.cpp index 8e1e38c..b488481 100644 --- a/proton-c/bindings/cpp/src/Value.cpp +++ b/proton-c/bindings/cpp/src/Value.cpp @@ -21,21 +21,15 @@ #include "proton_bits.h" #include <proton/codec.h> #include <ostream> +#include <algorithm> namespace proton { namespace reactor { -Values::Values() {} -Values::Values(const Values& v) { *this = v; } -Values::~Values() {} -Values& Values::operator=(const Values& v) { Data::operator=(v); } - -void Values::rewind() { pn_data_rewind(data); } - -Value::Value() {} +Value::Value() { *this = Null(); } Value::Value(const Value& v) { *this = v; } Value::~Value() {} -Value& Value::operator=(const Value& v) { values = v.values; } +Value& Value::operator=(const Value& v) { values = v.values; return *this; } TypeId Value::type() const { const_cast<Values&>(values).rewind(); @@ -50,22 +44,93 @@ template <class T> T check(T result) { } } -Encoder& operator<<(Encoder& e, const Value& v) { - if (e.data == v.values.data) throw Encoder::Error("Values inserted into self"); - pn_data_narrow(e.data); - int result = pn_data_appendn(e.data, v.values.data, 1); - pn_data_widen(e.data); - check(result); - return e; +std::ostream& operator<<(std::ostream& o, const Value& v) { + return o << v.values; +} + +namespace { + +// Compare nodes, return -1 if a<b, 0 if a==b, +1 if a>b +// Forward-declare so we can use it recursively. +int compareNext(Values& a, Values& b); + +template <class T> int compare(const T& a, const T& b) { + if (a < b) return -1; + else if (a > b) return +1; + else return 0; +} + +int compareContainer(Values& a, Values& b) { + Decoder::Scope sa(a), sb(b); + // Compare described vs. not-described. + int cmp = compare(sa.isDescribed, sb.isDescribed); + if (cmp) return cmp; + // Lexical sort (including descriptor if there is one) + size_t minSize = std::min(sa.size, sb.size) + int(sa.isDescribed); + for (size_t i = 0; i < minSize; ++i) { + cmp = compareNext(a, b); + if (cmp) return cmp; + } + return compare(sa.size, sb.size); +} + +template <class T> int compareSimple(Values& a, Values& b) { + T va, vb; + a >> va; + b >> vb; + return compare(va, vb); +} + +int compareNext(Values& a, Values& b) { + // Sort by TypeId first. + TypeId ta = a.type(), tb = b.type(); + int cmp = compare(ta, tb); + if (cmp) return cmp; + + switch (ta) { + case NULL_: return 0; + case ARRAY: + case LIST: + case MAP: + case DESCRIBED: + return compareContainer(a, b); + case BOOL: return compareSimple<Bool>(a, b); + case UBYTE: return compareSimple<Ubyte>(a, b); + case BYTE: return compareSimple<Byte>(a, b); + case USHORT: return compareSimple<Ushort>(a, b); + case SHORT: return compareSimple<Short>(a, b); + case UINT: return compareSimple<Uint>(a, b); + case INT: return compareSimple<Int>(a, b); + case CHAR: return compareSimple<Char>(a, b); + case ULONG: return compareSimple<Ulong>(a, b); + case LONG: return compareSimple<Long>(a, b); + case TIMESTAMP: return compareSimple<Timestamp>(a, b); + case FLOAT: return compareSimple<Float>(a, b); + case DOUBLE: return compareSimple<Double>(a, b); + case DECIMAL32: return compareSimple<Decimal32>(a, b); + case DECIMAL64: return compareSimple<Decimal64>(a, b); + case DECIMAL128: return compareSimple<Decimal128>(a, b); + case UUID: return compareSimple<Uuid>(a, b); + case BINARY: return compareSimple<Binary>(a, b); + case STRING: return compareSimple<String>(a, b); + case SYMBOL: return compareSimple<Symbol>(a, b); + } + // Invalid but equal TypeId, treat as equal. + return 0; +} + +} // namespace + +bool Value::operator==(const Value& v) const { + values.rewind(); + v.values.rewind(); + return compareNext(values, v.values) == 0; } -Decoder& operator>>(Decoder& e, Value& v) { - if (e.data == v.values.data) throw Decoder::Error("Values extracted from self"); - pn_data_narrow(e.data); - int result = pn_data_appendn(e.data, v.values.data, 1); - pn_data_widen(e.data); - check(result); - return e; +bool Value::operator<(const Value& v) const { + values.rewind(); + v.values.rewind(); + return compareNext(values, v.values) < 0; } }} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/Values.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/Values.cpp b/proton-c/bindings/cpp/src/Values.cpp new file mode 100644 index 0000000..20fb9f1 --- /dev/null +++ b/proton-c/bindings/cpp/src/Values.cpp @@ -0,0 +1,39 @@ +/* + * 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 "proton/cpp/Value.h" +#include "proton_bits.h" +#include <proton/codec.h> +#include <ostream> + +namespace proton { +namespace reactor { + +Values::Values() {} +Values::Values(const Values& v) { *this = v; } +Values::~Values() {} +Values& Values::operator=(const Values& v) { Data::operator=(v); return *this; } + +Values& Values::rewind() { pn_data_rewind(data); return *this; } + +std::ostream& operator<<(std::ostream& o, const Values& v) { + return o << static_cast<const Encoder&>(v); +} + +}} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/interop_test.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/interop_test.cpp b/proton-c/bindings/cpp/src/interop_test.cpp index b344553..d7bef51 100644 --- a/proton-c/bindings/cpp/src/interop_test.cpp +++ b/proton-c/bindings/cpp/src/interop_test.cpp @@ -48,11 +48,7 @@ string read(string filename) { return string(istreambuf_iterator<char>(ifs), istreambuf_iterator<char>()); } -template <class T> T get(Decoder& d) { - T value; - d >> exact(value); - return value; -} +template <class T> T get(Decoder& d) { return d.getAs<T, TypeIdOf<T>::value>(); } template <class T> std::string str(const T& value) { ostringstream oss; @@ -129,7 +125,7 @@ int run_test(void (*testfn)(), const char* name) { return 1; } -// FIXME aconway 2015-06-11: not testing all types. +// TODO aconway 2015-06-11: interop test is not complete. #define RUN_TEST(T) run_test(&T, #T) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/proton_bits.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/proton_bits.cpp b/proton-c/bindings/cpp/src/proton_bits.cpp index 2e96430..10f7c59 100644 --- a/proton-c/bindings/cpp/src/proton_bits.cpp +++ b/proton-c/bindings/cpp/src/proton_bits.cpp @@ -39,6 +39,14 @@ std::string errorStr(int code) { } } +std::string errorStr(pn_error_t* err, int code) { + if (err && pn_error_code(err)) { + const char* text = pn_error_text(err); + return text ? std::string(text) : errorStr(pn_error_code(err)); + } + return errorStr(code); +} + std::ostream& operator<<(std::ostream& o, const Object& object) { pn_string_t* str = pn_string(""); pn_inspect(object.value, str); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/proton_bits.h ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/proton_bits.h b/proton-c/bindings/cpp/src/proton_bits.h index a905172..e57f188 100644 --- a/proton-c/bindings/cpp/src/proton_bits.h +++ b/proton-c/bindings/cpp/src/proton_bits.h @@ -20,13 +20,18 @@ */ #include <iosfwd> +#include <proton/error.h> /**@file * * Assorted internal proton utilities. */ + std::string errorStr(int code); +/** Print the error string from pn_error_t, or from code if pn_error_t has no error. */ +std::string errorStr(pn_error_t*, int code=0); + /** Wrapper for a proton object pointer. */ struct Object { void* value; Object(void* o) : value(o) {} }; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/cpp/src/types.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/types.cpp b/proton-c/bindings/cpp/src/types.cpp index 127ee7d..4f2c6ac 100644 --- a/proton-c/bindings/cpp/src/types.cpp +++ b/proton-c/bindings/cpp/src/types.cpp @@ -19,32 +19,11 @@ #include "proton/cpp/types.h" #include <proton/codec.h> +#include <ostream> namespace proton { namespace reactor { -const TypeId TypeIdOf<Null>::value = NULL_; -const TypeId TypeIdOf<Bool>::value = BOOL; -const TypeId TypeIdOf<Ubyte>::value = UBYTE; -const TypeId TypeIdOf<Byte>::value = BYTE; -const TypeId TypeIdOf<Ushort>::value = USHORT; -const TypeId TypeIdOf<Short>::value = SHORT; -const TypeId TypeIdOf<Uint>::value = UINT; -const TypeId TypeIdOf<Int>::value = INT; -const TypeId TypeIdOf<Char>::value = CHAR; -const TypeId TypeIdOf<Ulong>::value = ULONG; -const TypeId TypeIdOf<Long>::value = LONG; -const TypeId TypeIdOf<Timestamp>::value = TIMESTAMP; -const TypeId TypeIdOf<Float>::value = FLOAT; -const TypeId TypeIdOf<Double>::value = DOUBLE; -const TypeId TypeIdOf<Decimal32>::value = DECIMAL32; -const TypeId TypeIdOf<Decimal64>::value = DECIMAL64; -const TypeId TypeIdOf<Decimal128>::value = DECIMAL128; -const TypeId TypeIdOf<Uuid>::value = UUID; -const TypeId TypeIdOf<Binary>::value = BINARY; -const TypeId TypeIdOf<String>::value = STRING; -const TypeId TypeIdOf<Symbol>::value = SYMBOL; - std::string typeName(TypeId t) { switch (t) { case NULL_: return "null"; @@ -76,7 +55,27 @@ std::string typeName(TypeId t) { } } -std::string str(const pn_bytes_t& b) { return std::string(b.start, b.size); } -pn_bytes_t bytes(const std::string& s) { pn_bytes_t b; b.start = &s[0]; b.size = s.size(); return b; } +std::ostream& operator<<(std::ostream& o,TypeId t) { return o << typeName(t); } + +PN_CPP_EXTERN bool isContainer(TypeId t) { + return (t == LIST || t == MAP || t == ARRAY || t == DESCRIBED); +} + +pn_bytes_t pn_bytes(const std::string& s) { + pn_bytes_t b = { s.size(), const_cast<char*>(&s[0]) }; + return b; +} + +pn_uuid_t pn_uuid(const std::string& s) { + pn_uuid_t u = {0}; // Zero initialized. + std::copy(s.begin(), s.begin() + std::max(s.size(), sizeof(pn_uuid_t::bytes)), &u.bytes[0]); + return u; +} + +Start::Start(TypeId t, TypeId e, bool d, size_t s) : type(t), element(e), isDescribed(d), size(s) {} +Start Start::array(TypeId element, bool described) { return Start(ARRAY, element, described); } +Start Start::list() { return Start(LIST); } +Start Start::map() { return Start(MAP); } +Start Start::described() { return Start(DESCRIBED, NULL_, true); } }} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/693752d3/proton-c/bindings/go/README.md ---------------------------------------------------------------------- diff --git a/proton-c/bindings/go/README.md b/proton-c/bindings/go/README.md deleted file mode 100644 index 0d3a74e..0000000 --- a/proton-c/bindings/go/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# *EXPERIMENTAL* Go binding for proton - -Experimental work on the Go language binding has been moved to the `go1` branch -until it is ready for use. You can `git checkout go1` on your git clone, or -browse at https://github.com/apache/qpid-proton/blob/go1/proton-c/bindings/go/README.md --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
