NO-JIRA: c++ add support for application properties and the inferred flag. Added message::message(const value&) to construct a message with an initial body value Get rid of templated conversions, causing ambiguity.
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/89923cf5 Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/89923cf5 Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/89923cf5 Branch: refs/heads/go1 Commit: 89923cf5bbf016df5fd82b84b29f92ec943fb050 Parents: f4b3551 Author: Alan Conway <[email protected]> Authored: Mon Nov 16 15:06:40 2015 -0500 Committer: Alan Conway <[email protected]> Committed: Tue Nov 17 13:50:21 2015 -0500 ---------------------------------------------------------------------- examples/cpp/encode_decode.cpp | 9 +- examples/cpp/helloworld.cpp | 3 +- examples/cpp/helloworld_blocking.cpp | 3 +- examples/cpp/helloworld_direct.cpp | 3 +- proton-c/bindings/cpp/CMakeLists.txt | 1 + proton-c/bindings/cpp/include/proton/data.hpp | 8 +- .../bindings/cpp/include/proton/decoder.hpp | 5 +- .../bindings/cpp/include/proton/message.hpp | 46 +++++++++- .../bindings/cpp/include/proton/message_id.hpp | 6 +- .../cpp/include/proton/pn_unique_ptr.hpp | 6 +- proton-c/bindings/cpp/include/proton/value.hpp | 4 - proton-c/bindings/cpp/src/decoder.cpp | 6 +- proton-c/bindings/cpp/src/message.cpp | 58 +++++++++++++ proton-c/bindings/cpp/src/message_test.cpp | 89 ++++++++++++++++++++ 14 files changed, 221 insertions(+), 26 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/examples/cpp/encode_decode.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/encode_decode.cpp b/examples/cpp/encode_decode.cpp index 3d6fe48..1ed8b64 100644 --- a/examples/cpp/encode_decode.cpp +++ b/examples/cpp/encode_decode.cpp @@ -71,7 +71,8 @@ void uniform_containers() { // 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::list<int> a1; + v.get(a1); // Decode as a C++ std::list instead std::cout << a1 << std::endl; // You can specify that a container should be encoded as an AMQP list instead. @@ -111,7 +112,8 @@ void mixed_containers() { // By default, a sequence of proton::value is treated as an AMQP list. v = l; print(v); - std::vector<proton::value> l2 = v; + std::vector<proton::value> l2; + v.get(l2); std::cout << l2 << std::endl; std::map<proton::value, proton::value> m; @@ -119,7 +121,8 @@ void mixed_containers() { m[proton::value(4)] = proton::value("four"); v = m; print(v); - std::map<proton::value, proton::value> m2 = v; + std::map<proton::value, proton::value> m2; + v.get(m2); std::cout << m2 << std::endl; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/examples/cpp/helloworld.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/helloworld.cpp b/examples/cpp/helloworld.cpp index b1eed87..d6caa27 100644 --- a/examples/cpp/helloworld.cpp +++ b/examples/cpp/helloworld.cpp @@ -40,8 +40,7 @@ class hello_world : public proton::messaging_handler { } void on_sendable(proton::event &e) { - proton::message m; - m.body("Hello World!"); + proton::message m("Hello World!"); e.sender().send(m); e.sender().close(); } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/examples/cpp/helloworld_blocking.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/helloworld_blocking.cpp b/examples/cpp/helloworld_blocking.cpp index f054df2..81a56b9 100644 --- a/examples/cpp/helloworld_blocking.cpp +++ b/examples/cpp/helloworld_blocking.cpp @@ -34,8 +34,7 @@ int main(int argc, char **argv) { proton::blocking_receiver receiver(conn, url.path()); proton::blocking_sender sender(conn, url.path()); - proton::message m; - m.body("Hello World!"); + proton::message m("Hello World!"); sender.send(m); proton::duration timeout(30000); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/examples/cpp/helloworld_direct.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/helloworld_direct.cpp b/examples/cpp/helloworld_direct.cpp index b141f1b..d055fde 100644 --- a/examples/cpp/helloworld_direct.cpp +++ b/examples/cpp/helloworld_direct.cpp @@ -40,8 +40,7 @@ class hello_world_direct : public proton::messaging_handler { } void on_sendable(proton::event &e) { - proton::message m; - m.body("Hello World!"); + proton::message m("Hello World!"); e.sender().send(m); e.sender().close(); } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/proton-c/bindings/cpp/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/CMakeLists.txt b/proton-c/bindings/cpp/CMakeLists.txt index 3995c20..7660961 100644 --- a/proton-c/bindings/cpp/CMakeLists.txt +++ b/proton-c/bindings/cpp/CMakeLists.txt @@ -162,5 +162,6 @@ macro(add_cpp_test test) endmacro(add_cpp_test) add_cpp_test(interop_test ${CMAKE_SOURCE_DIR}/tests) +add_cpp_test(message_test) add_cpp_test(conversion_test) add_cpp_test(encode_decode_test) http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/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 21504a5..752765e 100644 --- a/proton-c/bindings/cpp/include/proton/data.hpp +++ b/proton-c/bindings/cpp/include/proton/data.hpp @@ -42,6 +42,7 @@ class data : public facade<pn_data_t, data, comparable<data> > { PN_CPP_EXTERN static pn_unique_ptr<data> create(); PN_CPP_EXTERN data& operator=(const data&); + template<class T> data& operator=(const T &t) { clear(); encoder() << t; decoder().rewind(); return *this; } @@ -58,13 +59,12 @@ class data : public facade<pn_data_t, data, comparable<data> > { /** Decoder to decode from this value */ PN_CPP_EXTERN class decoder& decoder(); - /** Type of the current value*/ + /** Rewind and return the type of the first value*/ PN_CPP_EXTERN type_id type() const; - /** Get the first value, don't move the decoder pointer. */ - template<class T> void get(T &t) const { decoder() >> t; decoder().backup(); } + /** Rewind and decode the first value */ + template<class T> void get(T &t) const { decoder().rewind(); decoder() >> t; } - /** Get the first value, don't move the decoder pointer. */ template<class T> T get() const { T t; get(t); return t; } PN_CPP_EXTERN bool operator==(const data& x) const; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/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 1258e5a..3da078c 100644 --- a/proton-c/bindings/cpp/include/proton/decoder.hpp +++ b/proton-c/bindings/cpp/include/proton/decoder.hpp @@ -173,6 +173,9 @@ class decoder : public facade<pn_data_t, decoder> { /** Rewind to the start of the data. */ PN_CPP_EXTERN void rewind(); + /** Skip one value */ + PN_CPP_EXTERN void skip(); + /** Back up by one value */ PN_CPP_EXTERN void backup(); @@ -250,7 +253,7 @@ class decoder : public facade<pn_data_t, decoder> { PN_CPP_EXTERN friend decoder& operator>>(decoder&, finish); /** Skip a value */ - PN_CPP_EXTERN friend decoder& operator>>(decoder&, skip); + PN_CPP_EXTERN friend decoder& operator>>(decoder&, struct skip); /** Throw an exception if decoder.type() != assert_type.type */ PN_CPP_EXTERN friend decoder& operator>>(decoder&, assert_type); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/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 76bbc19..f68873b 100644 --- a/proton-c/bindings/cpp/include/proton/message.hpp +++ b/proton-c/bindings/cpp/include/proton/message.hpp @@ -46,6 +46,8 @@ class message public: 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 @@ -57,7 +59,7 @@ class message /** Clear the message content and properties. */ PN_CPP_EXTERN void clear(); - ///@name Message properties + ///@name Standard AMQP properties ///@{ PN_CPP_EXTERN void id(const message_id& id); @@ -95,6 +97,7 @@ class message PN_CPP_EXTERN void reply_to_group_id(const std::string &s); PN_CPP_EXTERN std::string reply_to_group_id() const; + ///@} /** Set the body. */ @@ -106,6 +109,30 @@ class message /** Get a reference to the body data, can be modified in-place. */ PN_CPP_EXTERN data& 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&); + + /** 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; + + /** Get a reference to the application properties, can be modified in-place.*/ + PN_CPP_EXTERN data& properties(); + + /** 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); + /** Encode into a string, growing the string if necessary. */ PN_CPP_EXTERN void encode(std::string &bytes) const; @@ -118,6 +145,23 @@ class message /// Decode the message from link corresponding to delivery. PN_CPP_EXTERN void decode(proton::link&, proton::delivery&); + /** + * Get the inferred flag for a message. + * + * The inferred flag for a message indicates how the message content + * is encoded into AMQP sections. If inferred is true then binary and + * list values in the body of the message will be encoded as AMQP DATA + * 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. Use + * ::pn_message_set_inferred to set the value. + * + * @param[in] msg a message object + * @return the value of the inferred flag for the message + */ + PN_CPP_EXTERN bool inferred() const; + PN_CPP_EXTERN void inferred(bool); + private: pn_message_t *message_; }; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/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 4ff8b52..d6ce186 100644 --- a/proton-c/bindings/cpp/include/proton/message_id.hpp +++ b/proton-c/bindings/cpp/include/proton/message_id.hpp @@ -29,14 +29,13 @@ 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) {} 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& operator=(const message_id& x) { value_ = x.value_; return *this; } message_id& operator=(const uint64_t& x) { value_ = x; return *this; } @@ -45,6 +44,7 @@ class message_id : public comparable<message_id> { 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; } void clear() { value_.clear(); } bool empty() const { return value_.empty(); } @@ -58,7 +58,6 @@ class message_id : public comparable<message_id> { void get(std::string& x) const { value_.get(x); } template<class T> T get() const { T x; get(x); return x; } - template<class T> operator T() const { return get<T>(); } // String representation: decimal representation of uint64_t, standard text // representation of UUID, amqp_string or amqp_binary are returned without @@ -73,6 +72,7 @@ class message_id : public comparable<message_id> { friend PN_CPP_EXTERN decoder& operator>>(decoder&, message_id&); private: + message_id(const data& d) : value_(d) {} value value_; friend class message; }; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/proton-c/bindings/cpp/include/proton/pn_unique_ptr.hpp ---------------------------------------------------------------------- diff --git a/proton-c/bindings/cpp/include/proton/pn_unique_ptr.hpp b/proton-c/bindings/cpp/include/proton/pn_unique_ptr.hpp index b94667f..d63c8e5 100644 --- a/proton-c/bindings/cpp/include/proton/pn_unique_ptr.hpp +++ b/proton-c/bindings/cpp/include/proton/pn_unique_ptr.hpp @@ -46,9 +46,11 @@ template <class T> class pn_unique_ptr { T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } T* get() const { return ptr_; } - void swap(pn_unique_ptr<T>& x) { T *p = x.ptr_; x.ptr_ = ptr_; ptr_ = p; } - void reset(T* p = 0) { pn_unique_ptr<T> tmp(p); tmp.swap(*this); } + void swap(pn_unique_ptr<T>& x) { std::swap(ptr_, x.ptr_); } + void reset(T* p = 0) { pn_unique_ptr<T> tmp(p); swap(tmp); } T* release() { T *p = ptr_; ptr_ = 0; return p; } + operator bool() const { return get(); } + bool operator !() const { return get(); } #if PN_HAS_STD_PTR operator std::unique_ptr<T>() { T *p = ptr_; ptr_ = 0; return std::unique_ptr<T>(p); } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/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 39902dc..62b8a9e 100644 --- a/proton-c/bindings/cpp/include/proton/value.hpp +++ b/proton-c/bindings/cpp/include/proton/value.hpp @@ -71,9 +71,6 @@ class value : public comparable<value> { /** 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; @@ -83,7 +80,6 @@ class value : public comparable<value> { private: pn_unique_ptr<data> data_; - friend class message; }; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/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 1fe13a2..cab7826 100644 --- a/proton-c/bindings/cpp/src/decoder.cpp +++ b/proton-c/bindings/cpp/src/decoder.cpp @@ -77,9 +77,11 @@ bool decoder::more() const { return pn_data_next(pn_cast(this)); } -void decoder::rewind() { ::pn_data_rewind(pn_cast(this)); } +void decoder::rewind() { pn_data_rewind(pn_cast(this)); } -void decoder::backup() { ::pn_data_prev(pn_cast(this)); } +void decoder::backup() { pn_data_prev(pn_cast(this)); } + +void decoder::skip() { pn_data_next(pn_cast(this)); } data& decoder::data() { return *data::cast(pn_cast(this)); } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/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 7ebb821..285771b 100644 --- a/proton-c/bindings/cpp/src/message.cpp +++ b/proton-c/bindings/cpp/src/message.cpp @@ -44,6 +44,8 @@ message::message(const message &m) : message_(::pn_message()) { *this = m; } message::message(message &&m) : message_(::pn_message()) { swap(m); } #endif +message::message(const value& v) : message_(::pn_message()) { body(v); } + message::~message() { ::pn_message_free(message_); } void message::swap(message& m) { std::swap(message_, m.message_); } @@ -161,6 +163,10 @@ std::string message::reply_to_group_id() const { 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() = v; } const data& message::body() const { @@ -171,6 +177,58 @@ data& message::body() { return *data::cast(pn_message_body(message_)); } +void message::properties(const value& v) { + properties() = v; +} + +const data& message::properties() const { + return *data::cast(pn_message_properties(message_)); +} + +data& message::properties() { + return *data::cast(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; + } + return value(); +} + +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; + } + } + return false; +} + void message::encode(std::string &s) const { size_t sz = s.capacity(); if (sz < 512) sz = 512; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/89923cf5/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 new file mode 100644 index 0000000..928e5a6 --- /dev/null +++ b/proton-c/bindings/cpp/src/message_test.cpp @@ -0,0 +1,89 @@ +/* + * 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/message.hpp" +#include "test_bits.hpp" +#include <string> +#include <fstream> +#include <streambuf> +#include <iosfwd> + +using namespace std; +using namespace proton; + + +#define CHECK_STR(ATTR) \ + m.ATTR(#ATTR); \ + ASSERT_EQUAL(std::string(#ATTR), m.ATTR()) + +#define CHECK_STR_VALUE(ATTR) \ + m.ATTR(#ATTR); \ + ASSERT_EQUAL(std::string(#ATTR), m.ATTR().get<std::string>()) + +void test_message() { + message m("hello"); + std::string s = m.body().get<std::string>(); + ASSERT_EQUAL("hello", s); + + CHECK_STR_VALUE(id); + CHECK_STR(user_id); + CHECK_STR(address); + CHECK_STR(subject); + CHECK_STR(reply_to); + CHECK_STR_VALUE(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"))); +} + +int main(int argc, char** argv) { + int failed = 0; + failed += RUN_TEST(test_message); + return failed; +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
