NO-JIRA: C++: Conversions for complex types Intuitive automatic conversions between standard C++ containers and AMQP types. User can override for non-standard containers or non-default use of standard containers.
Examples in encode_decode.cpp. Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/01e136cc Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/01e136cc Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/01e136cc Branch: refs/heads/go1 Commit: 01e136cc660a721da9178a98ff43bac143a4c8ce Parents: ecbe90a Author: Alan Conway <[email protected]> Authored: Thu Nov 5 09:43:45 2015 -0500 Committer: Alan Conway <[email protected]> Committed: Tue Nov 10 17:32:40 2015 -0500 ---------------------------------------------------------------------- examples/cpp/direct_send.cpp | 2 +- examples/cpp/encode_decode.cpp | 159 +++++----- examples/cpp/example_test.py | 24 +- examples/cpp/simple_send.cpp | 2 +- .../bindings/cpp/include/proton/decoder.hpp | 287 +++++++++++++++---- .../bindings/cpp/include/proton/encoder.hpp | 103 +++++-- proton-c/bindings/cpp/include/proton/engine.hpp | 2 +- .../bindings/cpp/include/proton/message.hpp | 18 +- .../bindings/cpp/include/proton/message_id.hpp | 3 +- .../cpp/include/proton/request_response.hpp | 3 - .../bindings/cpp/include/proton/type_traits.hpp | 19 +- proton-c/bindings/cpp/include/proton/types.hpp | 43 +-- proton-c/bindings/cpp/include/proton/value.hpp | 32 ++- proton-c/bindings/cpp/src/decoder.cpp | 23 +- proton-c/bindings/cpp/src/encoder.cpp | 8 +- proton-c/bindings/cpp/src/interop_test.cpp | 6 +- proton-c/bindings/cpp/src/message.cpp | 2 +- proton-c/bindings/cpp/src/value.cpp | 16 +- 18 files changed, 501 insertions(+), 251 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/examples/cpp/direct_send.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/direct_send.cpp b/examples/cpp/direct_send.cpp index 62e6ab9..13ca510 100644 --- a/examples/cpp/direct_send.cpp +++ b/examples/cpp/direct_send.cpp @@ -53,7 +53,7 @@ class simple_send : public proton::messaging_handler { msg.id(sent + 1); std::map<std::string, int> m; m["sequence"] = sent+1; - msg.body(proton::as<proton::MAP>(m)); + msg.body(m); sender.send(msg); sent++; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/examples/cpp/encode_decode.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/encode_decode.cpp b/examples/cpp/encode_decode.cpp index 4f88c93..3d6fe48 100644 --- a/examples/cpp/encode_decode.cpp +++ b/examples/cpp/encode_decode.cpp @@ -24,8 +24,8 @@ #include <map> #include <sstream> #include <vector> +#include <list> -using namespace std; // Examples of how to use the encoder and decoder to create and examine AMQP values. // @@ -34,12 +34,18 @@ using namespace std; // values from a decoder in terms of their simple components. void print(proton::value&); -// Some helper templates to print map and vector results. +// Some helper templates to print map and std::vector results. namespace std { -template<class T, class U> ostream& operator<<(ostream& o, const pair<T,U>& p) { +template<class T, class U> ostream& operator<<(ostream& o, const std::pair<T,U>& p) { return o << p.first << ":" << p.second; } -template<class T> ostream& operator<<(ostream& o, const vector<T>& v) { +template<class T> ostream& operator<<(ostream& o, const std::vector<T>& v) { + o << "[ "; + ostream_iterator<T> oi(o, " "); + copy(v.begin(), v.end(), oi); + return o << "]"; +} +template<class T> ostream& operator<<(ostream& o, const std::list<T>& v) { o << "[ "; ostream_iterator<T> oi(o, " "); copy(v.begin(), v.end(), oi); @@ -47,85 +53,109 @@ template<class T> ostream& operator<<(ostream& o, const vector<T>& v) { } template<class K, class T> ostream& operator<<(ostream& o, const map<K, T>& m) { o << "{ "; - ostream_iterator<pair<K,T> > oi(o, " "); + ostream_iterator<std::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; +// Insert/extract native C++ containers with uniform type values. +void uniform_containers() { + std::cout << std::endl << "== Array, list and map of uniform type." << std::endl; + proton::value v; - vector<int> a; + std::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; + // By default a C++ container is encoded as an AMQP array. + v = a; + print(v); + std::list<int> a1 = v; // Decode as a C++ std::list instead + std::cout << a1 << std::endl; - proton::value v; - v.encoder() << proton::as<proton::ARRAY>(a) << proton::as<proton::LIST>(l) << proton::as<proton::MAP>(m); + // You can specify that a container should be encoded as an AMQP list instead. + v = proton::as<proton::LIST>(a1); print(v); + std::cout << v.get<std::vector<int> >() << std::endl; - vector<int> a1, l1; - map<string, int> m1; - v.decoder().rewind(); - v.decoder() >> proton::as<proton::ARRAY>(a1) >> proton::as<proton::LIST>(l1) >> proton::as<proton::MAP>(m1); - cout << "Extracted: " << a1 << ", " << l1 << ", " << m1 << endl; + // C++ map types (types with key_type, mapped_type) convert to an AMQP map by default. + std::map<std::string, int> m; + m["one"] = 1; + m["two"] = 2; + v = m; + print(v); + std::cout << v.get<std::map<std::string, int> >() << std::endl; + + // You can convert a sequence of pairs to an AMQP map if you need to control the + // encoded ordering. + std::vector<std::pair<std::string, int> > pairs; + pairs.push_back(std::make_pair("z", 3)); + pairs.push_back(std::make_pair("a", 4)); + v = proton::as<proton::MAP>(pairs); + print(v); + // You can also decode an AMQP map as a sequence of pairs using decoder() and proton::to_pairs + std::vector<std::pair<std::string, int> > pairs2; + v.decoder() >> proton::to_pairs(pairs2); + std::cout << pairs2 << std::endl; } -// Containers with mixed types, use value to represent arbitrary AMQP types. +// 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<proton::value> l; + std::cout << std::endl << "== List and map of mixed type values." << std::endl; + proton::value v; + + std::vector<proton::value> l; l.push_back(proton::value(42)); l.push_back(proton::value(proton::amqp_string("foo"))); - map<proton::value, proton::value> m; + // By default, a sequence of proton::value is treated as an AMQP list. + v = l; + print(v); + std::vector<proton::value> l2 = v; + std::cout << l2 << std::endl; + + std::map<proton::value, proton::value> m; m[proton::value("five")] = proton::value(5); m[proton::value(4)] = proton::value("four"); - proton::value v; - v.encoder() << proton::as<proton::LIST>(l) << proton::as<proton::MAP>(m); + v = m; print(v); - - vector<proton::value> l1; - map<proton::value, proton::value> m1; - v.decoder().rewind(); - v.decoder() >> proton::as<proton::LIST>(l1) >> proton::as<proton::MAP>(m1); - cout << "Extracted: " << l1 << ", " << m1 << endl; + std::map<proton::value, proton::value> m2 = v; + std::cout << m2 << std::endl; } // Insert using stream operators (see print_next for example of extracting with stream ops.) -void insert_extract_stream_operators() { - cout << endl << "== Insert with stream operators." << endl; +void insert_stream_operators() { + std::cout << std::endl << "== Insert with stream operators." << std::endl; proton::value v; - // 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 v.encode() is called. - v.encoder() << proton::start::array(proton::INT) << proton::amqp_int(1) << proton::amqp_int(2) << proton::amqp_int(3) << proton::finish(); + + // Create an array of INT with values [1, 2, 3] + v.encoder() << proton::start::array(proton::INT) + << proton::amqp_int(1) << proton::amqp_int(2) << proton::amqp_int(3) + << proton::finish(); print(v); - v.clear(); - v.encoder() << proton::start::list() << proton::amqp_int(42) << false << proton::amqp_symbol("x") << proton::finish(); + // Create a mixed-type list of the values [42, false, "x"]. + v.encoder() << proton::start::list() + << proton::amqp_int(42) << false << proton::amqp_symbol("x") + << proton::finish(); print(v); - v.clear(); - v.encoder() << proton::start::map() << "k1" << proton::amqp_int(42) << proton::amqp_symbol("k2") << false << proton::finish(); + // Create a map { "k1":42, "k2": false } + v.encoder() << proton::start::map() + << "k1" << proton::amqp_int(42) + << proton::amqp_symbol("k2") << false + << proton::finish(); print(v); } int main(int, char**) { try { - insert_extract_containers(); + uniform_containers(); mixed_containers(); - insert_extract_stream_operators(); + insert_stream_operators(); return 0; - } catch (const exception& e) { - cerr << endl << "error: " << e.what() << endl; + } catch (const std::exception& e) { + std::cerr << std::endl << "error: " << e.what() << std::endl; } return 1; } @@ -141,47 +171,47 @@ void print_next(proton::decoder& d) { switch (type) { case proton::ARRAY: { d >> s; - cout << "array<" << s.element; + std::cout << "array<" << s.element; if (s.is_described) { - cout << ", descriptor="; + std::cout << ", descriptor="; print_next(d); } - cout << ">["; + std::cout << ">["; for (size_t i = 0; i < s.size; ++i) { - if (i) cout << ", "; + if (i) std::cout << ", "; print_next(d); } - cout << "]"; + std::cout << "]"; d >> proton::finish(); break; } case proton::LIST: { d >> s; - cout << "list["; + std::cout << "list["; for (size_t i = 0; i < s.size; ++i) { - if (i) cout << ", "; + if (i) std::cout << ", "; print_next(d); } - cout << "]"; + std::cout << "]"; d >> proton::finish(); break; } case proton::MAP: { d >> s; - cout << "map{"; + std::cout << "map{"; for (size_t i = 0; i < s.size/2; ++i) { - if (i) cout << ", "; + if (i) std::cout << ", "; print_next(d); - cout << ":"; // key:value + std::cout << ":"; // key:value print_next(d); } - cout << "}"; + std::cout << "}"; d >> proton::finish(); break; } case proton::DESCRIBED: { d >> s; - cout << "described("; + std::cout << "described("; print_next(d); // Descriptor print_next(d); // value d >> proton::finish(); @@ -192,7 +222,7 @@ void print_next(proton::decoder& d) { // we will take a short cut and extract to another value and print that. proton::value v2; d >> v2; - cout << type << "(" << v2 << ")"; + std::cout << type << "(" << v2 << ")"; } } @@ -200,10 +230,9 @@ void print_next(proton::decoder& d) { void print(proton::value& v) { proton::decoder& d = v.decoder(); d.rewind(); - cout << "Values: "; while (d.more()) { print_next(d); - if (d.more()) cout << ", "; + if (d.more()) std::cout << ", "; } - cout << endl; + std::cout << std::endl; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/examples/cpp/example_test.py ---------------------------------------------------------------------- diff --git a/examples/cpp/example_test.py b/examples/cpp/example_test.py index f51cf57..bc552ab 100644 --- a/examples/cpp/example_test.py +++ b/examples/cpp/example_test.py @@ -199,18 +199,26 @@ class ExampleTest(unittest.TestCase): def test_encode_decode(self): expect=""" -== 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 } +== Array, list and map of uniform type. +array<int>[int(1), int(2), int(3)] +[ 1 2 3 ] +list[int(1), int(2), int(3)] +[ 1 2 3 ] +map{string(one):int(1), string(two):int(2)} +{ one:1 two:2 } +map{string(z):int(3), string(a):int(4)} +[ z:3 a:4 ] == 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 } +list[int(42), string(foo)] +[ 42 foo ] +map{int(4):string(four), string(five):int(5)} +{ 4:four five:5 } == Insert with stream operators. -Values: array<int>[int(1), int(2), int(3)] -Values: list[int(42), boolean(false), symbol(x)] -Values: map{string(k1):int(42), symbol(k2):boolean(false)} +array<int>[int(1), int(2), int(3)] +list[int(42), boolean(false), symbol(x)] +map{string(k1):int(42), symbol(k2):boolean(false)} """ self.maxDiff = None self.assertEqual(expect, execute("encode_decode")) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/examples/cpp/simple_send.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/simple_send.cpp b/examples/cpp/simple_send.cpp index cb33207..31e2c73 100644 --- a/examples/cpp/simple_send.cpp +++ b/examples/cpp/simple_send.cpp @@ -51,7 +51,7 @@ class simple_send : public proton::messaging_handler { msg.id(sent + 1); std::map<std::string, int> m; m["sequence"] = sent+1; - msg.body(proton::as<proton::MAP>(m)); + msg.body(m); sender.send(msg); sent++; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/decoder.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/decoder.hpp b/proton-c/bindings/cpp/include/proton/decoder.hpp index 33ddb4f..1d105e9 100644 --- a/proton-c/bindings/cpp/include/proton/decoder.hpp +++ b/proton-c/bindings/cpp/include/proton/decoder.hpp @@ -23,51 +23,130 @@ #include "proton/type_traits.hpp" #include "proton/types.hpp" #include "proton/facade.hpp" + #include <iosfwd> +#ifndef PN_NO_CONTAINER_CONVERT + +#include <vector> +#include <deque> +#include <list> +#include <map> + +#if PN_HAS_CPP11 +#include <array> +#include <forward_list> +#include <unordered_map> +#endif // PN_HAS_CPP11 + +#endif // PN_NO_CONTAINER_CONVERT + struct pn_data_t; namespace proton { -class data; class message_id; /** Raised by decoder operations on error.*/ struct decode_error : public error { PN_CPP_EXTERN explicit decode_error(const std::string&) throw(); }; -/** Skips a value with `decoder >> skip()`. */ +/** Skips a value with `dec >> skip()`. */ struct skip{}; -/** Rewind the decoder with `decoder >> rewind()`. */ +/** Assert the next type of value in the decoder: `dec >> assert_type(t)` + * throws if decoder.type() != t + */ +struct assert_type { + type_id type; + assert_type(type_id t) : type(t) {} +}; + +/** Rewind the decoder with `dec >> rewind()`. */ struct rewind{}; /** - * Stream-like decoder from AMQP bytes to a stream of C++ values. - * - * 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. - * - * amqp_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<amqp_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 preserve order when - * extracting AMQP maps. - * - * You can also extract container values element-by-element, see decoder::operator>>(decoder&, start&) - * +Stream-like decoder from AMQP bytes to C++ values. + +@see types.hpp defines C++ types corresponding to AMQP types. + +The decoder operator>> will extract AMQP types into any compatible C++ +type or throw an exception if the types are not compatible. + ++-------------------------+-------------------------------+ +|AMQP type |Compatible C++ types | ++=========================+===============================+ +|BOOLEAN |amqp_boolean, bool | ++-------------------------+-------------------------------+ +|signed integer type I |C++ signed integer type T where| +| |sizeof(T) >= sizeof(I) | ++-------------------------+-------------------------------+ +|unsigned integer type U |C++ unsigned integer type T | +| |where sizeof(T) >= sizeof(U) | ++-------------------------+-------------------------------+ +|CHAR |amqp_char, wchar_t | ++-------------------------+-------------------------------+ +|FLOAT |amqp_float, float | ++-------------------------+-------------------------------+ +|DOUBLE |amqp_double, double | ++-------------------------+-------------------------------+ +|STRING |amqp_string, std::string | ++-------------------------+-------------------------------+ +|SYMBOL |amqp_symbol, std::string | ++-------------------------+-------------------------------+ +|BINARY |amqp_binary, std::string | ++-------------------------+-------------------------------+ +|DECIMAL<n> |amqp_decimal<n> | ++-------------------------+-------------------------------+ +|TIMESTAMP |amqp_timestamp | ++-------------------------+-------------------------------+ +|UUID |amqp_uuid | ++-------------------------+-------------------------------+ + +The special proton::value type can hold any AMQP type, simple or compound. + +By default operator >> will do any conversion that does not lose data. For example +any AMQP signed integer type can be extracted as follows: + + int64_t i; + dec >> i; + +You can assert the exact AMQP type with proton::assert_type, for example +the following will throw if the AMQP type is not an AMQP INT (32 bits) + + amqp_int i; + dec >> assert_type(INT) >> i; // Will throw if decoder does not contain an INT + +You can extract AMQP ARRAY, LIST or MAP into standard C++ containers of compatible types, for example: + + std::vector<int32_t> v; + dec >> v; + +This will work if the decoder contains an AMQP ARRAY or LIST of SHORT or INT values. It won't work +for LONG or other types. It will also work for a MAP with keys and values that are SHORT OR INT, +the map will be "flattened" into a sequence [ key1, value1, key2, value2 ] This will work with +std::dequeue, std::array, std::list or std::forward_list. + +You can extract a MAP into a std::map or std::unordered_map + + std::map<std::string, std::string> v; + dec >> v; + +This will work for any AMQP map with keys and values that are STRING, SYMBOL or BINARY. + +If you have non-standard container types that meet the most basic requirements for +the container or associative-container concepts, you can use them via helper functions: + + my_sequence_type<int64_t> s; + dec >> proton::to_sequence(s); // Decode sequence of integers + my_map_type<amqp_string, bool> s; + dec >> proton::to_map(s); // Decode map of string: bool. + +Finally you can extract an AMQP LIST with mixed type elements into a container of proton::value, e.g. + + std::vector<proton::value> v; + dec >> v; + +You can also extract container values element-by-element, see decoder::operator>>(decoder&, start&) */ class decoder : public facade<pn_data_t, decoder> { public: @@ -124,14 +203,11 @@ class decoder : public facade<pn_data_t, decoder> { PN_CPP_EXTERN friend decoder& operator>>(decoder&, amqp_uuid&); PN_CPP_EXTERN friend decoder& operator>>(decoder&, std::string&); PN_CPP_EXTERN friend decoder& operator>>(decoder&, message_id&); - PN_CPP_EXTERN friend decoder& operator>>(decoder&, class data&); + PN_CPP_EXTERN friend decoder& operator>>(decoder&, value&); ///@} /** 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, type_id A> T get_as() { T value; *this >> as<A>(value); return value; } + template <class T> T extract() { T value; *this >> value; return value; } /** Call decoder::start() in constructor, decoder::finish in destructor(). * @@ -142,21 +218,15 @@ class decoder : public facade<pn_data_t, decoder> { ~scope() { decoder_ >> finish(); } }; - template <type_id A, class T> friend decoder& operator>>(decoder& d, ref<T, A> ref) { - d.check_type(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; + * dec >> 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(); + * dec >> finish(); * * The first value of an ARRAY is a descriptor if start::descriptor is true, * followed by start.size elements of type start::element. @@ -182,6 +252,9 @@ class decoder : public facade<pn_data_t, decoder> { /** Skip a value */ PN_CPP_EXTERN friend decoder& operator>>(decoder&, skip); + /** Throw an exception if decoder.type() != assert_type.type */ + PN_CPP_EXTERN friend decoder& operator>>(decoder&, assert_type); + /** Rewind to the beginning */ PN_CPP_EXTERN friend decoder& operator>>(decoder&, struct rewind); @@ -191,46 +264,138 @@ class decoder : public facade<pn_data_t, decoder> { friend class encoder; }; + // operator >> for integer types that are not covered by the standard overrides. template <class T> -typename enable_if<is_unknown_integer<T>::value, decoder&>::type operator>>(decoder& d, T& i) { +typename enable_if<is_unknown_integer<T>::value, decoder&>::type +operator>>(decoder& d, T& i) { typename integer_type<sizeof(T), is_signed<T>::value>::type v; d >> v; // Extract as a known integer type i = v; return d; } -template <class T> decoder& operator>>(decoder& d, ref<T, ARRAY> ref) { +///@cond INTERNAL +template <class T> struct sequence_ref { + sequence_ref(T& v) : value(v) {} + T& value; +}; + +template <class T> struct map_ref { + map_ref(T& v) : value(v) {} + T& value; +}; + +template <class T> struct pairs_ref { + pairs_ref(T& v) : value(v) {} + T& value; +}; +///@endcond + +/** + * Return a wrapper for a C++ container to be decoded as a sequence. The AMQP + * ARRAY, LIST, and MAP types can all be decoded as a sequence, a map will be + * decoded as alternating key and value (provided they can both be converted to + * the container's value_type) + * + * The following expressions must be valid for T t; + * T::iterator + * t.clear() + * t.resize() + * t.begin() + * t.end() + */ +template <class T> sequence_ref<T> to_sequence(T& v) { return sequence_ref<T>(v); } + +/** Return a wrapper for a C++ map container to be decoded from an AMQP MAP. + * The following expressions must be valid for T t; + * T::key_type + * T::mapped_type + * t.clear() + * T::key_type k; T::mapped_type v; t[k] = v; + */ +template <class T> map_ref<T> to_map(T& v) { return map_ref<T>(v); } + +/** Return a wrapper for a C++ container of std::pair that can be decoded from AMQP maps, + * preserving the encoded map order. + * + * The following expressions must be valid for T t; + * T::iterator + * t.clear() + * t.resize() + * t.begin() + * t.end() + * T::iterator i; i->first; i->second + */ +template <class T> pairs_ref<T> to_pairs(T& v) { return pairs_ref<T>(v); } + +/** Extract any AMQP sequence (ARRAY, LIST or MAP) to a C++ container of T if + * the elements types are convertible to T. A MAP is extracted as [key1, value1, + * key2, value2...] + */ +template <class T> decoder& operator>>(decoder& d, sequence_ref<T> ref) { decoder::scope s(d); if (s.is_described) d >> skip(); - ref.value.clear(); - ref.value.resize(s.size); - for (typename T::iterator i = ref.value.begin(); i != ref.value.end(); ++i) { + T& v = ref.value; + v.clear(); + v.resize(s.size); + for (typename T::iterator i = v.begin(); i != v.end(); ++i) d >> *i; - } return d; } -template <class T> decoder& operator>>(decoder& d, ref<T, LIST> ref) { +void assert_map_scope(const decoder::scope& s); + +/** Extract an AMQP MAP to a C++ map */ +template <class T> decoder& operator>>(decoder& d, map_ref<T> 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; + assert_map_scope(s); + T& m = ref.value; + m.clear(); + for (size_t i = 0; i < s.size/2; ++i) { + typename remove_const<typename T::key_type>::type k; + typename remove_const<typename T::mapped_type>::type v; + d >> k >> v; + m[k] = v; + } return d; } -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; +/** Extract an AMQP MAP to a C++ container of std::pair, preserving order. */ +template <class T> decoder& operator>>(decoder& d, pairs_ref<T> ref) { + decoder::scope s(d); + assert_map_scope(s); + T& m = ref.value; + m.clear(); + m.resize(s.size/2); + for (typename T::iterator i = m.begin(); i != m.end(); ++i) { + d >> i->first >> i->second; } return d; } +#ifndef PN_NO_CONTAINER_CONVERT + +// Decode to sequence. +template <class T, class A> decoder& operator>>(decoder &d, std::vector<T, A>& v) { return d >> to_sequence(v); } +template <class T, class A> decoder& operator>>(decoder &d, std::deque<T, A>& v) { return d >> to_sequence(v); } +template <class T, class A> decoder& operator>>(decoder &d, std::list<T, A>& v) { return d >> to_sequence(v); } + +// Decode to map. +template <class K, class T, class C, class A> decoder& operator>>(decoder &d, std::map<K, T, C, A>& v) { return d >> to_map(v); } + +#if PN_HAS_CPP11 + +// Decode to sequence. +template <class T, class A> decoder& operator>>(decoder &d, std::forward_list<T, A>& v) { return d >> to_sequence(v); } +template <class T, std::size_t N> decoder& operator>>(decoder &d, std::array<T, N>& v) { return d >> to_sequence(v); } + +// Decode to map. +template <class K, class T, class C, class A> decoder& operator>>(decoder &d, std::unordered_map<K, T, C, A>& v) { return d >> to_map(v); } + +#endif // PN_HAS_CPP11 +#endif // PN_NO_CONTAINER_CONVERT + } + #endif // DECODER_H http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/encoder.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/encoder.hpp b/proton-c/bindings/cpp/include/proton/encoder.hpp index 24684c1..7dd7ddb 100644 --- a/proton-c/bindings/cpp/include/proton/encoder.hpp +++ b/proton-c/bindings/cpp/include/proton/encoder.hpp @@ -25,39 +25,68 @@ #include "proton/facade.hpp" #include <iosfwd> +#ifndef PN_NO_CONTAINER_CONVERT + +#include <vector> +#include <deque> +#include <list> +#include <map> + +#if PN_HAS_CPP11 +#include <array> +#include <forward_list> +#include <unordered_map> +#endif // PN_HAS_CPP11 + +#endif // PN_NO_CONTAINER_CONVERT + struct pn_data_t; namespace proton { -class data; class message_id; /** Raised by encoder operations on error */ struct encode_error : public error { PN_CPP_EXTERN explicit encode_error(const std::string&) throw(); }; /** - * Stream C++ data values into an AMQP encoder using operator<<. + * Stream-like encoder from C++ values to AMQP values. * - * types.h defines C++ typedefs and types for AMQP each type. These types insert - * as the corresponding AMQP type. Conversion rules apply to other types: + * types.hpp defines a C++ type for each AMQP type. For simple types they are + * just typedefs for corresponding native C++ types. These types encode as the + * corresponding AMQP type. * - * - Integer types insert as the AMQP integer of matching size and signedness. - * - std::string or char* insert as AMQP strings. + * There are some special case conversions: * - * C++ containers can be inserted as AMQP containers with the as() helper - * functions. For example: + * - Integer types other than those mentioned in types.hpp encode as the AMQP + * integer type of matching size and signedness. + * - std::string or char* insert as AMQP STRING. * - * std::vector<amqp_symbol> v; - * encoder << as<amqp_list>(v); + * For example to encode an AMQP INT, BOOLEAN and STRING these are equivalent: * - * AMQP maps can be inserted from any container with std::pair<X,Y> as the - * value_type. That includes std::map and std::unordered_map but also for - * example std::vector<std::pair<X,Y> >. This allows you to control the order - * of elements when inserting AMQP maps. + * enc << proton::amqp_int(1) << proton::amqp_boolean(true) << proton::amqp_string("foo"); + * enc << int32_t(1) << true << "foo"; * - * You can also insert containers element-by-element, see operator<<(encoder&, const start&) + * You can force the encoding using the `proton::as` template function, for example: + * + * uint64_t i = 100; + * enc << as<proton::SHORT>(i); + * + * C++ standard containers can be inserted. By default: + * + * - std::map and std::unordered_map encode as AMQP MAP + * - std::vector, std::deque, std::list, std::array or std::forward_list encode as an AMQP ARRAY. + * - std::vector<proton::value> etc. encode as AMQP LIST + * + * Again you can force the encoding using proton::as<LIST>() or proton::as<ARRAY>() + * + * Note that you can encode a sequence of pairs as a map, which allows you to control the + * encoded order if that is important: + * + * std::vector<std::pair<T1, T2> > v; + * enc << proton::as<MAP>(v); * - *@throw decoder::error if the curent value is not a container type. + * You can also insert containers element-by-element, see operator<<(encoder&, const start&) */ class encoder : public facade<pn_data_t, encoder> { public: @@ -107,7 +136,7 @@ class encoder : public facade<pn_data_t, encoder> { friend PN_CPP_EXTERN encoder& operator<<(encoder&, amqp_symbol); friend PN_CPP_EXTERN encoder& operator<<(encoder&, amqp_binary); friend PN_CPP_EXTERN encoder& operator<<(encoder&, const message_id&); - friend PN_CPP_EXTERN encoder& operator<<(encoder&, const class data&); + friend PN_CPP_EXTERN encoder& operator<<(encoder&, const value&); ///@} /** @@ -118,7 +147,7 @@ class encoder : public facade<pn_data_t, encoder> { * and insert it into the encoder, followed by the contained elements. For * example: * - * encoder << start::list() << amqp_int(1) << amqp_symbol("two") << 3.0 << finish(); + * enc << start::list() << amqp_int(1) << amqp_symbol("two") << 3.0 << finish(); */ friend PN_CPP_EXTERN encoder& operator<<(encoder&, const start&); @@ -135,9 +164,6 @@ class encoder : public facade<pn_data_t, encoder> { template <class T> friend encoder& operator<<(encoder&, cref<T, MAP>); // TODO aconway 2015-06-16: described values. ///@} - - /** Copy data from a raw pn_data_t */ - friend PN_CPP_EXTERN encoder& operator<<(encoder&, pn_data_t*); }; // Need to disambiguate char* conversion to bool and std::string as amqp_string. @@ -179,11 +205,36 @@ template <class T> encoder& operator<<(encoder& e, cref<T, MAP> m){ e << finish(); return e; } -///@cond INTERNAL Convert a ref to a cref. -template <class T, type_id A> encoder& operator<<(encoder& e, ref<T, A> ref) { - return e << cref<T,A>(ref); -} -///@endcond +#ifndef PN_NO_CONTAINER_CONVERT +// Encode as ARRAY +template <class T, class A> encoder& operator<<(encoder &e, const std::vector<T, A>& v) { return e << as<ARRAY>(v); } +template <class T, class A> encoder& operator<<(encoder &e, const std::deque<T, A>& v) { return e << as<ARRAY>(v); } +template <class T, class A> encoder& operator<<(encoder &e, const std::list<T, A>& v) { return e << as<ARRAY>(v); } + +// Encode as LIST +template <class A> encoder& operator<<(encoder &e, const std::vector<value, A>& v) { return e << as<LIST>(v); } +template <class A> encoder& operator<<(encoder &e, const std::deque<value, A>& v) { return e << as<LIST>(v); } +template <class A> encoder& operator<<(encoder &e, const std::list<value, A>& v) { return e << as<LIST>(v); } + +// Encode as MAP +template <class K, class T, class C, class A> encoder& operator<<(encoder &e, const std::map<K, T, C, A>& v) { return e << as<MAP>(v); } + +#if PN_HAS_CPP11 + +// Encode as ARRAY. +template <class T, class A> encoder& operator<<(encoder &e, const std::forward_list<T, A>& v) { return e << as<ARRAY>(v); } +template <class T, std::size_t N> encoder& operator<<(encoder &e, const std::array<T, N>& v) { return e << as<ARRAY>(v); }n + +// Encode as LIST. +template <class value, class A> encoder& operator<<(encoder &e, const std::forward_list<value, A>& v) { return e << as<LIST>(v); } +template <class value, std::size_t N> encoder& operator<<(encoder &e, const std::array<value, N>& v) { return e << as<LIST>(v); } + +// Encode as map. +template <class K, class T, class C, class A> encoder& operator<<(encoder &e, const std::unordered_map<K, T, C, A>& v) { return e << as<MAP>(v); } + +#endif // PN_HAS_CPP11 + +#endif // PN_NO_CONTAINER_CONVERT } #endif // ENCODER_H http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/engine.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/engine.hpp b/proton-c/bindings/cpp/include/proton/engine.hpp index 672e911..f27033d 100644 --- a/proton-c/bindings/cpp/include/proton/engine.hpp +++ b/proton-c/bindings/cpp/include/proton/engine.hpp @@ -31,7 +31,7 @@ namespace proton { class handler; class connection; -/// Pointers to a data buffer. +/// Pointers to a byte range to use as a buffer. template <class T> class buffer { public: explicit buffer(T* begin__=0, T* end__=0) : begin_(begin__), end_(end__) {} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/message.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/message.hpp b/proton-c/bindings/cpp/include/proton/message.hpp index e9862c6..d996f85 100644 --- a/proton-c/bindings/cpp/include/proton/message.hpp +++ b/proton-c/bindings/cpp/include/proton/message.hpp @@ -26,6 +26,7 @@ #include "proton/message_id.hpp" #include "proton/data.hpp" #include "proton/pn_unique_ptr.hpp" +#include "proton/value.hpp" #include <string> #include <utility> @@ -95,32 +96,31 @@ class message PN_CPP_EXTERN std::string reply_to_group_id() const; ///@} - /** Set the body. If data has more than one value, each is encoded as an AMQP section. */ - PN_CPP_EXTERN void body(const data&); + /** Set the body. */ + PN_CPP_EXTERN void body(const value&); - /** Set the body to any type T that can be converted to proton::data */ - template <class T> void body(const T& v) { body().clear(); body().encoder() << v; } - - /** Get the body values. */ + /** Get the body. Note data can be copied to a proton::value */ PN_CPP_EXTERN const data& body() const; /** Get a reference to the body data, can be modified in-place. */ PN_CPP_EXTERN data& body(); - /** Encode into memory starting at buffer.first and ending before buffer.second */ + // FIXME aconway 2015-11-10: use buffer + /** Encode message into memory starting at buffer.first and ending before buffer.second */ PN_CPP_EXTERN void encode(std::pair<char*, char*> buffer); /** Encode into a string, growing the string if necessary. */ - PN_CPP_EXTERN void encode(std::string &data) const; + PN_CPP_EXTERN void encode(std::string &bytes) const; /** Return encoded message as a string */ PN_CPP_EXTERN std::string encode() const; + // FIXME aconway 2015-11-10: use buffer /** Decode from memory starting at buffer.first and ending before buffer.second */ PN_CPP_EXTERN void decode(std::pair<const char*, const char*> buffer); /** Decode from string data into the message. */ - PN_CPP_EXTERN void decode(const std::string &data); + PN_CPP_EXTERN void decode(const std::string &bytes); /// Decode the message from link corresponding to delivery. PN_CPP_EXTERN void decode(proton::link&, proton::delivery&); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/message_id.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/message_id.hpp b/proton-c/bindings/cpp/include/proton/message_id.hpp index a472afd..4ff8b52 100644 --- a/proton-c/bindings/cpp/include/proton/message_id.hpp +++ b/proton-c/bindings/cpp/include/proton/message_id.hpp @@ -29,6 +29,8 @@ namespace proton { class message_id : public comparable<message_id> { public: message_id() {} + message_id(const value& x) : value_(x) {} + message_id(const data& x) : value_(x) {} message_id(const uint64_t& x) : value_(x) {} message_id(const amqp_uuid& x) : value_(x) {} message_id(const amqp_binary& x) : value_(x) {} @@ -71,7 +73,6 @@ class message_id : public comparable<message_id> { friend PN_CPP_EXTERN decoder& operator>>(decoder&, message_id&); private: - message_id(const value& v) : value_(v) {} value value_; friend class message; }; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/request_response.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/request_response.hpp b/proton-c/bindings/cpp/include/proton/request_response.hpp index 957c91a..58b8156 100644 --- a/proton-c/bindings/cpp/include/proton/request_response.hpp +++ b/proton-c/bindings/cpp/include/proton/request_response.hpp @@ -29,9 +29,6 @@ #include <string> -struct pn_message_t; -struct pn_data_t; - namespace proton { /** http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/type_traits.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/type_traits.hpp b/proton-c/bindings/cpp/include/proton/type_traits.hpp index bebf0b1..d12e0ed 100644 --- a/proton-c/bindings/cpp/include/proton/type_traits.hpp +++ b/proton-c/bindings/cpp/include/proton/type_traits.hpp @@ -30,7 +30,9 @@ #include "proton/types.hpp" namespace proton { -template <bool, class T=void> struct enable_if; +class value; + +template <bool, class T=void> struct enable_if {}; template <class T> struct enable_if<true, T> { typedef T type; }; struct true_type { static const bool value = true; }; @@ -67,8 +69,15 @@ template <> struct is_signed<unsigned long long> : public false_type {}; template <> struct is_signed<signed long long> : public true_type {}; #endif -// Metafunction returning exact AMQP type associated with a C++ type -template <class T> struct type_id_of; +template <class T, class U> struct is_same { static const bool value=false; }; +template <class T> struct is_same<T,T> { static const bool value=true; }; + + +template< class T > struct remove_const { typedef T type; }; +template< class T > struct remove_const<const T> { typedef T type; }; + +// Metafunction returning AMQP type for basic C++ types +template <class T, class Enable=void> struct type_id_of; template<> struct type_id_of<amqp_null> { static const type_id value=NULL_; }; template<> struct type_id_of<amqp_boolean> { static const type_id value=BOOLEAN; }; template<> struct type_id_of<amqp_ubyte> { static const type_id value=UBYTE; }; @@ -91,12 +100,12 @@ template<> struct type_id_of<amqp_binary> { static const type_id value=BINARY; } template<> struct type_id_of<amqp_string> { static const type_id value=STRING; }; template<> struct type_id_of<amqp_symbol> { static const type_id value=SYMBOL; }; -template <class T, class Enable=void> struct has_type_id { static const bool value = false; }; +template <class T, class Enable=void> struct has_type_id : public false_type {}; template <class T> struct has_type_id<T, typename enable_if<!!type_id_of<T>::value>::type> { static const bool value = true; }; -// amqp_map to known integer types by sizeof and signedness. +// Map arbitrary integral types to know AMQP integral types. template<size_t N, bool S> struct integer_type; template<> struct integer_type<1, true> { typedef amqp_byte type; }; template<> struct integer_type<2, true> { typedef amqp_short type; }; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/types.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/types.hpp b/proton-c/bindings/cpp/include/proton/types.hpp index 0f82ae3..b9fedd1 100644 --- a/proton-c/bindings/cpp/include/proton/types.hpp +++ b/proton-c/bindings/cpp/include/proton/types.hpp @@ -159,51 +159,22 @@ struct amqp_timestamp : public comparable<amqp_timestamp> { }; ///@cond INTERNAL -template<class T, type_id A> struct type_pair { +template<class T, type_id A> struct cref { typedef T cpp_type; - type_id type; -}; - -template<class T, type_id A> struct ref : public type_pair<T, A> { - ref(T& v) : value(v) {} - T& value; -}; + static const type_id type; -template<class T, type_id A> struct cref : public type_pair<T, A> { cref(const T& v) : value(v) {} - cref(const ref<T,A>& ref) : value(ref.value) {} const T& value; }; +template <class T, type_id A> const type_id cref<T, A>::type = A; ///@endcond INTERNAL -/** A holder for AMQP values. A holder is always encoded/decoded as its amqp_value, no need - * for the as<TYPE>() helper functions. - * - * For example to encode an array of arrays using std::vector: - * - * typedef holder<std::vector<amqp_string>, ARRAY> Inner; - * typedef holder<std::vector<Inner>, ARRAY> Outer; - * Outer o ... - * encoder << o; - * - */ -template<class T, type_id A> struct holder : public type_pair<T, A> { - T value; -}; - -/** Create a reference to value as AMQP type A for decoding. - * For example to decode an array of amqp_int: - * - * std::vector<amqp_int> v; - * decoder >> as<ARRAY>(v); - */ -template <type_id 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. - * For example to encode an array of amqp_int: +/** + * Indicate the desired AMQP type to use when encoding T. + * For example to encode a vector as a list: * * std::vector<amqp_int> v; - * encoder << as<ARRAY>(v); + * encoder << as<LIST>(v); */ template <type_id A, class T> cref<T, A> as(const T& value) { return cref<T, A>(value); } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/value.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/value.hpp b/proton-c/bindings/cpp/include/proton/value.hpp index 8816064..ddc5501 100644 --- a/proton-c/bindings/cpp/include/proton/value.hpp +++ b/proton-c/bindings/cpp/include/proton/value.hpp @@ -30,11 +30,23 @@ class data; class encoder; class decoder; -/** AMQP data with normal value semantics: copy, assign etc. */ +/** + * Holder for an AMQP value. + * + * proton::value can hold any AMQP data value, simple or compound. It has + * assignment and conversion operators to convert its contents easily to and + * from native C++ types. + * + * See proton::encoder and proton::decoder for details of the conversion rules. + * Assigning to a proton::value follows the encoder rules, converting from a + * proton::value (or calling proton::value::get) follows the decoder rules. + */ class value : public comparable<value> { public: PN_CPP_EXTERN value(); PN_CPP_EXTERN value(const value& x); + PN_CPP_EXTERN value(const data&); + template <class T> value(const T& x) : data_(data::create()) { *data_ = x; } PN_CPP_EXTERN value& operator=(const value& x); @@ -44,23 +56,26 @@ class value : public comparable<value> { PN_CPP_EXTERN void clear(); PN_CPP_EXTERN bool empty() const; - /** Encoder to encode complex data into this value. - * Note if you enocde more than one value, all but the first will be ignored. - */ + // FIXME aconway 2015-11-06: rename encode/decode + + /** Encoder to encode complex data into this value. Note this clears the value. */ PN_CPP_EXTERN class encoder& encoder(); - /** Decoder to decode complex data from this value */ - PN_CPP_EXTERN class decoder& decoder(); + /** Decoder to decode complex data from this value. Note this rewinds the decoder. */ + PN_CPP_EXTERN class decoder& decoder() const; /** Type of the current value*/ PN_CPP_EXTERN type_id type() const; /** Get the value. */ - template<class T> void get(T &t) const { rewind() >> t; } + template<class T> void get(T &t) const { decoder() >> t; } /** Get the value. */ template<class T> T get() const { T t; get(t); return t; } + /** Automatic conversion */ + template<class T> operator T() const { return get<T>(); } + PN_CPP_EXTERN bool operator==(const value& x) const; PN_CPP_EXTERN bool operator<(const value& x) const; @@ -69,9 +84,6 @@ class value : public comparable<value> { friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream& o, const value& dv); private: - value(const data&); - class decoder& rewind() const { data_->decoder().rewind(); return data_->decoder(); } - pn_unique_ptr<data> data_; friend class message; }; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/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 d148b12..1fe13a2 100644 --- a/proton-c/bindings/cpp/src/decoder.cpp +++ b/proton-c/bindings/cpp/src/decoder.cpp @@ -147,16 +147,19 @@ decoder& operator>>(decoder& d, finish) { pn_data_exit(pn_cast(&d)); return d; } decoder& operator>>(decoder& d, skip) { pn_data_next(pn_cast(&d)); return d; } +decoder& operator>>(decoder& d, assert_type a) { bad_type(a.type, d.type()); return d; } + decoder& operator>>(decoder& d, rewind) { d.rewind(); return d; } -decoder& operator>>(decoder& d, data& v) { - if (pn_cast(&d) == pn_cast(&v)) throw decode_error("extract into self"); - v.clear(); +decoder& operator>>(decoder& d, value& v) { + pn_data_t *ddata = pn_cast(&d); + pn_data_t *vdata = pn_cast(&v.encoder()); + if (ddata == vdata) throw decode_error("extract into self"); { - narrow n(pn_cast(&d)); - check(pn_data_appendn(pn_cast(&v), pn_cast(&d), 1)); + narrow n(ddata); + check(pn_data_appendn(vdata, ddata, 1)); } - if (!pn_data_next(pn_cast(&d))) throw decode_error("no more data"); + if (!pn_data_next(ddata)) throw decode_error("no more data"); return d; } @@ -340,4 +343,12 @@ decoder& operator>>(decoder& d, std::string& value) { return d; } +void assert_map_scope(const decoder::scope& s) { + if (s.type != MAP) + throw decode_error("cannot decode "+type_name(s.type)+" as map"); + if (s.size % 2 != 0) + throw decode_error("odd number of elements in map"); +} + + } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/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 0230e88..c8e6928 100644 --- a/proton-c/bindings/cpp/src/encoder.cpp +++ b/proton-c/bindings/cpp/src/encoder.cpp @@ -130,9 +130,11 @@ encoder& operator<<(encoder& e, amqp_string value) { return insert(e, pn_cast(&e encoder& operator<<(encoder& e, amqp_symbol value) { return insert(e, pn_cast(&e), value, pn_data_put_symbol); } encoder& operator<<(encoder& e, amqp_binary value) { return insert(e, pn_cast(&e), value, pn_data_put_binary); } -encoder& operator<<(encoder& e, const data& v) { - if (pn_cast(&e) == pn_cast(&v)) throw encode_error("cannot insert into self"); - check(pn_data_append(pn_cast(&e), pn_cast(&v)), pn_cast(&e)); +encoder& operator<<(encoder& e, const value& v) { + pn_data_t *edata = pn_cast(&e); + pn_data_t *vdata = pn_cast(&v.decoder()); + if (edata == vdata) throw encode_error("cannot insert into self"); + check(pn_data_append(edata, vdata), edata); return e; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/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 ac2701f..0bac03c 100644 --- a/proton-c/bindings/cpp/src/interop_test.cpp +++ b/proton-c/bindings/cpp/src/interop_test.cpp @@ -39,7 +39,11 @@ string read(string filename) { return string(istreambuf_iterator<char>(ifs), istreambuf_iterator<char>()); } -template <class T> T get(decoder& d) { return d.get_as<T, type_id_of<T>::value>(); } +template <class T> T get(decoder& d) { + T v; + d >> assert_type(type_id_of<T>::value) >> v; + return v; +} template <class T> std::string str(const T& value) { ostringstream oss; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/src/message.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/message.cpp b/proton-c/bindings/cpp/src/message.cpp index 63f6520..7ebb821 100644 --- a/proton-c/bindings/cpp/src/message.cpp +++ b/proton-c/bindings/cpp/src/message.cpp @@ -161,7 +161,7 @@ std::string message::reply_to_group_id() const { return s ? std::string(s) : std::string(); } -void message::body(const data& v) { body() = v; } +void message::body(const value& v) { body() = v; } const data& message::body() const { return *data::cast(pn_message_body(message_)); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/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 6b4c402..7059d69 100644 --- a/proton-c/bindings/cpp/src/value.cpp +++ b/proton-c/bindings/cpp/src/value.cpp @@ -40,18 +40,16 @@ void value::clear() { data_->clear(); } bool value::empty() const { return data_->empty(); } -class encoder& value::encoder() { return data_->encoder(); } +class encoder& value::encoder() { clear(); return data_->encoder(); } -class decoder& value::decoder() { return data_->decoder(); } +class decoder& value::decoder() const { data_->decoder().rewind(); return data_->decoder(); } -type_id value::type() const { return rewind().type(); } +type_id value::type() const { return decoder().type(); } bool value::operator==(const value& x) const { return *data_ == *x.data_; } bool value::operator<(const value& x) const { return *data_ < *x.data_; } - - std::ostream& operator<<(std::ostream& o, const value& v) { // pn_inspect prints strings with quotes which is not normal in C++. switch (v.type()) { @@ -63,12 +61,4 @@ std::ostream& operator<<(std::ostream& o, const value& v) { } } -class encoder& operator<<(class encoder& e, const value& v) { - return e << *v.data_; -} - -class decoder& operator>>(class decoder& d, value& v) { - return d >> *v.data_; -} - } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
