PROTON-1085: c++: clean up access to message properties, instructions and annotations.
- Use proton::value& as message::body() type. - Create pn_message on demand for empty message. - Use proton::scalar as basis for messsage_id and annotation_key - message_id restricted to be one of: uint64_t, amqp_uuid, amqp_binary or amqp_string. - annotation_key restricted to be on of: uint64_t or amqp_symbol. - Decode and cache application properties, annotations and instructions on demand as std::map. - message::properties() returns std::map<std::string, scalar>& - message::instructions()/annotations() returns std::map<annotation_key, value>& Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/1622d0e6 Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/1622d0e6 Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/1622d0e6 Branch: refs/heads/go1 Commit: 1622d0e608738da7b603b7e497bc3a8166325865 Parents: d2c5a5f Author: Alan Conway <[email protected]> Authored: Fri Dec 18 09:33:58 2015 -0500 Committer: Alan Conway <[email protected]> Committed: Tue Dec 29 15:56:44 2015 -0500 ---------------------------------------------------------------------- examples/cpp/client.cpp | 2 +- examples/cpp/example_test.py | 18 +- proton-c/CMakeLists.txt | 2 +- .../cpp/include/proton/annotation_key.hpp | 58 +++++ proton-c/bindings/cpp/include/proton/data.hpp | 2 - .../bindings/cpp/include/proton/decoder.hpp | 27 +-- .../bindings/cpp/include/proton/encoder.hpp | 26 +- proton-c/bindings/cpp/include/proton/error.hpp | 6 + .../bindings/cpp/include/proton/message.hpp | 81 +++---- .../bindings/cpp/include/proton/message_id.hpp | 67 ++--- proton-c/bindings/cpp/include/proton/scalar.hpp | 117 +++++---- proton-c/bindings/cpp/include/proton/ssl.hpp | 2 +- .../bindings/cpp/include/proton/type_traits.hpp | 2 +- proton-c/bindings/cpp/include/proton/types.hpp | 77 +++--- proton-c/bindings/cpp/include/proton/value.hpp | 16 +- proton-c/bindings/cpp/src/decoder.cpp | 138 ++++++----- .../bindings/cpp/src/encode_decode_test.cpp | 39 ++- proton-c/bindings/cpp/src/encoder.cpp | 56 +++-- proton-c/bindings/cpp/src/error.cpp | 2 + proton-c/bindings/cpp/src/event.cpp | 2 +- proton-c/bindings/cpp/src/message.cpp | 242 ++++++++----------- proton-c/bindings/cpp/src/message_test.cpp | 102 +++++--- proton-c/bindings/cpp/src/scalar.cpp | 97 +++++--- proton-c/bindings/cpp/src/scalar_test.cpp | 80 ++++-- proton-c/bindings/cpp/src/types.cpp | 20 +- proton-c/bindings/cpp/src/value.cpp | 14 ++ 26 files changed, 751 insertions(+), 544 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/examples/cpp/client.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/client.cpp b/examples/cpp/client.cpp index 4dee119..3f5c4ab 100644 --- a/examples/cpp/client.cpp +++ b/examples/cpp/client.cpp @@ -58,7 +58,7 @@ class client : public proton::messaging_handler { void on_message(proton::event &e) { if (requests.empty()) return; // Spurious extra message! proton::message& response = e.message(); - std::cout << '"' << requests.front() << '"' << " => " << response.body() << std::endl; + std::cout << requests.front() << " => " << response.body() << std::endl; requests.erase(requests.begin()); if (!requests.empty()) { send_request(); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/examples/cpp/example_test.py ---------------------------------------------------------------------- diff --git a/examples/cpp/example_test.py b/examples/cpp/example_test.py index 1f800c0..c1e7ac5 100644 --- a/examples/cpp/example_test.py +++ b/examples/cpp/example_test.py @@ -125,17 +125,17 @@ class ExampleTest(unittest.TestCase): def test_helloworld(self): b = Broker.get() hw = execute("helloworld", b.addr) - self.assertEqual('"Hello World!"\n', hw) + self.assertEqual('Hello World!\n', hw) def test_helloworld_blocking(self): b = Broker.get() hw = execute("helloworld_blocking", b.addr) - self.assertEqual('"Hello World!"\n', hw) + self.assertEqual('Hello World!\n', hw) def test_helloworld_direct(self): addr = pick_addr() hw = execute("helloworld_direct", addr) - self.assertEqual('"Hello World!"\n', hw) + self.assertEqual('Hello World!\n', hw) def test_simple_send_recv(self): b = Broker.get() @@ -165,10 +165,10 @@ class ExampleTest(unittest.TestCase): send_expect = "direct_send listening on amqp://%s\nall messages confirmed\n" % (addr) self.assertEqual(send_expect, verify(send)) - CLIENT_EXPECT=""""Twas brillig, and the slithy toves" => "TWAS BRILLIG, AND THE SLITHY TOVES" -"Did gire and gymble in the wabe." => "DID GIRE AND GYMBLE IN THE WABE." -"All mimsy were the borogroves," => "ALL MIMSY WERE THE BOROGROVES," -"And the mome raths outgrabe." => "AND THE MOME RATHS OUTGRABE." + CLIENT_EXPECT="""Twas brillig, and the slithy toves => TWAS BRILLIG, AND THE SLITHY TOVES +Did gire and gymble in the wabe. => DID GIRE AND GYMBLE IN THE WABE. +All mimsy were the borogroves, => ALL MIMSY WERE THE BOROGROVES, +And the mome raths outgrabe. => AND THE MOME RATHS OUTGRABE. """ def test_simple_recv_send(self): # Start receiver first, then run sender""" @@ -247,7 +247,7 @@ Tock... def test_ssl(self): # SSL without SASL expect="""Outgoing client connection connected via SSL. Server certificate identity CN=test_server -"Hello World!" +Hello World! """ addr = "amqps://" + pick_addr() + "/examples" ignore_first_line, ignore_nl, ssl_hw = execute("ssl", addr, ssl_certs_dir()).partition('\n') @@ -257,7 +257,7 @@ Tock... # SSL with SASL EXTERNAL expect="""Inbound client certificate identity CN=test_client Outgoing client connection connected via SSL. Server certificate identity CN=test_server -"Hello World!" +Hello World! """ addr = "amqps://" + pick_addr() + "/examples" ignore_first_line, ignore_nl, ssl_hw = execute("ssl_client_cert", addr, ssl_certs_dir()).partition('\n') http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt index 3d2f457..b645563 100644 --- a/proton-c/CMakeLists.txt +++ b/proton-c/CMakeLists.txt @@ -246,7 +246,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") if (ENABLE_WARNING_ERROR) set (WERROR "-Werror") endif (ENABLE_WARNING_ERROR) - set (CXX_WARNING_FLAGS "${WERROR} -pedantic -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-float-equal -Wno-padded -Wno-sign-conversion -Wno-switch-enum -Wno-weak-vtables -Wno-exit-time-destructors -Wno-global-constructors -Wno-shorten-64-to-32") + set (CXX_WARNING_FLAGS "${WERROR} -pedantic -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-float-equal -Wno-padded -Wno-sign-conversion -Wno-switch-enum -Wno-weak-vtables -Wno-exit-time-destructors -Wno-global-constructors -Wno-shorten-64-to-32 -Wno-documentation -Wno-documentation-unknown-command") endif() if (MSVC) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/bindings/cpp/include/proton/annotation_key.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/annotation_key.hpp b/proton-c/bindings/cpp/include/proton/annotation_key.hpp new file mode 100644 index 0000000..edd63b1 --- /dev/null +++ b/proton-c/bindings/cpp/include/proton/annotation_key.hpp @@ -0,0 +1,58 @@ +#ifndef ANNOTATION_KEY_HPP +#define ANNOTATION_KEY_HPP +/* + * 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/types.hpp" +#include "proton/scalar.hpp" + +namespace proton { +class encoder; +class decoder; + +/** An annotation_key can contain one of the following types: uint64_t or amqp_symbol. */ +class annotation_key : public restricted_scalar { + public: + annotation_key() { scalar_ = uint64_t(0); } + + ///@name Assign a C++ value, deduce the AMQP type() + ///@{ + annotation_key& operator=(uint64_t x) { scalar_ = x; return *this; } + annotation_key& operator=(const amqp_symbol& x) { scalar_ = x; return *this; } + /// std::string is encoded as amqp_symbol + annotation_key& operator=(const std::string& x) { scalar_ = amqp_symbol(x); return *this; } + /// char* is encoded as amqp_symbol + annotation_key& operator=(const char *x) { scalar_ = amqp_symbol(x); return *this; } + ///@} + + /// Converting constructor from any type that we can assign from. + template <class T> annotation_key(T x) { *this = x; } + + void get(uint64_t& x) const { scalar_.get(x); } + void get(amqp_symbol& x) const { scalar_.get(x); } + + template<class T> T get() const { T x; get(x); return x; } + + friend PN_CPP_EXTERN encoder operator<<(encoder, const annotation_key&); + friend PN_CPP_EXTERN decoder operator>>(decoder, annotation_key&); + friend class message; +}; + +} +#endif // ANNOTATION_KEY_HPP http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/bindings/cpp/include/proton/data.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/data.hpp b/proton-c/bindings/cpp/include/proton/data.hpp index 37e1a61..7581ac7 100644 --- a/proton-c/bindings/cpp/include/proton/data.hpp +++ b/proton-c/bindings/cpp/include/proton/data.hpp @@ -39,8 +39,6 @@ class data; class data : public object<pn_data_t> { public: data(pn_data_t* d) : object<pn_data_t>(d) {} - data(const data& d) : object<pn_data_t>(d) {} - data& operator=(const data&); PN_CPP_EXTERN static data create(); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 3073989..5dae3c3 100644 --- a/proton-c/bindings/cpp/include/proton/decoder.hpp +++ b/proton-c/bindings/cpp/include/proton/decoder.hpp @@ -44,11 +44,11 @@ struct pn_data_t; namespace proton { +class scalar; 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&); }; +class annotation_key; +class value; /** Skips a value with `dec >> skip()`. */ struct skip{}; @@ -208,7 +208,9 @@ class decoder : public object<pn_data_t> { 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, annotation_key&); PN_CPP_EXTERN friend decoder operator>>(decoder, value&); + PN_CPP_EXTERN friend decoder operator>>(decoder, scalar&); ///@} /** Extract and return a value of type T. */ @@ -256,8 +258,6 @@ class decoder : public object<pn_data_t> { private: PN_CPP_EXTERN void check_type(type_id); - - friend class encoder; }; /** Call decoder::start() in constructor, decoder::finish in destructor(). @@ -280,20 +280,13 @@ operator>>(decoder d, T& i) { } ///@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) {} +template <class T> struct ref { + ref(T& v) : value(v) {} T& value; }; +template <class T> struct sequence_ref : public ref<T> { sequence_ref(T& v) : ref<T>(v) {} }; +template <class T> struct map_ref : public ref<T> { map_ref(T& v) : ref<T>(v) {} }; +template <class T> struct pairs_ref : public ref<T> { pairs_ref(T& v) : ref<T>(v) {} }; ///@endcond /** http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 940064d..e521922 100644 --- a/proton-c/bindings/cpp/include/proton/encoder.hpp +++ b/proton-c/bindings/cpp/include/proton/encoder.hpp @@ -44,11 +44,31 @@ struct pn_data_t; namespace proton { +class scalar; class data; class message_id; +class annotation_key; +class value; -/** Raised by encoder operations on error */ -struct encode_error : public error { PN_CPP_EXTERN explicit encode_error(const std::string&); }; +///@cond INTERNAL +template<class T, type_id A> struct cref { + typedef T cpp_type; + static const type_id type; + + cref(const T& v) : value(v) {} + const T& value; +}; +template <class T, type_id A> const type_id cref<T, A>::type = A; +///@endcond INTERNAL + +/** + * 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<LIST>(v); + */ +template <type_id A, class T> cref<T, A> as(const T& value) { return cref<T, A>(value); } /** * Stream-like encoder from C++ values to AMQP values. @@ -139,7 +159,9 @@ class encoder : public object<pn_data_t> { 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 annotation_key&); friend PN_CPP_EXTERN encoder operator<<(encoder, const value&); + friend PN_CPP_EXTERN encoder operator<<(encoder, const scalar&); ///@} /** http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/bindings/cpp/include/proton/error.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/error.hpp b/proton-c/bindings/cpp/include/proton/error.hpp index d2f57c6..f3475cf 100644 --- a/proton-c/bindings/cpp/include/proton/error.hpp +++ b/proton-c/bindings/cpp/include/proton/error.hpp @@ -34,6 +34,12 @@ struct error : public std::runtime_error { PN_CPP_EXTERN explicit error(const st /** Raised if timeout expires */ struct timeout_error : public error { PN_CPP_EXTERN explicit timeout_error(const std::string&); }; +/** Raised if there is an error decoding AMQP data as a C++ value. */ +struct decode_error : public error { PN_CPP_EXTERN explicit decode_error(const std::string&); }; + +/** Raised if there is an error encoding a C++ value as AMQP data. */ +struct encode_error : public error { PN_CPP_EXTERN explicit encode_error(const std::string&); }; + } #endif /*!PROTON_CPP_EXCEPTIONS_H*/ http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 52195aa..8bd1f85 100644 --- a/proton-c/bindings/cpp/include/proton/message.hpp +++ b/proton-c/bindings/cpp/include/proton/message.hpp @@ -22,9 +22,10 @@ * */ -#include "proton/data.hpp" #include "proton/export.hpp" #include "proton/message_id.hpp" +#include "proton/annotation_key.hpp" +#include "proton/pn_unique_ptr.hpp" #include "proton/value.hpp" #include "proton/duration.hpp" @@ -38,22 +39,28 @@ namespace proton { class link; class delivery; class message_id; +class annotation_key; /** An AMQP message. Value semantics, can be copied or assigned to make a new message. */ class message { public: + typedef std::map<std::string, scalar> property_map; + typedef std::map<annotation_key, value> annotation_map; + PN_CPP_EXTERN message(); PN_CPP_EXTERN message(const message&); - PN_CPP_EXTERN message(const value&); - #if PN_HAS_CPP11 PN_CPP_EXTERN message(message&&); #endif + /// Constructor that sets the body from any type that can be assigned to a value. + template <class T> explicit message(const T& body_) { body() = body_; } + PN_CPP_EXTERN ~message(); + PN_CPP_EXTERN message& operator=(const message&); - void swap(message& x); + PN_CPP_EXTERN void swap(message& x); /** Clear the message content and properties. */ PN_CPP_EXTERN void clear(); @@ -99,47 +106,28 @@ class message ///@} - /** Set the body. */ - PN_CPP_EXTERN void body(const value&); + /** Set the body, equivalent to body() = v */ + template<class T> void body(const T& v) { body() = v; } - /** Get the body. Note data can be copied to a proton::value */ - PN_CPP_EXTERN const data body() const; + /** Get the body. */ + PN_CPP_EXTERN const value& body() const; - /** Get a reference to the body data, can be modified in-place. */ - PN_CPP_EXTERN data body(); + /** Get a reference to the body that can be modified in-place. */ + PN_CPP_EXTERN value& body(); - /** Set the application properties. Must be a map with string keys or an - * empty value. You can assign to a proton::value from a standard C++ map - * of std::string to proton::value. - */ - PN_CPP_EXTERN void properties(const value&); + /** Application properties map, can be modified in place. */ + PN_CPP_EXTERN property_map& properties(); + PN_CPP_EXTERN const property_map& properties() const; - /** Get the application properties, which will be a map with string keys or - * an empty value. You can assign proton::value containing a map to a - * standard C++ map of std::string to proton::value. - */ - PN_CPP_EXTERN const data properties() const; + /** Message annotations map, can be modified in place. */ + PN_CPP_EXTERN annotation_map& annotations(); + PN_CPP_EXTERN const annotation_map& annotations() const; - /** Get a reference to the application properties, can be modified in-place.*/ - PN_CPP_EXTERN data properties(); + /** Delivery instructions map, can be modified in place. */ + PN_CPP_EXTERN annotation_map& instructions(); + PN_CPP_EXTERN const annotation_map& instructions() const; - /** Set an individual application property. */ - PN_CPP_EXTERN void property(const std::string &name, const value &); - - /** Get an individual application property. Returns an empty value if not found. */ - PN_CPP_EXTERN value property(const std::string &name) const; - - /** Erase an application property. Returns false if there was no such property. */ - PN_CPP_EXTERN bool erase_property(const std::string &name); - - PN_CPP_EXTERN void annotations(const value&); - PN_CPP_EXTERN const data annotations() const; - PN_CPP_EXTERN data annotations(); - PN_CPP_EXTERN void annotation(const proton::amqp_symbol &key, const value &val); - PN_CPP_EXTERN value annotation(const proton::amqp_symbol &key) const; - PN_CPP_EXTERN bool erase_annotation(const proton::amqp_symbol &key); - - /** Encode into a string, growing the string if necessary. */ + /** Encode entire message into a string, growing the string if necessary. */ PN_CPP_EXTERN void encode(std::string &bytes) const; /** Return encoded message as a string */ @@ -148,7 +136,7 @@ class message /** Decode from string data into the message. */ PN_CPP_EXTERN void decode(const std::string &bytes); - /// Decode the message from link corresponding to delivery. + /** Decode the message corresponding to a delivery from a link. */ PN_CPP_EXTERN void decode(proton::link, proton::delivery); /** @@ -160,8 +148,6 @@ class message * and AMQP SEQUENCE sections, respectively. If inferred is false, * then all values in the body of the message will be encoded as AMQP * VALUE sections regardless of their type. - * - * @return the value of the inferred flag for the message */ PN_CPP_EXTERN bool inferred() const; /** Get the inferred flag for a message. */ @@ -247,7 +233,16 @@ class message PN_CPP_EXTERN void sequence(int32_t); private: - pn_message_t *message_; + pn_message_t *pn_msg() const; + struct impl { + PN_CPP_EXTERN impl(); + PN_CPP_EXTERN ~impl(); + mutable pn_message_t *msg; + mutable value body; + } impl_; + mutable property_map properties_; + mutable annotation_map annotations_; + mutable annotation_map instructions_; }; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 4734dff..b08482f 100644 --- a/proton-c/bindings/cpp/include/proton/message_id.hpp +++ b/proton-c/bindings/cpp/include/proton/message_id.hpp @@ -20,63 +20,44 @@ */ #include "proton/types.hpp" -#include "proton/value.hpp" +#include "proton/scalar.hpp" namespace proton { +class encoder; +class decoder; + /** A message_id can contain one of the following types: - * uint64_t (aka amqp_ulong), amqp_uuid, amqp_binary or amqp_string. + * uint64_t, amqp_uuid, amqp_binary or amqp_string. */ -class message_id : public comparable<message_id> { +class message_id : public restricted_scalar { public: - message_id() {} - message_id(const message_id& x) : value_(x.value_) {} - message_id(const uint64_t& x) : value_(x) {} - message_id(const amqp_uuid& x) : value_(x) {} - message_id(const amqp_binary& x) : value_(x) {} - message_id(const amqp_string& x) : value_(x) {} - /// string is encoded as amqp_string - message_id(const std::string& x) : value_(x) {} - message_id(const char *x) : value_(x) {} + message_id() { scalar_ = uint64_t(0); } - message_id& operator=(const message_id& x) { value_ = x.value_; return *this; } - message_id& operator=(const uint64_t& x) { value_ = x; return *this; } - message_id& operator=(const amqp_uuid& x) { value_ = x; return *this; } - message_id& operator=(const amqp_binary& x) { value_ = x; return *this; } - message_id& operator=(const amqp_string& x) { value_ = x; return *this; } - /// string is encoded as amqp_string - message_id& operator=(const std::string& x) { value_ = x; return *this; } - message_id& operator=(const char *x) { value_ = x; return *this; } + ///@name Assign a C++ value, deduce the AMQP type() + ///@{ + message_id& operator=(uint64_t x) { scalar_ = x; return *this; } + message_id& operator=(const amqp_uuid& x) { scalar_ = x; return *this; } + message_id& operator=(const amqp_binary& x) { scalar_ = x; return *this; } + message_id& operator=(const amqp_string& x) { scalar_ = x; return *this; } + /// std::string is encoded as amqp_string + message_id& operator=(const std::string& x) { scalar_ = amqp_string(x); return *this; } + /// char* is encoded as amqp_string + message_id& operator=(const char *x) { scalar_ = amqp_string(x); return *this; } + ///@} - void clear() { value_.clear(); } - bool empty() const { return value_.empty(); } - type_id type() { return value_.type(); } + /// Converting constructor from any type that we can assign from. + template <class T> message_id(T x) { *this = x; } - void get(uint64_t& x) const { value_.get(x); } - void get(amqp_uuid& x) const { value_.get(x); } - void get(amqp_binary& x) const { value_.get(x); } - void get(amqp_string& x) const { value_.get(x); } - /// Both amqp_binary and amqp_string can be converted to std::string - void get(std::string& x) const { value_.get(x); } + void get(uint64_t& x) const { scalar_.get(x); } + void get(amqp_uuid& x) const { scalar_.get(x); } + void get(amqp_binary& x) const { scalar_.get(x); } + void get(amqp_string& x) const { scalar_.get(x); } template<class T> T get() const { T x; get(x); return x; } - // String representation: decimal representation of uint64_t, standard text - // representation of UUID, amqp_string or amqp_binary are returned without - // modification. - std::string& str() const; - - bool operator==(const message_id& x) const { return value_ == x.value_; } - bool operator<(const message_id& x) const { return value_ < x.value_; } - - friend std::ostream& operator<<(std::ostream&, const message_id&); friend PN_CPP_EXTERN encoder operator<<(encoder, const message_id&); friend PN_CPP_EXTERN decoder operator>>(decoder, message_id&); - - private: - //message_id(const data d) : value_(d) {} - value value_; friend class message; - friend class data; }; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/bindings/cpp/include/proton/scalar.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/scalar.hpp b/proton-c/bindings/cpp/include/proton/scalar.hpp index 0745281..6fe29fa 100644 --- a/proton-c/bindings/cpp/include/proton/scalar.hpp +++ b/proton-c/bindings/cpp/include/proton/scalar.hpp @@ -21,54 +21,56 @@ #include "proton/types.hpp" #include <iosfwd> +#include <string> namespace proton { -class scalar; +class encoder; +class decoder; -/** scalar holds an instance of an atomic proton type. */ +/** scalar holds an instance of any scalar AMQP type. */ class scalar : public comparable<scalar> { public: PN_CPP_EXTERN scalar(); - // Use default assign and copy. + PN_CPP_EXTERN scalar(const scalar&); + PN_CPP_EXTERN scalar& operator=(const scalar&); /// Type for the value in the scalar, NULL_TYPE if empty() PN_CPP_EXTERN type_id type() const; + /// True if the scalar is empty. PN_CPP_EXTERN bool empty() const; - ///@name Create an scalar, type() is deduced from the C++ type of the value. + ///@name Assign a C++ value, deduce the AMQP type() ///@{ - PN_CPP_EXTERN explicit scalar(bool); - PN_CPP_EXTERN explicit scalar(uint8_t); - PN_CPP_EXTERN explicit scalar(int8_t); - PN_CPP_EXTERN explicit scalar(uint16_t); - PN_CPP_EXTERN explicit scalar(int16_t); - PN_CPP_EXTERN explicit scalar(uint32_t); - PN_CPP_EXTERN explicit scalar(int32_t); - PN_CPP_EXTERN explicit scalar(uint64_t); - PN_CPP_EXTERN explicit scalar(int64_t); - PN_CPP_EXTERN explicit scalar(wchar_t); - PN_CPP_EXTERN explicit scalar(float); - PN_CPP_EXTERN explicit scalar(double); - PN_CPP_EXTERN explicit scalar(amqp_timestamp); - PN_CPP_EXTERN explicit scalar(const amqp_decimal32&); - PN_CPP_EXTERN explicit scalar(const amqp_decimal64&); - PN_CPP_EXTERN explicit scalar(const amqp_decimal128&); - PN_CPP_EXTERN explicit scalar(const amqp_uuid&); - PN_CPP_EXTERN explicit scalar(const amqp_string&); - PN_CPP_EXTERN explicit scalar(const amqp_symbol&); - PN_CPP_EXTERN explicit scalar(const amqp_binary&); - PN_CPP_EXTERN explicit scalar(const std::string& s); ///< Treated as an AMQP string - PN_CPP_EXTERN explicit scalar(const char* s); ///< Treated as an AMQP string + PN_CPP_EXTERN scalar& operator=(bool); + PN_CPP_EXTERN scalar& operator=(uint8_t); + PN_CPP_EXTERN scalar& operator=(int8_t); + PN_CPP_EXTERN scalar& operator=(uint16_t); + PN_CPP_EXTERN scalar& operator=(int16_t); + PN_CPP_EXTERN scalar& operator=(uint32_t); + PN_CPP_EXTERN scalar& operator=(int32_t); + PN_CPP_EXTERN scalar& operator=(uint64_t); + PN_CPP_EXTERN scalar& operator=(int64_t); + PN_CPP_EXTERN scalar& operator=(wchar_t); + PN_CPP_EXTERN scalar& operator=(float); + PN_CPP_EXTERN scalar& operator=(double); + PN_CPP_EXTERN scalar& operator=(amqp_timestamp); + PN_CPP_EXTERN scalar& operator=(const amqp_decimal32&); + PN_CPP_EXTERN scalar& operator=(const amqp_decimal64&); + PN_CPP_EXTERN scalar& operator=(const amqp_decimal128&); + PN_CPP_EXTERN scalar& operator=(const amqp_uuid&); + PN_CPP_EXTERN scalar& operator=(const amqp_string&); + PN_CPP_EXTERN scalar& operator=(const amqp_symbol&); + PN_CPP_EXTERN scalar& operator=(const amqp_binary&); + PN_CPP_EXTERN scalar& operator=(const std::string& s); ///< Treated as an AMQP string + PN_CPP_EXTERN scalar& operator=(const char* s); ///< Treated as an AMQP string ///@} - /// Assign to an scalar using the same rules as construction. - template <class T> scalar& operator=(T x) { return *this = scalar(x); } + /// Construct from any type that we can assign from. + template <class T> explicit scalar(T x) { *this = x; } - ///@name get(T&) extracts the value if the types match exactly, - ///i.e. if `type() == type_id_of<T>::value` - /// throws type_mismatch otherwise. + ///@name get(T&) extracts the value if the types match exactly, throws type_error otherwise. ///@{ PN_CPP_EXTERN void get(bool&) const; PN_CPP_EXTERN void get(uint8_t&) const; @@ -97,25 +99,58 @@ class scalar : public comparable<scalar> { template<class T> T get() const { T x; get(x); return x; } ///@name as_ methods do "loose" conversion, they will convert the scalar's - ///value to the requested type if possible, else throw type_mismatch + ///value to the requested type if possible, else throw type_error ///@{ - PN_CPP_EXTERN int64_t as_int() const; ///< Allowed if type_id_integral(type()) - PN_CPP_EXTERN uint64_t as_uint() const; ///< Allowed if type_id_integral(type()) - PN_CPP_EXTERN double as_double() const; ///< Allowed if type_id_floating_point(type()) - PN_CPP_EXTERN std::string as_string() const; ///< Allowed if type_id_string_like(type()) + PN_CPP_EXTERN int64_t as_int() const; ///< Allowed if type_id_is_integral(type()) + PN_CPP_EXTERN uint64_t as_uint() const; ///< Allowed if type_id_is_integral(type()) + PN_CPP_EXTERN double as_double() const; ///< Allowed if type_id_is_floating_point(type()) + PN_CPP_EXTERN std::string as_string() const; ///< Allowed if type_id_is_string_like(type()) ///@} - PN_CPP_EXTERN bool operator==(const scalar& x) const; - /// Note if the values are of different type(), operator< will compare the type() - PN_CPP_EXTERN bool operator<(const scalar& x) const; + friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const scalar&); + friend PN_CPP_EXTERN encoder operator<<(encoder, const scalar&); + friend PN_CPP_EXTERN decoder operator>>(decoder, scalar&); + - PN_CPP_EXTERN friend std::ostream& operator<<(std::ostream&, const scalar&); + /// Scalars with different type() are considered unequal even if the values + /// are equal as numbers or strings. + friend PN_CPP_EXTERN bool operator==(const scalar& x, const scalar& y); + + /// For scalars of different type(), operator< sorts by order of type(). + friend PN_CPP_EXTERN bool operator<(const scalar& x, const scalar& y); private: void ok(pn_type_t) const; - void set(const std::string&); + void set(const std::string&, pn_type_t); + void set(const pn_atom_t&); pn_atom_t atom_; std::string str_; // Owner of string-like data. + + friend class message; +}; + +///@internal base for restricted scalar types +class restricted_scalar : public comparable<restricted_scalar> { + public: + operator scalar() const { return scalar_; } + type_id type() const { return scalar_.type(); } + + ///@name as_ methods do "loose" conversion, they will convert the scalar's + ///value to the requested type if possible, else throw type_error + ///@{ + int64_t as_int() const { return scalar_.as_int(); } + uint64_t as_uint() const { return scalar_.as_uint(); } + double as_double() const { return scalar_.as_double(); } + std::string as_string() const { return scalar_.as_string(); } + ///@} + + friend std::ostream& operator<<(std::ostream& o, const restricted_scalar& x) { return o << x.scalar_; } + friend bool operator<(const restricted_scalar& x, const restricted_scalar& y) { return x.scalar_ < y.scalar_; } + friend bool operator==(const restricted_scalar& x, const restricted_scalar& y) { return x.scalar_ == y.scalar_; } + + protected: + restricted_scalar() {} + scalar scalar_; }; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/bindings/cpp/include/proton/ssl.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/ssl.hpp b/proton-c/bindings/cpp/include/proton/ssl.hpp index b3757dc..0518dc6 100644 --- a/proton-c/bindings/cpp/include/proton/ssl.hpp +++ b/proton-c/bindings/cpp/include/proton/ssl.hpp @@ -79,7 +79,7 @@ class ssl_domain { public: PN_CPP_EXTERN ssl_domain(const ssl_domain&); PN_CPP_EXTERN ssl_domain& operator=(const ssl_domain&); - ~ssl_domain(); + PN_CPP_EXTERN ~ssl_domain(); protected: ssl_domain(bool is_server); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 494f7db..8f502a3 100644 --- a/proton-c/bindings/cpp/include/proton/type_traits.hpp +++ b/proton-c/bindings/cpp/include/proton/type_traits.hpp @@ -76,7 +76,7 @@ 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 atomic C++ types +// Metafunction returning AMQP type for scalar 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_TYPE; }; template<> struct type_id_of<amqp_boolean> { static const type_id value=BOOLEAN; }; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 645d9da..db5989b 100644 --- a/proton-c/bindings/cpp/include/proton/types.hpp +++ b/proton-c/bindings/cpp/include/proton/types.hpp @@ -67,12 +67,26 @@ enum type_id { MAP=PN_MAP ///< A sequence of key:value pairs, may be of mixed types. }; +/// Name of the AMQP type +PN_CPP_EXTERN std::string type_name(type_id); + +/// Print the type_name +PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, type_id); + /// Raised when there is a type mismatch, with the expected and actual type ID. -struct type_mismatch : public error { - PN_CPP_EXTERN explicit type_mismatch(type_id want, type_id got, const std::string& =std::string()); +struct type_error : public decode_error { + PN_CPP_EXTERN explicit type_error(type_id want, type_id got, const std::string& =std::string()); type_id want; ///< Expected type_id type_id got; ///< Actual type_id }; + + +///@cond INTERNAL +/// Provide a full set of comparison operators for proton:: types that have < and ==. +template <class T> bool operator>(const T &a, const T &b) { return b < a; } +template <class T> bool operator<=(const T &a, const T &b) { return !(a > b); } +template <class T> bool operator>=(const T &a, const T &b) { return !(a < b); } +template <class T> bool operator!=(const T &a, const T &b) { return !(a == b); } PN_CPP_EXTERN pn_bytes_t pn_bytes(const std::string&); PN_CPP_EXTERN std::string str(const pn_bytes_t& b); @@ -107,26 +121,23 @@ typedef double amqp_double; /// AMQP UTF-8 encoded string. struct amqp_string : public std::string { - amqp_string(const std::string& s=std::string()) : std::string(s) {} - amqp_string(const char* s) : std::string(s) {} - amqp_string(const pn_bytes_t& b) : std::string(b.start, b.size) {} - operator pn_bytes_t() const { return pn_bytes(*this); } + explicit amqp_string(const std::string& s=std::string()) : std::string(s) {} + explicit amqp_string(const char* s) : std::string(s) {} + explicit amqp_string(const pn_bytes_t& b) : std::string(b.start, b.size) {} }; /// AMQP ASCII encoded symbolic name. struct amqp_symbol : public std::string { - amqp_symbol(const std::string& s=std::string()) : std::string(s) {} - amqp_symbol(const char* s) : std::string(s) {} - amqp_symbol(const pn_bytes_t& b) : std::string(b.start, b.size) {} - operator pn_bytes_t() const { return pn_bytes(*this); } + explicit amqp_symbol(const std::string& s=std::string()) : std::string(s) {} + explicit amqp_symbol(const char* s) : std::string(s) {} + explicit amqp_symbol(const pn_bytes_t& b) : std::string(b.start, b.size) {} }; /// AMQP variable-length binary data. struct amqp_binary : public std::string { - amqp_binary(const std::string& s=std::string()) : std::string(s) {} - amqp_binary(const char* s) : std::string(s) {} - amqp_binary(const pn_bytes_t& b) : std::string(b.start, b.size) {} - operator pn_bytes_t() const { return pn_bytes(*this); } + explicit amqp_binary(const std::string& s=std::string()) : std::string(s) {} + explicit amqp_binary(const char* s) : std::string(s) {} + explicit amqp_binary(const pn_bytes_t& b) : std::string(b.start, b.size) {} }; /// Template for opaque proton proton types that can be treated as byte arrays. @@ -170,40 +181,28 @@ struct amqp_timestamp : public comparable<amqp_timestamp> { }; PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const amqp_timestamp&); -///@cond INTERNAL -template<class T, type_id A> struct cref { - typedef T cpp_type; - static const type_id type; - - cref(const T& v) : value(v) {} - const T& value; -}; -template <class T, type_id A> const type_id cref<T, A>::type = A; -///@endcond INTERNAL - -/** - * 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<LIST>(v); - */ -template <type_id A, class T> cref<T, A> as(const T& value) { return cref<T, A>(value); } - // TODO aconway 2015-06-16: described types. -/// Name of the AMQP type -PN_CPP_EXTERN std::string type_name(type_id); - ///@name Attributes of a type_id value, returns same result as the /// corresponding std::type_traits tests for the corresponding C++ types. ///@{ +/// Any scalar type PN_CPP_EXTERN bool type_id_is_scalar(type_id); +/// One of the signed integer types: BYTE, SHORT, INT or LONG +PN_CPP_EXTERN bool type_id_is_signed_int(type_id); +/// One of the unsigned integer types: UBYTE, USHORT, UINT or ULONG +PN_CPP_EXTERN bool type_id_is_unsigned_int(type_id); +/// Any of the signed or unsigned integers, BOOL, CHAR or TIMESTAMP. PN_CPP_EXTERN bool type_id_is_integral(type_id); -PN_CPP_EXTERN bool type_id_is_signed(type_id); ///< CHAR and BOOL are not signed. +/// A floating point type, float or double PN_CPP_EXTERN bool type_id_is_floating_point(type_id); +/// Any signed integer, float or double. BOOL, CHAR and TIMESTAMP are not signed. +PN_CPP_EXTERN bool type_id_is_signed(type_id); +/// Any DECIMAL type. PN_CPP_EXTERN bool type_id_is_decimal(type_id); -PN_CPP_EXTERN bool type_id_is_string_like(type_id); ///< STRING, SYMBOL, BINARY +/// STRING, SYMBOL or BINARY +PN_CPP_EXTERN bool type_id_is_string_like(type_id); +/// Container types: MAP, LIST, ARRAY or DESCRIBED. PN_CPP_EXTERN bool type_id_is_container(type_id); ///@} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 c33eea1..dedcadd 100644 --- a/proton-c/bindings/cpp/include/proton/value.hpp +++ b/proton-c/bindings/cpp/include/proton/value.hpp @@ -43,9 +43,10 @@ class decoder; class value : public comparable<value> { public: PN_CPP_EXTERN value(); - PN_CPP_EXTERN value(const value& x); - // TODO: Should enumerate specifically all the pointer types that can convert to value - // to avoid accidental conversions to bool this will require enable_if<> or the like + PN_CPP_EXTERN value(const value&); +#if PN_HAS_CPP11 + PN_CPP_EXTERN value(value&&); +#endif template <class T> value(const T& x) : data_(data::create()) { data_.copy(x); } PN_CPP_EXTERN value& operator=(const value& x); @@ -65,18 +66,27 @@ class value : public comparable<value> { /** Get the value. */ template<class T> void get(T &t) const { decoder() >> t; } + template<class T> void get(map_ref<T> t) const { decoder() >> t; } + template<class T> void get(pairs_ref<T> t) const { decoder() >> t; } + template<class T> void get(sequence_ref<T> t) const { decoder() >> t; } /** Get the value. */ template<class T> T get() const { T t; get(t); return t; } + // FIXME aconway 2015-12-28: friend ops. PN_CPP_EXTERN bool operator==(const value& x) const; PN_CPP_EXTERN bool operator<(const value& x) const; + PN_CPP_EXTERN void swap(value& v); + friend PN_CPP_EXTERN class encoder operator<<(class encoder e, const value& dv); friend PN_CPP_EXTERN class decoder operator>>(class decoder d, value& dv); friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream& o, const value& dv); private: + value(data d); + value& ref(data d); + data data_; friend class message; }; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 20d0461..4639683 100644 --- a/proton-c/bindings/cpp/src/decoder.cpp +++ b/proton-c/bindings/cpp/src/decoder.cpp @@ -21,6 +21,7 @@ #include "proton/decoder.hpp" #include "proton/value.hpp" #include "proton/message_id.hpp" +#include "proton/annotation_key.hpp" #include "proton_bits.hpp" #include "msg.hpp" @@ -34,8 +35,6 @@ namespace proton { * to be returned by the decoder. * */ -decode_error::decode_error(const std::string& msg) : error("decode: "+msg) {} - namespace { struct save_state { pn_data_t* data; @@ -87,8 +86,7 @@ data decoder::data() { return proton::data(pn_object()); } namespace { void bad_type(type_id want, type_id got) { - if (want != got) - throw decode_error("expected "+type_name(want)+" found "+type_name(got)); + if (want != got) throw type_error(want, got); } type_id pre_get(pn_data_t* data) { @@ -99,10 +97,10 @@ type_id pre_get(pn_data_t* data) { } // Simple extract with no type conversion. -template <class T, class U> void extract(pn_data_t* data, T& value, U (*get)(pn_data_t*)) { +template <class T, class U> void extract(pn_data_t* data, T& x, U (*get)(pn_data_t*)) { save_state ss(data); bad_type(type_id_of<T>::value, pre_get(data)); - value = T(get(data)); + x = T(get(data)); ss.cancel(); // No error, no rewind } @@ -165,191 +163,211 @@ decoder operator>>(decoder d, value& v) { return d; } -decoder operator>>(decoder d, message_id& id) { +decoder operator>>(decoder d, message_id& x) { switch (d.type()) { case ULONG: case UUID: case BINARY: case STRING: - return d >> id.value_; + return d >> x.scalar_; default: throw decode_error("expected one of ulong, uuid, binary or string but found " + type_name(d.type())); }; } +decoder operator>>(decoder d, annotation_key& x) { + switch (d.type()) { + case ULONG: + case SYMBOL: + return d >> x.scalar_; + default: + throw decode_error("expected one of ulong or symbol but found " + type_name(d.type())); + }; +} + decoder operator>>(decoder d, amqp_null) { save_state ss(d.pn_object()); bad_type(NULL_TYPE, pre_get(d.pn_object())); return d; } -decoder operator>>(decoder d, amqp_boolean& value) { - extract(d.pn_object(), value, pn_data_get_bool); +decoder operator>>(decoder d, scalar& x) { + save_state ss(d.pn_object()); + type_id got = pre_get(d.pn_object()); + if (!type_id_is_scalar(got)) + throw decode_error("expected scalar, found "+type_name(got)); + x.set(pn_data_get_atom(d.pn_object())); + ss.cancel(); // No error, no rewind + return d; +} + +decoder operator>>(decoder d, amqp_boolean &x) { + extract(d.pn_object(), x, pn_data_get_bool); return d; } -decoder operator>>(decoder d0, amqp_ubyte& value) { +decoder operator>>(decoder d0, amqp_ubyte &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case UBYTE: value = pn_data_get_ubyte(d); break; + case UBYTE: x = pn_data_get_ubyte(d); break; default: bad_type(UBYTE, type_id(type_id(pn_data_type(d)))); } ss.cancel(); return d0; } -decoder operator>>(decoder d0, amqp_byte& value) { +decoder operator>>(decoder d0, amqp_byte &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case BYTE: value = pn_data_get_byte(d); break; + case BYTE: x = pn_data_get_byte(d); break; default: bad_type(BYTE, type_id(type_id(pn_data_type(d)))); } ss.cancel(); return d0; } -decoder operator>>(decoder d0, amqp_ushort& value) { +decoder operator>>(decoder d0, amqp_ushort &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case UBYTE: value = pn_data_get_ubyte(d); break; - case USHORT: value = pn_data_get_ushort(d); break; + case UBYTE: x = pn_data_get_ubyte(d); break; + case USHORT: x = pn_data_get_ushort(d); break; default: bad_type(USHORT, type_id(type_id(pn_data_type(d)))); } ss.cancel(); return d0; } -decoder operator>>(decoder d0, amqp_short& value) { +decoder operator>>(decoder d0, amqp_short &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case BYTE: value = pn_data_get_byte(d); break; - case SHORT: value = pn_data_get_short(d); break; + case BYTE: x = pn_data_get_byte(d); break; + case SHORT: x = pn_data_get_short(d); break; default: bad_type(SHORT, type_id(pn_data_type(d))); } ss.cancel(); return d0; } -decoder operator>>(decoder d0, amqp_uint& value) { +decoder operator>>(decoder d0, amqp_uint &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case UBYTE: value = pn_data_get_ubyte(d); break; - case USHORT: value = pn_data_get_ushort(d); break; - case UINT: value = pn_data_get_uint(d); break; + case UBYTE: x = pn_data_get_ubyte(d); break; + case USHORT: x = pn_data_get_ushort(d); break; + case UINT: x = pn_data_get_uint(d); break; default: bad_type(UINT, type_id(pn_data_type(d))); } ss.cancel(); return d0; } -decoder operator>>(decoder d0, amqp_int& value) { +decoder operator>>(decoder d0, amqp_int &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case BYTE: value = pn_data_get_byte(d); break; - case SHORT: value = pn_data_get_short(d); break; - case INT: value = pn_data_get_int(d); break; + case BYTE: x = pn_data_get_byte(d); break; + case SHORT: x = pn_data_get_short(d); break; + case INT: x = pn_data_get_int(d); break; default: bad_type(INT, type_id(pn_data_type(d))); } ss.cancel(); return d0; } -decoder operator>>(decoder d0, amqp_ulong& value) { +decoder operator>>(decoder d0, amqp_ulong &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case UBYTE: value = pn_data_get_ubyte(d); break; - case USHORT: value = pn_data_get_ushort(d); break; - case UINT: value = pn_data_get_uint(d); break; - case ULONG: value = pn_data_get_ulong(d); break; + case UBYTE: x = pn_data_get_ubyte(d); break; + case USHORT: x = pn_data_get_ushort(d); break; + case UINT: x = pn_data_get_uint(d); break; + case ULONG: x = pn_data_get_ulong(d); break; default: bad_type(ULONG, type_id(pn_data_type(d))); } ss.cancel(); return d0; } -decoder operator>>(decoder d0, amqp_long& value) { +decoder operator>>(decoder d0, amqp_long &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case BYTE: value = pn_data_get_byte(d); break; - case SHORT: value = pn_data_get_short(d); break; - case INT: value = pn_data_get_int(d); break; - case LONG: value = pn_data_get_long(d); break; + case BYTE: x = pn_data_get_byte(d); break; + case SHORT: x = pn_data_get_short(d); break; + case INT: x = pn_data_get_int(d); break; + case LONG: x = pn_data_get_long(d); break; default: bad_type(LONG, type_id(pn_data_type(d))); } ss.cancel(); return d0; } -decoder operator>>(decoder d, amqp_char& value) { - extract(d.pn_object(), value, pn_data_get_char); +decoder operator>>(decoder d, amqp_char &x) { + extract(d.pn_object(), x, pn_data_get_char); return d; } -decoder operator>>(decoder d, amqp_timestamp& value) { - extract(d.pn_object(), value, pn_data_get_timestamp); +decoder operator>>(decoder d, amqp_timestamp &x) { + extract(d.pn_object(), x, pn_data_get_timestamp); return d; } -decoder operator>>(decoder d0, amqp_float& value) { +decoder operator>>(decoder d0, amqp_float &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case FLOAT: value = pn_data_get_float(d); break; - case DOUBLE: value = float(pn_data_get_double(d)); break; + case FLOAT: x = pn_data_get_float(d); break; + case DOUBLE: x = float(pn_data_get_double(d)); break; default: bad_type(FLOAT, type_id(pn_data_type(d))); } ss.cancel(); return d0; } -decoder operator>>(decoder d0, amqp_double& value) { +decoder operator>>(decoder d0, amqp_double &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case FLOAT: value = pn_data_get_float(d); break; - case DOUBLE: value = pn_data_get_double(d); break; + case FLOAT: x = pn_data_get_float(d); break; + case DOUBLE: x = pn_data_get_double(d); break; default: bad_type(DOUBLE, type_id(pn_data_type(d))); } ss.cancel(); return d0; } -decoder operator>>(decoder d, amqp_decimal32& value) { - extract(d.pn_object(), value, pn_data_get_decimal32); +decoder operator>>(decoder d, amqp_decimal32 &x) { + extract(d.pn_object(), x, pn_data_get_decimal32); return d; } -decoder operator>>(decoder d, amqp_decimal64& value) { - extract(d.pn_object(), value, pn_data_get_decimal64); +decoder operator>>(decoder d, amqp_decimal64 &x) { + extract(d.pn_object(), x, pn_data_get_decimal64); return d; } -decoder operator>>(decoder d, amqp_decimal128& value) { - extract(d.pn_object(), value, pn_data_get_decimal128); +decoder operator>>(decoder d, amqp_decimal128 &x) { + extract(d.pn_object(), x, pn_data_get_decimal128); return d; } -decoder operator>>(decoder d, amqp_uuid& value) { - extract(d.pn_object(), value, pn_data_get_uuid); +decoder operator>>(decoder d, amqp_uuid &x) { + extract(d.pn_object(), x, pn_data_get_uuid); return d; } -decoder operator>>(decoder d0, std::string& value) { +decoder operator>>(decoder d0, std::string &x) { pn_data_t* d = d0.pn_object(); save_state ss(d); switch (pre_get(d)) { - case STRING: value = str(pn_data_get_string(d)); break; - case BINARY: value = str(pn_data_get_binary(d)); break; - case SYMBOL: value = str(pn_data_get_symbol(d)); break; + case STRING: x = str(pn_data_get_string(d)); break; + case BINARY: x = str(pn_data_get_binary(d)); break; + case SYMBOL: x = str(pn_data_get_symbol(d)); break; default: bad_type(STRING, type_id(pn_data_type(d))); } ss.cancel(); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/bindings/cpp/src/encode_decode_test.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/encode_decode_test.cpp b/proton-c/bindings/cpp/src/encode_decode_test.cpp index 3e15a58..8c285ac 100644 --- a/proton-c/bindings/cpp/src/encode_decode_test.cpp +++ b/proton-c/bindings/cpp/src/encode_decode_test.cpp @@ -48,31 +48,13 @@ template <class T> void value_test(T x, type_id tid, const std::string& s, T y) ASSERT(y > x); } -void value_tests() { - value_test(false, BOOLEAN, "false", true); - value_test(amqp_ubyte(42), UBYTE, "42", amqp_ubyte(50)); - value_test(amqp_byte(-42), BYTE, "-42", amqp_byte(-40)); - value_test(amqp_ushort(4242), USHORT, "4242", amqp_ushort(5252)); - value_test(amqp_short(-4242), SHORT, "-4242", amqp_short(3)); - value_test(amqp_uint(4242), UINT, "4242", amqp_uint(5252)); - value_test(amqp_int(-4242), INT, "-4242", amqp_int(3)); - value_test(amqp_ulong(4242), ULONG, "4242", amqp_ulong(5252)); - value_test(amqp_long(-4242), LONG, "-4242", amqp_long(3)); - value_test(amqp_float(1.234), FLOAT, "1.234", amqp_float(2.345)); - value_test(amqp_double(11.2233), DOUBLE, "11.2233", amqp_double(12)); - value_test(amqp_string("aaa"), STRING, "aaa", amqp_string("aaaa")); - value_test(std::string("xxx"), STRING, "xxx", std::string("yyy")); - value_test(amqp_symbol("aaa"), SYMBOL, "aaa", amqp_symbol("aaaa")); - value_test(amqp_binary("aaa"), BINARY, "b\"aaa\"", amqp_binary("aaaa")); -} - // Map values void map_test() { std::map<string, int> m; m["a"] = 1; m["b"] = 2; m["c"] = 3; - proton::value v = m; + value v = m; ASSERT_EQUAL("{\"a\"=1, \"b\"=2, \"c\"=3}", str(v)); std::map<value, value> mv; v.get(mv); @@ -81,8 +63,9 @@ void map_test() { mv.erase("c"); v = value(mv); ASSERT_EQUAL("{\"a\"=1, \"b\"=b\"xyz\"}", str(v)); + std::vector<std::pair<string, value> > vec; - v.decoder() >> to_pairs(vec); // FIXME aconway 2015-11-17: get doesn't work with pairs. + v.get(to_pairs(vec)); ASSERT_EQUAL(2, vec.size()); ASSERT_EQUAL(std::make_pair(std::string("a"), value(1)), vec[0]); ASSERT_EQUAL(std::make_pair(std::string("b"), value(amqp_binary("xyz"))), vec[1]); @@ -90,7 +73,21 @@ void map_test() { int main(int, char**) { int failed = 0; + RUN_TEST(failed, value_test(false, BOOLEAN, "false", true)); + RUN_TEST(failed, value_test(amqp_ubyte(42), UBYTE, "42", amqp_ubyte(50))); + RUN_TEST(failed, value_test(amqp_byte(-42), BYTE, "-42", amqp_byte(-40))); + RUN_TEST(failed, value_test(amqp_ushort(4242), USHORT, "4242", amqp_ushort(5252))); + RUN_TEST(failed, value_test(amqp_short(-4242), SHORT, "-4242", amqp_short(3))); + RUN_TEST(failed, value_test(amqp_uint(4242), UINT, "4242", amqp_uint(5252))); + RUN_TEST(failed, value_test(amqp_int(-4242), INT, "-4242", amqp_int(3))); + RUN_TEST(failed, value_test(amqp_ulong(4242), ULONG, "4242", amqp_ulong(5252))); + RUN_TEST(failed, value_test(amqp_long(-4242), LONG, "-4242", amqp_long(3))); + RUN_TEST(failed, value_test(amqp_float(1.234), FLOAT, "1.234", amqp_float(2.345))); + RUN_TEST(failed, value_test(amqp_double(11.2233), DOUBLE, "11.2233", amqp_double(12))); + RUN_TEST(failed, value_test(amqp_string("aaa"), STRING, "aaa", amqp_string("aaaa"))); + RUN_TEST(failed, value_test(std::string("xxx"), STRING, "xxx", std::string("yyy"))); + RUN_TEST(failed, value_test(amqp_symbol("aaa"), SYMBOL, "aaa", amqp_symbol("aaaa"))); + RUN_TEST(failed, value_test(amqp_binary("aaa"), BINARY, "b\"aaa\"", amqp_binary("aaaa"))); RUN_TEST(failed, map_test()); - RUN_TEST(failed, value_tests()); return failed; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 327426a..109b290 100644 --- a/proton-c/bindings/cpp/src/encoder.cpp +++ b/proton-c/bindings/cpp/src/encoder.cpp @@ -20,6 +20,7 @@ #include "proton/data.hpp" #include "proton/encoder.hpp" #include "proton/message_id.hpp" +#include "proton/annotation_key.hpp" #include "proton/value.hpp" #include "proton_bits.hpp" @@ -100,35 +101,39 @@ encoder operator<<(encoder e, finish) { namespace { template <class T, class U> -encoder insert(encoder e, pn_data_t* data, T& value, int (*put)(pn_data_t*, U)) { +encoder insert(encoder e, pn_data_t* data, T& x, int (*put)(pn_data_t*, U)) { save_state ss(data); // Save state in case of error. - check(put(data, value), data); + check(put(data, x), data); ss.cancel(); // Don't restore state, all is good. return e; } + +int pn_data_put_amqp_string(pn_data_t *d, const amqp_string& x) { return pn_data_put_string(d, pn_bytes(x)); } +int pn_data_put_amqp_binary(pn_data_t *d, const amqp_binary& x) { return pn_data_put_binary(d, pn_bytes(x)); } +int pn_data_put_amqp_symbol(pn_data_t *d, const amqp_symbol& x) { return pn_data_put_symbol(d, pn_bytes(x)); } } encoder operator<<(encoder e, amqp_null) { pn_data_put_null(e.pn_object()); return e; } -encoder operator<<(encoder e, amqp_boolean value) { return insert(e, e.pn_object(), value, pn_data_put_bool); } -encoder operator<<(encoder e, amqp_ubyte value) { return insert(e, e.pn_object(), value, pn_data_put_ubyte); } -encoder operator<<(encoder e, amqp_byte value) { return insert(e, e.pn_object(), value, pn_data_put_byte); } -encoder operator<<(encoder e, amqp_ushort value) { return insert(e, e.pn_object(), value, pn_data_put_ushort); } -encoder operator<<(encoder e, amqp_short value) { return insert(e, e.pn_object(), value, pn_data_put_short); } -encoder operator<<(encoder e, amqp_uint value) { return insert(e, e.pn_object(), value, pn_data_put_uint); } -encoder operator<<(encoder e, amqp_int value) { return insert(e, e.pn_object(), value, pn_data_put_int); } -encoder operator<<(encoder e, amqp_char value) { return insert(e, e.pn_object(), value, pn_data_put_char); } -encoder operator<<(encoder e, amqp_ulong value) { return insert(e, e.pn_object(), value, pn_data_put_ulong); } -encoder operator<<(encoder e, amqp_long value) { return insert(e, e.pn_object(), value, pn_data_put_long); } -encoder operator<<(encoder e, amqp_timestamp value) { return insert(e, e.pn_object(), value, pn_data_put_timestamp); } -encoder operator<<(encoder e, amqp_float value) { return insert(e, e.pn_object(), value, pn_data_put_float); } -encoder operator<<(encoder e, amqp_double value) { return insert(e, e.pn_object(), value, pn_data_put_double); } -encoder operator<<(encoder e, amqp_decimal32 value) { return insert(e, e.pn_object(), value, pn_data_put_decimal32); } -encoder operator<<(encoder e, amqp_decimal64 value) { return insert(e, e.pn_object(), value, pn_data_put_decimal64); } -encoder operator<<(encoder e, amqp_decimal128 value) { return insert(e, e.pn_object(), value, pn_data_put_decimal128); } -encoder operator<<(encoder e, amqp_uuid value) { return insert(e, e.pn_object(), value, pn_data_put_uuid); } -encoder operator<<(encoder e, amqp_string value) { return insert(e, e.pn_object(), value, pn_data_put_string); } -encoder operator<<(encoder e, amqp_symbol value) { return insert(e, e.pn_object(), value, pn_data_put_symbol); } -encoder operator<<(encoder e, amqp_binary value) { return insert(e, e.pn_object(), value, pn_data_put_binary); } +encoder operator<<(encoder e, amqp_boolean x) { return insert(e, e.pn_object(), x, pn_data_put_bool); } +encoder operator<<(encoder e, amqp_ubyte x) { return insert(e, e.pn_object(), x, pn_data_put_ubyte); } +encoder operator<<(encoder e, amqp_byte x) { return insert(e, e.pn_object(), x, pn_data_put_byte); } +encoder operator<<(encoder e, amqp_ushort x) { return insert(e, e.pn_object(), x, pn_data_put_ushort); } +encoder operator<<(encoder e, amqp_short x) { return insert(e, e.pn_object(), x, pn_data_put_short); } +encoder operator<<(encoder e, amqp_uint x) { return insert(e, e.pn_object(), x, pn_data_put_uint); } +encoder operator<<(encoder e, amqp_int x) { return insert(e, e.pn_object(), x, pn_data_put_int); } +encoder operator<<(encoder e, amqp_char x) { return insert(e, e.pn_object(), x, pn_data_put_char); } +encoder operator<<(encoder e, amqp_ulong x) { return insert(e, e.pn_object(), x, pn_data_put_ulong); } +encoder operator<<(encoder e, amqp_long x) { return insert(e, e.pn_object(), x, pn_data_put_long); } +encoder operator<<(encoder e, amqp_timestamp x) { return insert(e, e.pn_object(), x, pn_data_put_timestamp); } +encoder operator<<(encoder e, amqp_float x) { return insert(e, e.pn_object(), x, pn_data_put_float); } +encoder operator<<(encoder e, amqp_double x) { return insert(e, e.pn_object(), x, pn_data_put_double); } +encoder operator<<(encoder e, amqp_decimal32 x) { return insert(e, e.pn_object(), x, pn_data_put_decimal32); } +encoder operator<<(encoder e, amqp_decimal64 x) { return insert(e, e.pn_object(), x, pn_data_put_decimal64); } +encoder operator<<(encoder e, amqp_decimal128 x) { return insert(e, e.pn_object(), x, pn_data_put_decimal128); } +encoder operator<<(encoder e, amqp_uuid x) { return insert(e, e.pn_object(), x, pn_data_put_uuid); } +encoder operator<<(encoder e, amqp_string x) { return insert(e, e.pn_object(), x, pn_data_put_amqp_string); } +encoder operator<<(encoder e, amqp_symbol x) { return insert(e, e.pn_object(), x, pn_data_put_amqp_symbol); } +encoder operator<<(encoder e, amqp_binary x) { return insert(e, e.pn_object(), x, pn_data_put_amqp_binary); } encoder operator<<(encoder e, const value& v) { data edata = e.data(); @@ -138,6 +143,11 @@ encoder operator<<(encoder e, const value& v) { return e; } -encoder operator<<(encoder e, const message_id& v) { return e << v.value_; } +encoder operator<<(encoder e, const scalar& x) { + return insert(e, e.pn_object(), x.atom_, pn_data_put_atom); +} + +encoder operator<<(encoder e, const message_id& x) { return e << x.scalar_; } +encoder operator<<(encoder e, const annotation_key& x) { return e << x.scalar_; } } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/bindings/cpp/src/error.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/error.cpp b/proton-c/bindings/cpp/src/error.cpp index 980d6c0..8382a70 100644 --- a/proton-c/bindings/cpp/src/error.cpp +++ b/proton-c/bindings/cpp/src/error.cpp @@ -27,4 +27,6 @@ error::error(const std::string& msg) : std::runtime_error(prefix+msg) {} timeout_error::timeout_error(const std::string& msg) : error(msg) {} +decode_error::decode_error(const std::string& msg) : error("decode: "+msg) {} + } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/bindings/cpp/src/event.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/event.cpp b/proton-c/bindings/cpp/src/event.cpp index 2a35ee8..b3e93d8 100644 --- a/proton-c/bindings/cpp/src/event.cpp +++ b/proton-c/bindings/cpp/src/event.cpp @@ -77,5 +77,5 @@ class message &event::message() const { throw error(MSG("No message associated with event")); } -event_loop::~event_loop() {} // FIXME aconway 2015-12-28: move +event_loop::~event_loop() {} } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/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 c2fcd00..c7ee1f6 100644 --- a/proton-c/bindings/cpp/src/message.cpp +++ b/proton-c/bindings/cpp/src/message.cpp @@ -19,6 +19,7 @@ * */ +#include "proton/data.hpp" #include "proton/message.hpp" #include "proton/error.hpp" #include "proton/link.hpp" @@ -31,24 +32,39 @@ #include "msg.hpp" #include "proton_bits.hpp" -#include <cstring> +#include <string> #include <assert.h> namespace proton { -message::message() : message_(::pn_message()) {} +// impl exists so body is pre-constructed in reference mode (prevent it creating its own data) +// for all message ctors. +message::impl::impl() : msg(0), body(0) {} -message::message(const message &m) : message_(::pn_message()) { *this = m; } +message::impl::~impl() { + if (msg) { + body.ref(0); // Clear the reference. + pn_message_free(msg); + } +} -#if defined(PN_HAS_CPP11) && PN_HAS_CPP11 -message::message(message &&m) : message_(::pn_message()) { swap(m); } -#endif +// Lazy construct the message. +pn_message_t* message::pn_msg() const { + if (!impl_.msg) impl_.msg = pn_message(); + return impl_.msg; +} + +message::message() {} + +message::message(const message &m) { *this = m; } -message::message(const value& v) : message_(::pn_message()) { body(v); } +#if PN_HAS_CPP11 +message::message(message &&m) { swap(m); } +#endif -message::~message() { ::pn_message_free(message_); } +message::~message() {} -void message::swap(message& m) { std::swap(message_, m.message_); } +void message::swap(message& m) { std::swap(impl_.msg, m.impl_.msg); } message& message::operator=(const message& m) { // TODO aconway 2015-08-10: more efficient pn_message_copy function @@ -58,7 +74,7 @@ message& message::operator=(const message& m) { return *this; } -void message::clear() { pn_message_clear(message_); } +void message::clear() { if (impl_.msg) pn_message_clear(impl_.msg); } namespace { void check(int err) { @@ -66,7 +82,7 @@ void check(int err) { } } // namespace -void message::id(const message_id& id) { data(pn_message_id(message_)).copy(id.value_); } +void message::id(const message_id& id) { pn_message_set_id(pn_msg(), id.scalar_.atom_); } namespace { inline message_id from_pn_atom(const pn_atom_t& v) { @@ -86,222 +102,169 @@ inline message_id from_pn_atom(const pn_atom_t& v) { } message_id message::id() const { - return from_pn_atom(pn_message_get_id(message_)); + return from_pn_atom(pn_message_get_id(pn_msg())); } void message::user_id(const std::string &id) { - check(pn_message_set_user_id(message_, pn_bytes(id))); + check(pn_message_set_user_id(pn_msg(), pn_bytes(id))); } std::string message::user_id() const { - return str(pn_message_get_user_id(message_)); + return str(pn_message_get_user_id(pn_msg())); } void message::address(const std::string &addr) { - check(pn_message_set_address(message_, addr.c_str())); + check(pn_message_set_address(pn_msg(), addr.c_str())); } std::string message::address() const { - const char* addr = pn_message_get_address(message_); + const char* addr = pn_message_get_address(pn_msg()); return addr ? std::string(addr) : std::string(); } void message::subject(const std::string &s) { - check(pn_message_set_subject(message_, s.c_str())); + check(pn_message_set_subject(pn_msg(), s.c_str())); } std::string message::subject() const { - const char* s = pn_message_get_subject(message_); + const char* s = pn_message_get_subject(pn_msg()); return s ? std::string(s) : std::string(); } void message::reply_to(const std::string &s) { - check(pn_message_set_reply_to(message_, s.c_str())); + check(pn_message_set_reply_to(pn_msg(), s.c_str())); } std::string message::reply_to() const { - const char* s = pn_message_get_reply_to(message_); + const char* s = pn_message_get_reply_to(pn_msg()); return s ? std::string(s) : std::string(); } void message::correlation_id(const message_id& id) { - data(pn_message_correlation_id(message_)).copy(id.value_); + data(pn_message_correlation_id(pn_msg())).copy(id.scalar_); } message_id message::correlation_id() const { - return from_pn_atom(pn_message_get_correlation_id(message_)); + return from_pn_atom(pn_message_get_correlation_id(pn_msg())); } void message::content_type(const std::string &s) { - check(pn_message_set_content_type(message_, s.c_str())); + check(pn_message_set_content_type(pn_msg(), s.c_str())); } std::string message::content_type() const { - const char* s = pn_message_get_content_type(message_); + const char* s = pn_message_get_content_type(pn_msg()); return s ? std::string(s) : std::string(); } void message::content_encoding(const std::string &s) { - check(pn_message_set_content_encoding(message_, s.c_str())); + check(pn_message_set_content_encoding(pn_msg(), s.c_str())); } std::string message::content_encoding() const { - const char* s = pn_message_get_content_encoding(message_); + const char* s = pn_message_get_content_encoding(pn_msg()); return s ? std::string(s) : std::string(); } void message::expiry_time(amqp_timestamp t) { - pn_message_set_expiry_time(message_, t.milliseconds); + pn_message_set_expiry_time(pn_msg(), t.milliseconds); } amqp_timestamp message::expiry_time() const { - return amqp_timestamp(pn_message_get_expiry_time(message_)); + return amqp_timestamp(pn_message_get_expiry_time(pn_msg())); } void message::creation_time(amqp_timestamp t) { - pn_message_set_creation_time(message_, t); + pn_message_set_creation_time(pn_msg(), t); } amqp_timestamp message::creation_time() const { - return pn_message_get_creation_time(message_); + return pn_message_get_creation_time(pn_msg()); } void message::group_id(const std::string &s) { - check(pn_message_set_group_id(message_, s.c_str())); + check(pn_message_set_group_id(pn_msg(), s.c_str())); } std::string message::group_id() const { - const char* s = pn_message_get_group_id(message_); + const char* s = pn_message_get_group_id(pn_msg()); return s ? std::string(s) : std::string(); } void message::reply_to_group_id(const std::string &s) { - check(pn_message_set_reply_to_group_id(message_, s.c_str())); + check(pn_message_set_reply_to_group_id(pn_msg(), s.c_str())); } std::string message::reply_to_group_id() const { - const char* s = pn_message_get_reply_to_group_id(message_); + const char* s = pn_message_get_reply_to_group_id(pn_msg()); return s ? std::string(s) : std::string(); } -bool message::inferred() const { return pn_message_is_inferred(message_); } - -void message::inferred(bool b) { pn_message_set_inferred(message_, b); } - -void message::body(const value& v) { body().copy(v); } +bool message::inferred() const { return pn_message_is_inferred(pn_msg()); } -const data message::body() const { - return pn_message_body(message_); -} +void message::inferred(bool b) { pn_message_set_inferred(pn_msg(), b); } -data message::body() { - return pn_message_body(message_); -} +const value& message::body() const { return impl_.body.ref(pn_message_body(pn_msg())); } +value& message::body() { return impl_.body.ref(pn_message_body(pn_msg())); } -void message::properties(const value& v) { - properties().copy(v); -} +// MAP CACHING: the properties, annotations and instructions maps can either be +// encoded in the pn_message pn_data_t structures OR decoded as C++ map members +// of the message but not both. At least one of the pn_data_t or the map member +// is always empty, the non-empty one is the authority. -const data message::properties() const { - return pn_message_properties(message_); -} - -data message::properties() { - return pn_message_properties(message_); -} - -namespace { -typedef std::map<std::string, value> props_map; -} - -void message::property(const std::string& name, const value &v) { - // TODO aconway 2015-11-17: not efficient but avoids cache consistency problems. - // Could avoid full encode/decode with linear scan of names. Need - // better codec suport for in-place modification of data. - props_map m; - if (!properties().empty()) - properties().get(m); - m[name] = v; - properties(m); -} - -value message::property(const std::string& name) const { - // TODO aconway 2015-11-17: not efficient but avoids cache consistency problems. - if (!properties().empty()) { - props_map m; - properties().get(m); - props_map::const_iterator i = m.find(name); - if (i != m.end()) - return i->second; +// Decode a map on demand +template<class M> M& get_map(pn_message_t* msg, pn_data_t* (*get)(pn_message_t*), M& map) { + data d(get(msg)); + if (map.empty() && !d.empty()) { + d.decoder() >> rewind() >> map; + d.clear(); // The map member is now the authority. } - return value(); + return map; } -bool message::erase_property(const std::string& name) { - // TODO aconway 2015-11-17: not efficient but avoids cache consistency problems. - if (!properties().empty()) { - props_map m; - properties().get(m); - if (m.erase(name)) { - properties(m); - return true; - } +// Encode a map if necessary. +template<class M> M& put_map(pn_message_t* msg, pn_data_t* (*get)(pn_message_t*), M& map) { + data d(get(msg)); + if (d.empty() && !map.empty()) { + d.encoder() << map; + map.clear(); // The encoded pn_data_t is now the authority. } - return false; + return map; } -void message::annotations(const value& v) { - annotations().copy(v); +message::property_map& message::properties() { + return get_map(pn_msg(), pn_message_properties, properties_); } -const data message::annotations() const { - return pn_message_annotations(message_); +const message::property_map& message::properties() const { + return get_map(pn_msg(), pn_message_properties, properties_); } -data message::annotations() { - return pn_message_annotations(message_); -} -namespace { -typedef std::map<proton::amqp_symbol, value> annotation_map; +message::annotation_map& message::annotations() { + return get_map(pn_msg(), pn_message_annotations, annotations_); } -void message::annotation(const proton::amqp_symbol &k, const value &v) { - annotation_map m; - if (!annotations().empty()) - annotations().get(m); - m[k] = v; - annotations(m); +const message::annotation_map& message::annotations() const { + return get_map(pn_msg(), pn_message_annotations, annotations_); } -value message::annotation(const proton::amqp_symbol &k) const { - if (!annotations().empty()) { - annotation_map m; - annotations().get(m); - annotation_map::const_iterator i = m.find(k); - if (i != m.end()) - return i->second; - } - return value(); +message::annotation_map& message::instructions() { + return get_map(pn_msg(), pn_message_instructions, instructions_); } -bool message::erase_annotation(const proton::amqp_symbol &k) { - if (!annotations().empty()) { - annotation_map m; - annotations().get(m); - if (m.erase(k)) { - annotations(m); - return true; - } - } - return false; +const message::annotation_map& message::instructions() const { + return get_map(pn_msg(), pn_message_instructions, instructions_); } void message::encode(std::string &s) const { + put_map(pn_msg(), pn_message_properties, properties_); + put_map(pn_msg(), pn_message_annotations, annotations_); + put_map(pn_msg(), pn_message_instructions, instructions_); size_t sz = s.capacity(); if (sz < 512) sz = 512; while (true) { s.resize(sz); - int err = pn_message_encode(message_, const_cast<char*>(s.data()), &sz); + int err = pn_message_encode(pn_msg(), const_cast<char*>(s.data()), &sz); if (err) { if (err != PN_OVERFLOW) check(err); @@ -320,7 +283,10 @@ std::string message::encode() const { } void message::decode(const std::string &s) { - check(pn_message_decode(message_, s.data(), s.size())); + properties_.clear(); + annotations_.clear(); + instructions_.clear(); + check(pn_message_decode(pn_msg(), s.data(), s.size())); } void message::decode(proton::link link, proton::delivery delivery) { @@ -333,22 +299,22 @@ void message::decode(proton::link link, proton::delivery delivery) { link.advance(); } -bool message::durable() const { return pn_message_is_durable(message_); } -void message::durable(bool b) { pn_message_set_durable(message_, b); } +bool message::durable() const { return pn_message_is_durable(pn_msg()); } +void message::durable(bool b) { pn_message_set_durable(pn_msg(), b); } -duration message::ttl() const { return duration(pn_message_get_ttl(message_)); } -void message::ttl(duration d) { pn_message_set_ttl(message_, d.milliseconds); } +duration message::ttl() const { return duration(pn_message_get_ttl(pn_msg())); } +void message::ttl(duration d) { pn_message_set_ttl(pn_msg(), d.milliseconds); } -uint8_t message::priority() const { return pn_message_get_priority(message_); } -void message::priority(uint8_t d) { pn_message_set_priority(message_, d); } +uint8_t message::priority() const { return pn_message_get_priority(pn_msg()); } +void message::priority(uint8_t d) { pn_message_set_priority(pn_msg(), d); } -bool message::first_acquirer() const { return pn_message_is_first_acquirer(message_); } -void message::first_acquirer(bool b) { pn_message_set_first_acquirer(message_, b); } +bool message::first_acquirer() const { return pn_message_is_first_acquirer(pn_msg()); } +void message::first_acquirer(bool b) { pn_message_set_first_acquirer(pn_msg(), b); } -uint32_t message::delivery_count() const { return pn_message_get_delivery_count(message_); } -void message::delivery_count(uint32_t d) { pn_message_set_delivery_count(message_, d); } +uint32_t message::delivery_count() const { return pn_message_get_delivery_count(pn_msg()); } +void message::delivery_count(uint32_t d) { pn_message_set_delivery_count(pn_msg(), d); } -int32_t message::sequence() const { return pn_message_get_group_sequence(message_); } -void message::sequence(int32_t d) { pn_message_set_group_sequence(message_, d); } +int32_t message::sequence() const { return pn_message_get_group_sequence(pn_msg()); } +void message::sequence(int32_t d) { pn_message_set_group_sequence(pn_msg(), d); } } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/1622d0e6/proton-c/bindings/cpp/src/message_test.cpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/src/message_test.cpp b/proton-c/bindings/cpp/src/message_test.cpp index 54995c0..9f05d1c 100644 --- a/proton-c/bindings/cpp/src/message_test.cpp +++ b/proton-c/bindings/cpp/src/message_test.cpp @@ -18,6 +18,7 @@ */ #include "proton/message.hpp" +#include "proton/scalar.hpp" #include "test_bits.hpp" #include <string> #include <fstream> @@ -32,58 +33,99 @@ using namespace proton; m.ATTR(#ATTR); \ ASSERT_EQUAL(std::string(#ATTR), m.ATTR()) -#define CHECK_STR_VALUE(ATTR) \ +#define CHECK_MESSAGE_ID(ATTR) \ m.ATTR(#ATTR); \ - ASSERT_EQUAL(std::string(#ATTR), m.ATTR().get<std::string>()) + ASSERT_EQUAL(scalar(#ATTR), m.ATTR()) -void test_message() { +void test_message_properties() { message m("hello"); std::string s = m.body().get<std::string>(); ASSERT_EQUAL("hello", s); - CHECK_STR_VALUE(id); + CHECK_MESSAGE_ID(id); CHECK_STR(user_id); CHECK_STR(address); CHECK_STR(subject); CHECK_STR(reply_to); - CHECK_STR_VALUE(correlation_id); + CHECK_MESSAGE_ID(correlation_id); CHECK_STR(content_type); CHECK_STR(content_encoding); CHECK_STR(group_id); CHECK_STR(reply_to_group_id); - m.expiry_time(42); ASSERT_EQUAL(m.expiry_time().milliseconds, 42); m.creation_time(4242); ASSERT_EQUAL(m.creation_time().milliseconds, 4242); - m.property("foo", 12); - ASSERT_EQUAL(m.property("foo"), value(12)); - m.property("xxx", false); - ASSERT_EQUAL(m.property("xxx"), value(false)); - ASSERT(m.erase_property("xxx")); - ASSERT(m.property("xxx").empty()); - ASSERT(!m.erase_property("yyy")); - - std::map<std::string, proton::value> props; - m.properties().get(props); - ASSERT_EQUAL(props.size(), 1); - ASSERT_EQUAL(props["foo"], value(12)); - props["bar"] = amqp_symbol("xyz"); - props["foo"] = true; - m.properties(props); - ASSERT_EQUAL(m.property("foo"), value(true)); - ASSERT_EQUAL(m.property("bar"), value(amqp_symbol("xyz"))); - m.property("bar", amqp_binary("bar")); - std::map<std::string, proton::value> props2; - m.properties().get(props2); - ASSERT_EQUAL(2, props2.size()); - ASSERT_EQUAL(props2["foo"], value(true)); - ASSERT_EQUAL(props2["bar"], value(amqp_binary("bar"))); + message m2(m); + ASSERT_EQUAL("hello", m2.body().get<std::string>()); + ASSERT_EQUAL(message_id("id"), m2.id()); + ASSERT_EQUAL("user_id", m2.user_id()); + ASSERT_EQUAL("address", m2.address()); + ASSERT_EQUAL("subject", m2.subject()); + ASSERT_EQUAL("reply_to", m2.reply_to()); + ASSERT_EQUAL(message_id("correlation_id"), m2.correlation_id()); + ASSERT_EQUAL("content_type", m2.content_type()); + ASSERT_EQUAL("content_encoding", m2.content_encoding()); + ASSERT_EQUAL("group_id", m2.group_id()); + ASSERT_EQUAL("reply_to_group_id", m2.reply_to_group_id()); + ASSERT_EQUAL(42, m2.expiry_time().milliseconds); + ASSERT_EQUAL(4242, m.creation_time().milliseconds); + + m2 = m; + ASSERT_EQUAL("hello", m2.body().get<std::string>()); + ASSERT_EQUAL(message_id("id"), m2.id()); + ASSERT_EQUAL("user_id", m2.user_id()); + ASSERT_EQUAL("address", m2.address()); + ASSERT_EQUAL("subject", m2.subject()); + ASSERT_EQUAL("reply_to", m2.reply_to()); + ASSERT_EQUAL(message_id("correlation_id"), m2.correlation_id()); + ASSERT_EQUAL("content_type", m2.content_type()); + ASSERT_EQUAL("content_encoding", m2.content_encoding()); + ASSERT_EQUAL("group_id", m2.group_id()); + ASSERT_EQUAL("reply_to_group_id", m2.reply_to_group_id()); + ASSERT_EQUAL(42, m2.expiry_time().milliseconds); + ASSERT_EQUAL(4242, m.creation_time().milliseconds); +} + +void test_message_maps() { + message m; + + ASSERT(m.properties().empty()); + ASSERT(m.annotations().empty()); + ASSERT(m.instructions().empty()); + + m.properties()["foo"] = 12; + m.instructions()["bar"] = "xyz"; + m.annotations()[23] = "23"; + + ASSERT_EQUAL(m.properties()["foo"], scalar(12)); + ASSERT_EQUAL(m.instructions()["bar"], scalar("xyz")); + ASSERT_EQUAL(m.annotations()[23], scalar("23")); + + message m2(m); + message::annotation_map& amap = m2.instructions(); + + ASSERT_EQUAL(m2.properties()["foo"], scalar(12)); + ASSERT_EQUAL(m2.instructions()["bar"], scalar("xyz")); + ASSERT_EQUAL(m2.annotations()[23], scalar("23")); + + m.properties()["foo"] = "newfoo"; + m.instructions()[24] = 1000; + m.annotations().erase(23); + + m2 = m; + ASSERT_EQUAL(1, m2.properties().size()); + ASSERT_EQUAL(m2.properties()["foo"], scalar("newfoo")); + ASSERT_EQUAL(2, m2.instructions().size()); + ASSERT_EQUAL(m2.instructions()["bar"], scalar("xyz")); + ASSERT_EQUAL(m2.instructions()[24], scalar(1000)); + ASSERT(m2.annotations().empty()); } int main(int argc, char** argv) { int failed = 0; - RUN_TEST(failed, test_message()); + RUN_TEST(failed, test_message_properties()); + RUN_TEST(failed, test_message_maps()); return failed; } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
