You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by as...@apache.org on 2017/06/02 22:51:20 UTC
[06/20] qpid-proton git commit: PROTON-1288: c++ provide convenient
access to message/filter maps
PROTON-1288: c++ provide convenient access to message/filter maps
proton::map type to wrap map values for message properties/annotations and source filters.
Has the following features:
- type safe template for the standard AMQP 'restricted map' types
- simple get()/put() interface for simple check/set individual properties
- converts directly to/from a proton::value efficient pass-through, no re-coding
- converts directly to/from std:map, std::unordered_map etc.
- encode/decode only as required
Intended use: use proton::map directly for simple get()/put(), convert to a
standard map or sequence type for more complex use (iteration, preserving
encoded order etc.)
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/864519dd
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/864519dd
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/864519dd
Branch: refs/heads/PROTON-1488
Commit: 864519ddca6c45a10ec01598b3ff3b33e5591561
Parents: f2df847
Author: Alan Conway <ac...@redhat.com>
Authored: Wed May 24 13:03:11 2017 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Wed May 24 15:37:11 2017 -0400
----------------------------------------------------------------------
examples/cpp/CMakeLists.txt | 1 +
examples/cpp/README.dox | 6 +
examples/cpp/example_test.py | 41 ++-
examples/cpp/message_properties.cpp | 22 +-
examples/exampletest.py | 5 +-
proton-c/bindings/cpp/CMakeLists.txt | 3 +-
.../cpp/include/proton/codec/encoder.hpp | 13 +
.../cpp/include/proton/internal/cached_map.hpp | 91 -------
.../include/proton/internal/pn_unique_ptr.hpp | 6 +
.../cpp/include/proton/internal/type_traits.hpp | 1 -
proton-c/bindings/cpp/include/proton/map.hpp | 128 ++++++++++
.../bindings/cpp/include/proton/message.hpp | 28 +-
proton-c/bindings/cpp/include/proton/source.hpp | 8 +-
proton-c/bindings/cpp/include/proton/value.hpp | 3 +-
proton-c/bindings/cpp/src/cached_map.cpp | 130 ----------
.../bindings/cpp/src/connection_driver_test.cpp | 38 +++
proton-c/bindings/cpp/src/include/test_bits.hpp | 1 +
proton-c/bindings/cpp/src/map.cpp | 254 +++++++++++++++++++
proton-c/bindings/cpp/src/map_test.cpp | 126 +++++++++
proton-c/bindings/cpp/src/message.cpp | 92 +++----
proton-c/bindings/cpp/src/message_test.cpp | 19 +-
proton-c/bindings/cpp/src/node_options.cpp | 3 +-
proton-c/bindings/cpp/src/scalar_base.cpp | 3 +-
proton-c/bindings/cpp/src/source.cpp | 26 +-
proton-c/bindings/cpp/src/value.cpp | 3 +
25 files changed, 697 insertions(+), 354 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/examples/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt
index 236aac7..ecd88cf 100644
--- a/examples/cpp/CMakeLists.txt
+++ b/examples/cpp/CMakeLists.txt
@@ -41,6 +41,7 @@ foreach(example
helloworld_direct
simple_recv
simple_send
+ message_properties
scheduled_send_03
direct_recv
direct_send
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/examples/cpp/README.dox
----------------------------------------------------------------------
diff --git a/examples/cpp/README.dox b/examples/cpp/README.dox
index 447d3ad..327dbef 100644
--- a/examples/cpp/README.dox
+++ b/examples/cpp/README.dox
@@ -50,6 +50,12 @@ on 127.0.0.1:5672. Simply prints out the body of received messages.
*/
+/** @example message_properties.cpp
+
+Shows how to set and examine message properties.
+
+*/
+
/** @example direct_send.cpp
Accepts an incoming connection and then sends like `simple_send`. You can
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/examples/cpp/example_test.py
----------------------------------------------------------------------
diff --git a/examples/cpp/example_test.py b/examples/cpp/example_test.py
index f9a0825..5773142 100644
--- a/examples/cpp/example_test.py
+++ b/examples/cpp/example_test.py
@@ -125,53 +125,53 @@ class ContainerExampleTest(BrokerTestCase):
broker_exe = "broker"
def test_helloworld(self):
- self.assertEqual('Hello World!\n', self.proc(["helloworld", self.addr]).wait_exit())
+ self.assertMultiLineEqual('Hello World!\n', self.proc(["helloworld", self.addr]).wait_exit())
def test_helloworld_direct(self):
with TestPort() as tp:
- self.assertEqual('Hello World!\n', self.proc(["helloworld_direct", tp.addr]).wait_exit())
+ self.assertMultiLineEqual('Hello World!\n', self.proc(["helloworld_direct", tp.addr]).wait_exit())
def test_simple_send_recv(self):
- self.assertEqual("all messages confirmed\n",
+ self.assertMultiLineEqual("all messages confirmed\n",
self.proc(["simple_send", "-a", self.addr]).wait_exit())
- self.assertEqual(recv_expect("simple_recv", self.addr), self.proc(["simple_recv", "-a", self.addr]).wait_exit())
+ self.assertMultiLineEqual(recv_expect("simple_recv", self.addr), self.proc(["simple_recv", "-a", self.addr]).wait_exit())
def test_simple_recv_send(self):
# Start receiver first, then run sender"""
recv = self.proc(["simple_recv", "-a", self.addr])
- self.assertEqual("all messages confirmed\n",
+ self.assertMultiLineEqual("all messages confirmed\n",
self.proc(["simple_send", "-a", self.addr]).wait_exit())
- self.assertEqual(recv_expect("simple_recv", self.addr), recv.wait_exit())
+ self.assertMultiLineEqual(recv_expect("simple_recv", self.addr), recv.wait_exit())
def test_simple_send_direct_recv(self):
with TestPort() as tp:
addr = "%s/examples" % tp.addr
recv = self.proc(["direct_recv", "-a", addr], "listening")
- self.assertEqual("all messages confirmed\n",
+ self.assertMultiLineEqual("all messages confirmed\n",
self.proc(["simple_send", "-a", addr]).wait_exit())
- self.assertEqual(recv_expect("direct_recv", addr), recv.wait_exit())
+ self.assertMultiLineEqual(recv_expect("direct_recv", addr), recv.wait_exit())
def test_simple_recv_direct_send(self):
with TestPort() as tp:
addr = "%s/examples" % tp.addr
send = self.proc(["direct_send", "-a", addr], "listening")
- self.assertEqual(recv_expect("simple_recv", addr),
+ self.assertMultiLineEqual(recv_expect("simple_recv", addr),
self.proc(["simple_recv", "-a", addr]).wait_exit())
- self.assertEqual(
+ self.assertMultiLineEqual(
"direct_send listening on %s\nall messages confirmed\n" % addr,
send.wait_exit())
def test_request_response(self):
server = self.proc(["server", "-a", self.addr], "connected")
- self.assertEqual(CLIENT_EXPECT,
+ self.assertMultiLineEqual(CLIENT_EXPECT,
self.proc(["client", "-a", self.addr]).wait_exit())
def test_request_response_direct(self):
with TestPort() as tp:
addr = "%s/examples" % tp.addr
server = self.proc(["server_direct", "-a", addr], "listening")
- self.assertEqual(CLIENT_EXPECT,
+ self.assertMultiLineEqual(CLIENT_EXPECT,
self.proc(["client", "-a", addr]).wait_exit())
def test_flow_control(self):
@@ -181,7 +181,7 @@ success: Example 3: drain without credit
success: Exmaple 4: high/low watermark
"""
with TestPort() as tp:
- self.assertEqual(want, self.proc(["flow_control", "--address", tp.addr, "--quiet"]).wait_exit())
+ self.assertMultiLineEqual(want, self.proc(["flow_control", "--address", tp.addr, "--quiet"]).wait_exit())
def test_encode_decode(self):
want="""
@@ -208,7 +208,7 @@ list[int(42), boolean(0), symbol(x)]
map{string(k1):int(42), symbol(k2):boolean(0)}
"""
self.maxDiff = None
- self.assertEqual(want, self.proc(["encode_decode"]).wait_exit())
+ self.assertMultiLineEqual(want, self.proc(["encode_decode"]).wait_exit())
def test_scheduled_send_03(self):
# Output should be a bunch of "send" lines but can't guarantee exactly how many.
@@ -224,6 +224,19 @@ map{string(k1):int(42), symbol(k2):boolean(0)}
except ProcError: # File not found, not a C++11 build.
pass
+ def test_message_properties(self):
+ expect="""using put/get: short=123 string=foo symbol=sym
+using coerce: short(as long)=123
+props[short]=123
+props[string]=foo
+props[symbol]=sym
+short=42 string=bar
+expected conversion_error: "unexpected type, want: uint got: int"
+expected conversion_error: "unexpected type, want: uint got: string"
+"""
+ self.assertMultiLineEqual(expect, self.proc(["message_properties"]).wait_exit())
+
+
class ContainerExampleSSLTest(BrokerTestCase):
"""Run the SSL container examples, verify they behave as expected."""
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/examples/cpp/message_properties.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/message_properties.cpp b/examples/cpp/message_properties.cpp
index 64d597b..cb5c6b8 100644
--- a/examples/cpp/message_properties.cpp
+++ b/examples/cpp/message_properties.cpp
@@ -31,7 +31,6 @@ int main(int argc, char **argv) {
m.properties().put("short", int16_t(123));
m.properties().put("string", "foo");
m.properties().put("symbol", proton::symbol("sym"));
- m.properties().put("bool", true);
// Examining properties using proton::get()
@@ -53,25 +52,26 @@ int main(int argc, char **argv) {
<< " short=" << i
<< " string=" << s
<< " symbol=" << m.properties().get("symbol")
- << " bool=" << m.properties().get("bool")
<< std::endl;
- // Converting properties to a compatible type
+ // Converting properties to a convertible type
std::cout << "using coerce:"
- << " short(as int)=" << proton::coerce<int>(m.properties().get("short"))
- << " bool(as int)=" << proton::coerce<int>(m.properties().get("bool"))
+ << " short(as long)="
+ << proton::coerce<long>(m.properties().get("short"))
<< std::endl;
- // Extract the properties map for more complex map operations.
- proton::property_std_map props;
- proton::get(m.properties().value(), props);
- for (proton::property_std_map::iterator i = props.begin(); i != props.end(); ++i) {
+ // Extract the properties as a std::map for more complex map operations.
+ // You can use other map and sequence types to hold a map, see @ref types_page
+ typedef std::map<std::string, proton::scalar> property_map;
+ property_map props;
+ proton::get(m.properties(), props);
+ for (property_map::iterator i = props.begin(); i != props.end(); ++i) {
std::cout << "props[" << i->first << "]=" << i->second << std::endl;
}
props["string"] = "bar";
props["short"] = 42;
- // Update the properties in the message from props
- m.properties().value() = props;
+ // Update the properties in the message from the modified props map
+ m.properties() = props;
std::cout << "short=" << m.properties().get("short")
<< " string=" << m.properties().get("string")
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/examples/exampletest.py
----------------------------------------------------------------------
diff --git a/examples/exampletest.py b/examples/exampletest.py
index bb055a1..5c53616 100644
--- a/examples/exampletest.py
+++ b/examples/exampletest.py
@@ -166,10 +166,13 @@ class TestCase(unittest.TestCase):
super(TestCase, self).tearDown()
if _tc_missing('assertIn'):
-
def assertIn(self, a, b):
self.assertTrue(a in b, "%r not in %r" % (a, b))
+ if _tc_missing('assertMultiLineEqual'):
+ def assertMultiLineEqual(self, a, b):
+ self.assertEqual(a, b)
+
class ExampleTestCase(TestCase):
"""TestCase that manages started processes"""
def setUp(self):
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/CMakeLists.txt b/proton-c/bindings/cpp/CMakeLists.txt
index 04cb568..d1c6fd1 100644
--- a/proton-c/bindings/cpp/CMakeLists.txt
+++ b/proton-c/bindings/cpp/CMakeLists.txt
@@ -29,7 +29,7 @@ add_definitions(${CXX_WARNING_FLAGS})
set(qpid-proton-cpp-source
src/binary.cpp
src/byte_array.cpp
- src/cached_map.cpp
+ src/map.cpp
src/connection.cpp
src/connection_options.cpp
src/connector.cpp
@@ -176,6 +176,7 @@ add_cpp_test(connection_driver_test)
add_cpp_test(thread_safe_test)
add_cpp_test(interop_test ${CMAKE_SOURCE_DIR}/tests)
add_cpp_test(message_test)
+add_cpp_test(map_test)
add_cpp_test(scalar_test)
add_cpp_test(value_test)
add_cpp_test(container_test)
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/include/proton/codec/encoder.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/codec/encoder.hpp b/proton-c/bindings/cpp/include/proton/codec/encoder.hpp
index 222e95b..2610021 100644
--- a/proton-c/bindings/cpp/include/proton/codec/encoder.hpp
+++ b/proton-c/bindings/cpp/include/proton/codec/encoder.hpp
@@ -194,6 +194,19 @@ template <> struct is_encodable<value> : public true_type {};
using is_encodable_impl::is_encodable;
+// Metafunction to test if a class looks like an encodable map from K to T.
+template <class M, class K, class T, class Enable = void>
+struct is_encodable_map : public internal::false_type {};
+
+template <class M, class K, class T> struct is_encodable_map<
+ M, K, T, typename internal::enable_if<
+ internal::is_same<K, typename M::key_type>::value &&
+ internal::is_same<T, typename M::mapped_type>::value &&
+ is_encodable<M>::value
+ >::type
+ > : public internal::true_type {};
+
+
/// @endcond
} // codec
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/include/proton/internal/cached_map.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/internal/cached_map.hpp b/proton-c/bindings/cpp/include/proton/internal/cached_map.hpp
deleted file mode 100644
index 4f388a1..0000000
--- a/proton-c/bindings/cpp/include/proton/internal/cached_map.hpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#ifndef PROTON_CPP_CACHED_MAP_H
-#define PROTON_CPP_CACHED_MAP_H
-
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "./config.hpp"
-#include "./export.hpp"
-#include "./pn_unique_ptr.hpp"
-
-#include <cstddef>
-
-namespace proton {
-
-namespace codec {
-class decoder;
-class encoder;
-}
-
-namespace internal {
-
-template <class key_type, class value_type>
-class map_type_impl;
-
-/// A convenience class to view and manage AMQP map data contained in
-/// a proton::value. An internal cache of the map data is created as
-/// needed.
-template <class K, class V>
-class cached_map;
-
-template <class K, class V>
-PN_CPP_EXTERN proton::codec::decoder& operator>>(proton::codec::decoder& d, cached_map<K,V>& m);
-template <class K, class V>
-PN_CPP_EXTERN proton::codec::encoder& operator<<(proton::codec::encoder& e, const cached_map<K,V>& m);
-
-template <class key_type, class value_type>
-class PN_CPP_CLASS_EXTERN cached_map {
- typedef map_type_impl<key_type, value_type> map_type;
-
- public:
- PN_CPP_EXTERN cached_map();
- PN_CPP_EXTERN cached_map(const cached_map& cm);
- PN_CPP_EXTERN cached_map& operator=(const cached_map& cm);
-#if PN_CPP_HAS_RVALUE_REFERENCES
- PN_CPP_EXTERN cached_map(cached_map&&);
- PN_CPP_EXTERN cached_map& operator=(cached_map&&);
-#endif
- PN_CPP_EXTERN ~cached_map();
-
- PN_CPP_EXTERN value_type get(const key_type& k) const;
- PN_CPP_EXTERN void put(const key_type& k, const value_type& v);
- PN_CPP_EXTERN size_t erase(const key_type& k);
- PN_CPP_EXTERN bool exists(const key_type& k) const;
- PN_CPP_EXTERN size_t size();
- PN_CPP_EXTERN void clear();
- PN_CPP_EXTERN bool empty();
-
- /// @cond INTERNAL
- private:
- pn_unique_ptr<map_type> map_;
-
- void make_cached_map();
-
- friend PN_CPP_EXTERN proton::codec::decoder& operator>> <>(proton::codec::decoder& d, cached_map<key_type, value_type>& m);
- friend PN_CPP_EXTERN proton::codec::encoder& operator<< <>(proton::codec::encoder& e, const cached_map<key_type, value_type>& m);
- /// @endcond
-};
-
-
-}
-}
-
-#endif // PROTON_CPP_CACHED_MAP_H
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/include/proton/internal/pn_unique_ptr.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/internal/pn_unique_ptr.hpp b/proton-c/bindings/cpp/include/proton/internal/pn_unique_ptr.hpp
index 323c701..a3416e5 100644
--- a/proton-c/bindings/cpp/include/proton/internal/pn_unique_ptr.hpp
+++ b/proton-c/bindings/cpp/include/proton/internal/pn_unique_ptr.hpp
@@ -29,6 +29,9 @@
namespace proton {
namespace internal {
+template <class T> class pn_unique_ptr;
+template <class T> void swap(pn_unique_ptr<T>& x, pn_unique_ptr<T>& y);
+
/// A simple unique ownership pointer, used as a return value from
/// functions that transfer ownership to the caller.
///
@@ -55,6 +58,7 @@ template <class T> class pn_unique_ptr {
explicit operator bool() const { return get(); }
#endif
bool operator !() const { return !get(); }
+ void swap(pn_unique_ptr& x) { std::swap(ptr_, x.ptr_); }
#if PN_CPP_HAS_UNIQUE_PTR
operator std::unique_ptr<T>() { T *p = ptr_; ptr_ = 0; return std::unique_ptr<T>(p); }
@@ -64,6 +68,8 @@ template <class T> class pn_unique_ptr {
T* ptr_;
};
+template <class T> void swap(pn_unique_ptr<T>& x, pn_unique_ptr<T>& y) { x.swap(y); }
+
} // internal
} // proton
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/include/proton/internal/type_traits.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/internal/type_traits.hpp b/proton-c/bindings/cpp/include/proton/internal/type_traits.hpp
index 8abba55..bd16d29 100644
--- a/proton-c/bindings/cpp/include/proton/internal/type_traits.hpp
+++ b/proton-c/bindings/cpp/include/proton/internal/type_traits.hpp
@@ -151,7 +151,6 @@ template <class T> struct is_unknown_integer {
template<class T, class = typename enable_if<is_unknown_integer<T>::value>::type>
struct known_integer : public integer_type<sizeof(T), is_signed<T>::value> {};
-
// Helper base for SFINAE templates.
struct sfinae {
typedef char yes;
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/include/proton/map.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/map.hpp b/proton-c/bindings/cpp/include/proton/map.hpp
new file mode 100644
index 0000000..8399b16
--- /dev/null
+++ b/proton-c/bindings/cpp/include/proton/map.hpp
@@ -0,0 +1,128 @@
+#ifndef PROTON_CPP_MAP_H
+#define PROTON_CPP_MAP_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "./value.hpp"
+
+#include "internal/pn_unique_ptr.hpp"
+
+#include <cstddef>
+
+namespace proton {
+
+namespace codec {
+class decoder;
+class encoder;
+}
+
+template <class K, class T>
+class map_type_impl;
+
+template <class K, class T>
+class map;
+
+template <class K, class T>
+PN_CPP_EXTERN proton::codec::decoder& operator>>(proton::codec::decoder& d, map<K,T>& m);
+template <class K, class T>
+PN_CPP_EXTERN proton::codec::encoder& operator<<(proton::codec::encoder& e, const map<K,T>& m);
+template <class K, class T>
+PN_CPP_EXTERN void swap(map<K,T>&, map<K,T>&);
+
+/// Used to access standard AMQP property, annotation and filter maps attached
+/// to proton::message and proton::source.
+///
+/// Provides only basic get()/set() operations for convenience. For more
+/// complicated use (iteration, preserving order etc.) you should convert to a
+/// standard C++ map type such as std::map. See @ref message_propreties.cpp
+/// and @ref types_page.
+///
+template <class K, class T>
+class PN_CPP_CLASS_EXTERN map {
+ template <class M, class U=void>
+ struct assignable_map :
+ public internal::enable_if<codec::is_encodable_map<M,K,T>::value, U> {};
+
+ public:
+ /// Create an empty map value
+ PN_CPP_EXTERN map();
+
+ PN_CPP_EXTERN map(const map& cm);
+ PN_CPP_EXTERN map& operator=(const map& cm);
+#if PN_CPP_HAS_RVALUE_REFERENCES
+ PN_CPP_EXTERN map(map&&);
+ PN_CPP_EXTERN map& operator=(map&&);
+#endif
+
+ PN_CPP_EXTERN ~map();
+
+ /// Type-safe assign from a compatible map type, e.g. std::map<K,T> - see @types_page
+ template <class M>
+ typename assignable_map<M, map&>::type operator=(const M& x) { value(x); return *this;}
+
+ /// Copy from a proton::value.
+ /// @throw proton::conversion_error if x does not contain a compatible map.
+ PN_CPP_EXTERN void value(const value& x);
+ /// Access as a proton::value
+ PN_CPP_EXTERN proton::value& value();
+ /// Access as a proton::value
+ PN_CPP_EXTERN const proton::value& value() const;
+
+ /// Get the map entry for key k, return T() if no such entry
+ PN_CPP_EXTERN T get(const K& k) const;
+ /// Put a map entry for key k.
+ PN_CPP_EXTERN void put(const K& k, const T& v);
+ /// Erase the map entry at k
+ PN_CPP_EXTERN size_t erase(const K& k);
+ /// True if there is a map entry for k
+ PN_CPP_EXTERN bool exists(const K& k) const;
+ /// Number of map entries
+ PN_CPP_EXTERN size_t size() const;
+ /// Clear the map value
+ PN_CPP_EXTERN void clear();
+ /// True if the map is empty
+ PN_CPP_EXTERN bool empty() const;
+
+ ///@cond INTERNAL
+ explicit map(pn_data_t*);
+ void reset(pn_data_t*);
+ ///@endcond
+
+ private:
+ typedef map_type_impl<K,T> map_type;
+ mutable internal::pn_unique_ptr<map_type> map_;
+ mutable proton::value value_;
+
+ void ensure() const;
+ const map_type& cache() const;
+ map_type& cache_update();
+ proton::value& flush() const;
+
+ friend PN_CPP_EXTERN proton::codec::decoder& operator>> <>(proton::codec::decoder&, map&);
+ friend PN_CPP_EXTERN proton::codec::encoder& operator<< <>(proton::codec::encoder&, const map&);
+ friend PN_CPP_EXTERN void swap<>(map&, map&);
+ /// @endcond
+};
+
+} // namespace proton
+
+#endif // PROTON_CPP_MAP_H
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/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 faf2f29..a25f7db 100644
--- a/proton-c/bindings/cpp/include/proton/message.hpp
+++ b/proton-c/bindings/cpp/include/proton/message.hpp
@@ -27,8 +27,8 @@
#include "./duration.hpp"
#include "./timestamp.hpp"
#include "./value.hpp"
+#include "./map.hpp"
-#include "./internal/cached_map.hpp"
#include "./internal/pn_unique_ptr.hpp"
#include <proton/type_compat.h>
@@ -48,11 +48,11 @@ class message {
public:
/// **Experimental** - A map of string keys and AMQP scalar
/// values.
- class property_map : public internal::cached_map<std::string, scalar> {};
+ typedef map<std::string, scalar> property_map;
/// **Experimental** - A map of AMQP annotation keys and AMQP
/// values.
- class annotation_map : public internal::cached_map<annotation_key, value> {};
+ typedef map<annotation_key, value> annotation_map;
/// Create an empty message.
PN_CPP_EXTERN message();
@@ -243,7 +243,7 @@ class message {
/// acquired, by other recipients.
// XXX The triple-not in the last sentence above is confusing.
-
+
PN_CPP_EXTERN bool first_acquirer() const;
/// Set the first acquirer flag.
@@ -287,33 +287,25 @@ class message {
/// @}
- /// @name Extended attributes
+ /// @name **Experimental** - Application properties
/// @{
/// **Experimental** - Get the application properties map. It can
/// be modified in place.
PN_CPP_EXTERN property_map& properties();
- /// **Experimental** - Get the application properties map. It can
- /// be modified in place.
+ /// **Experimental** - examine the application properties map.
PN_CPP_EXTERN const property_map& properties() const;
- /// **Experimental** - Get the message annotations map. It can be
- /// modified in place.
+ /// @name **Experimental** - Annotations
+ ///
+ /// Normally used by messaging infrastructure, not applications.
+ /// @{
PN_CPP_EXTERN annotation_map& message_annotations();
-
- /// **Experimental** - Get the message annotations map. It can be
- /// modified in place.
PN_CPP_EXTERN const annotation_map& message_annotations() const;
- /// **Experimental** - Get the delivery annotations map. It can
- /// be modified in place.
PN_CPP_EXTERN annotation_map& delivery_annotations();
-
- /// **Experimental** - Get the delivery annotations map. It can
- /// be modified in place.
PN_CPP_EXTERN const annotation_map& delivery_annotations() const;
-
/// @}
/// Default priority assigned to new messages.
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/include/proton/source.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/source.hpp b/proton-c/bindings/cpp/include/proton/source.hpp
index 321af9a..59963dd 100644
--- a/proton-c/bindings/cpp/include/proton/source.hpp
+++ b/proton-c/bindings/cpp/include/proton/source.hpp
@@ -24,7 +24,7 @@
#include "./fwd.hpp"
#include "./internal/export.hpp"
-#include "./internal/cached_map.hpp"
+#include "./map.hpp"
#include "./symbol.hpp"
#include "./terminus.hpp"
#include "./value.hpp"
@@ -42,7 +42,7 @@ class source : public terminus {
public:
/// **Experimental** - A map of AMQP symbol keys and filter
/// specifiers.
- class filter_map : public internal::cached_map<symbol, value> {};
+ typedef map<symbol, value> filter_map;
/// Create an empty source.
source() : terminus() {}
@@ -69,13 +69,15 @@ class source : public terminus {
PN_CPP_EXTERN enum distribution_mode distribution_mode() const;
/// **Experimental** - Obtain the set of message filters.
- PN_CPP_EXTERN filter_map filters() const;
+ PN_CPP_EXTERN const filter_map& filters() const;
private:
source(pn_terminus_t* t);
source(const sender&);
source(const receiver&);
+ filter_map filters_;
+
/// @cond INTERNAL
friend class proton::internal::factory<source>;
friend class sender;
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/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 75927f7..bc7ed86 100644
--- a/proton-c/bindings/cpp/include/proton/value.hpp
+++ b/proton-c/bindings/cpp/include/proton/value.hpp
@@ -72,7 +72,7 @@ class value : public internal::value_base, private internal::comparable<value> {
#endif
/// @}
- /// Construct from any allowed type T.
+ /// Copy from any allowed type T.
template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
/// Assign from any allowed type T.
@@ -88,7 +88,6 @@ class value : public internal::value_base, private internal::comparable<value> {
/// True if the value is null
PN_CPP_EXTERN bool empty() const;
-
/// Reset the value to null/empty
PN_CPP_EXTERN void clear();
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/src/cached_map.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/cached_map.cpp b/proton-c/bindings/cpp/src/cached_map.cpp
deleted file mode 100644
index 5411aa1..0000000
--- a/proton-c/bindings/cpp/src/cached_map.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- *
- * 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/internal/cached_map.hpp"
-
-#include "proton/annotation_key.hpp"
-#include "proton/scalar.hpp"
-#include "proton/value.hpp"
-#include "proton/codec/decoder.hpp"
-#include "proton/codec/encoder.hpp"
-#include "proton/codec/map.hpp"
-
-#include <map>
-#include <string>
-
-namespace proton {
-namespace internal {
-
- // use std::map as the actual cached_map implementation type
-template <class K, class V>
-class map_type_impl : public std::map<K, V> {};
-
-template <class K, class V>
-cached_map<K,V>::cached_map() {}
-template <class K, class V>
-cached_map<K,V>::cached_map(const cached_map& cm) { if ( !cm.map_ ) return; map_.reset(new map_type(*cm.map_)); }
-template <class K, class V>
-cached_map<K,V>& cached_map<K,V>::operator=(const cached_map& cm) {
- if (&cm != this) {
- cached_map<K,V> t;
- map_type *m = !cm.map_ ? 0 : new map_type(*cm.map_);
- t.map_.reset(map_.release());
- map_.reset(m);
- }
- return *this;
-}
-
-template <class K, class V>
-#if PN_CPP_HAS_RVALUE_REFERENCES
-cached_map<K,V>::cached_map(cached_map&& cm) : map_(std::move(cm.map_)) {}
-template <class K, class V>
-cached_map<K,V>& cached_map<K,V>::operator=(cached_map&& cm) { map_.reset(cm.map_.release()); return *this; }
-template <class K, class V>
-#endif
-cached_map<K,V>::~cached_map() {}
-
-template <class K, class V>
-V cached_map<K,V>::get(const K& k) const {
- if ( !map_ ) return V();
- typename map_type::const_iterator i = map_->find(k);
- if ( i==map_->end() ) return V();
- return i->second;
-}
-template <class K, class V>
-void cached_map<K,V>::put(const K& k, const V& v) {
- if ( !map_ ) make_cached_map();
- (*map_)[k] = v;
-}
-template <class K, class V>
-size_t cached_map<K,V>::erase(const K& k) {
- if ( !map_ ) return 0;
- return map_->erase(k);
-}
-template <class K, class V>
-bool cached_map<K,V>::exists(const K& k) const {
- if ( !map_ ) return false;
- return map_->count(k) > 0;
-}
-
-template <class K, class V>
-size_t cached_map<K,V>::size() {
- if ( !map_ ) return 0;
- return map_->size();
-}
-template <class K, class V>
-void cached_map<K,V>::clear() {
- map_.reset();
-}
-template <class K, class V>
-bool cached_map<K,V>::empty() {
- if ( !map_ ) return true;
- return map_->empty();
-}
-
-template <class K, class V>
-void cached_map<K,V>::make_cached_map() { map_.reset(new map_type); }
-
-template <class K, class V>
-PN_CPP_EXTERN proton::codec::decoder& operator>>(proton::codec::decoder& d, cached_map<K,V>& m) {
- if ( !m.map_ ) m.make_cached_map();
- return d >> *(m.map_);
-}
-template <class K, class V>
-PN_CPP_EXTERN proton::codec::encoder& operator<<(proton::codec::encoder& e, const cached_map<K,V>& m) {
- if ( !m.map_ ) return e;
- return e << *(m.map_);
-}
-
-// Force the necessary template instantiations so that the library exports the correct symbols
-template class PN_CPP_CLASS_EXTERN cached_map<std::string, scalar>;
-template class PN_CPP_CLASS_EXTERN cached_map<annotation_key, value>;
-template class PN_CPP_CLASS_EXTERN cached_map<symbol, value>;
-
-template proton::codec::decoder& operator>> <>(proton::codec::decoder& d, cached_map<std::string, scalar>& m);
-template proton::codec::encoder& operator<< <>(proton::codec::encoder& e, const cached_map<std::string, scalar>& m);
-template proton::codec::decoder& operator>> <>(proton::codec::decoder& d, cached_map<annotation_key, value>& m);
-template proton::codec::encoder& operator<< <>(proton::codec::encoder& e, const cached_map<annotation_key, value>& m);
-template proton::codec::decoder& operator>> <>(proton::codec::decoder& d, cached_map<symbol, value>& m);
-template proton::codec::encoder& operator<< <>(proton::codec::encoder& e, const cached_map<symbol, value>& m);
-
-}
-}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/src/connection_driver_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/connection_driver_test.cpp b/proton-c/bindings/cpp/src/connection_driver_test.cpp
index 372240b..760ac55 100644
--- a/proton-c/bindings/cpp/src/connection_driver_test.cpp
+++ b/proton-c/bindings/cpp/src/connection_driver_test.cpp
@@ -21,6 +21,7 @@
#include "test_bits.hpp"
#include "proton_bits.hpp"
+#include "proton/container.hpp"
#include "proton/io/connection_driver.hpp"
#include "proton/io/link_namer.hpp"
#include "proton/link.hpp"
@@ -248,10 +249,47 @@ void test_no_container() {
} catch (proton::error) {}
}
+void test_link_filters() {
+ // Propagation of link properties
+ record_handler ha, hb;
+ driver_pair e(ha, hb);
+
+ source_options opts;
+ source::filter_map f;
+ f.put("xx", "xxx");
+ ASSERT_EQUAL(1U, f.size());
+ e.a.connection().open_sender("x", sender_options().source(source_options().filters(f)));
+
+ f.clear();
+ f.put("yy", "yyy");
+ ASSERT_EQUAL(1U, f.size());
+ e.a.connection().open_receiver("y", receiver_options().source(source_options().filters(f)));
+
+ while (ha.senders.size()+ha.receivers.size() < 2 ||
+ hb.senders.size()+hb.receivers.size() < 2)
+ e.process();
+
+ proton::sender ax = quick_pop(ha.senders);
+ proton::receiver ay = quick_pop(ha.receivers);
+ proton::receiver bx = quick_pop(hb.receivers);
+ proton::sender by = quick_pop(hb.senders);
+
+ // C++ binding only gives remote_source so only look at remote ends of links
+ f = by.source().filters();
+ ASSERT_EQUAL(1U, f.size());
+ ASSERT_EQUAL(value("yyy"), f.get("yy"));
+
+ f = bx.source().filters();
+ ASSERT_EQUAL(1U, f.size());
+ ASSERT_EQUAL(value("xxx"), bx.source().filters().get("xx"));
+}
+
+
}
int main(int, char**) {
int failed = 0;
+ RUN_TEST(failed, test_link_filters());
RUN_TEST(failed, test_driver_link_id());
RUN_TEST(failed, test_endpoint_close());
RUN_TEST(failed, test_driver_disconnected());
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/src/include/test_bits.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/include/test_bits.hpp b/proton-c/bindings/cpp/src/include/test_bits.hpp
index 0cfbe1f..79a0a17 100644
--- a/proton-c/bindings/cpp/src/include/test_bits.hpp
+++ b/proton-c/bindings/cpp/src/include/test_bits.hpp
@@ -54,6 +54,7 @@ inline void assert_equalish(T want, T got, T delta, const std::string& what)
test::assert_equal((WANT), (GOT), FAIL_MSG("failed ASSERT_EQUAL(" #WANT ", " #GOT ")"))
#define ASSERT_EQUALISH(WANT, GOT, DELTA) \
test::assert_equalish((WANT), (GOT), (DELTA), FAIL_MSG("failed ASSERT_EQUALISH(" #WANT ", " #GOT ")"))
+#define ASSERT_THROWS(WANT, EXPR) do { try { EXPR; FAIL("Expected " #WANT); } catch(const WANT&) {} } while(0)
#define RUN_TEST(BAD_COUNT, TEST) \
do { \
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/src/map.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/map.cpp b/proton-c/bindings/cpp/src/map.cpp
new file mode 100644
index 0000000..6fc80fa
--- /dev/null
+++ b/proton-c/bindings/cpp/src/map.cpp
@@ -0,0 +1,254 @@
+/*
+ *
+ * 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/map.hpp"
+
+#include "proton/annotation_key.hpp"
+#include "proton/scalar.hpp"
+#include "proton/value.hpp"
+#include "proton/codec/decoder.hpp"
+#include "proton/codec/encoder.hpp"
+#include "proton/codec/map.hpp"
+
+#include <map>
+#include <string>
+
+// IMPLEMENTATION NOTES:
+// - either value_, map_ or both can hold the up-to-date data
+// - avoid encoding or decoding between map_ and value_ unless necessary
+// - if (map.get()) then map_ is up to date
+// - if (!value_.empty()) then value_ is up to date
+// - if (map.get_() && !value_.empty()) then map_ and value_ have equivalent data
+// - if (!map.get_() && value_.empty()) then that's equivalent to an empty map
+// - cache() ensures that *map_ is up to date
+// - flush() ensures value_ is up to date
+
+namespace proton {
+
+// use std::map as the actual map implementation type
+template <class K, class T>
+class map_type_impl : public std::map<K, T> {};
+
+template <class K, class T>
+map<K,T>::map() {}
+
+template <class K, class T>
+map<K,T>::map(const map& x) { *this = x; }
+
+template <class K, class T>
+map<K,T>::map(pn_data_t *d) : value_(d) {}
+
+template <class K, class T>
+PN_CPP_EXTERN void swap(map<K,T>& x, map<K,T>& y) {
+ using namespace std;
+ swap(x.map_, y.map_);
+ swap(x.value_, y.value_);
+}
+
+template <class K, class T>
+void map<K,T>::ensure() const {
+ if (!map_) {
+ map_.reset(new map<K,T>::map_type);
+ }
+}
+
+template <class K, class T>
+map<K,T>& map<K,T>::operator=(const map& x) {
+ if (&x != this) {
+ if (!x.value_.empty()) {
+ map_.reset();
+ value_ = x.value_;
+ } else if (x.map_.get()) {
+ value_.clear();
+ ensure();
+ *map_ = *x.map_;
+ } else {
+ clear();
+ }
+ }
+ return *this;
+}
+
+#if PN_CPP_HAS_RVALUE_REFERENCES
+template <class K, class T>
+map<K,T>::map(map&& x) :
+ map_(std::move(x.map_)), value_(std::move(x.value_)) {}
+
+template <class K, class T>
+map<K,T>& map<K,T>::operator=(map&& x) {
+ if (&x != this) {
+ map_.reset(x.map_.release());
+ value_ = std::move(x.value_);
+ }
+ return *this;
+}
+#endif
+
+template <class K, class T>
+map<K,T>::~map() {}
+
+// Make sure map_ is valid
+template <class K, class T>
+const typename map<K,T>::map_type& map<K,T>::cache() const {
+ if (!map_) {
+ ensure();
+ if (!value_.empty()) {
+ proton::get(value_, *map_);
+ }
+ }
+ return *map_;
+}
+
+// Make sure map_ is valid, and mark value_ invalid
+template <class K, class T>
+typename map<K,T>::map_type& map<K,T>::cache_update() {
+ cache();
+ value_.clear();
+ return *map_;
+}
+
+template <class K, class T>
+value& map<K,T>::flush() const {
+ if (value_.empty()) {
+ // Create an empty map if need be, value_ must hold a valid map (even if empty)
+ // it must not be an empty (NULL_TYPE) proton::value.
+ ensure();
+ value_ = *map_;
+ }
+ return value_;
+}
+
+template <class K, class T>
+void map<K,T>::value(const proton::value& x) {
+ value_.clear();
+ // Validate the value by decoding it into map_, throw if not a valid map value.
+ ensure();
+ proton::get(x, *map_);
+}
+
+template <class K, class T>
+proton::value& map<K,T>::value() { return flush(); }
+
+template <class K, class T>
+const proton::value& map<K,T>::value() const { return flush(); }
+
+template <class K, class T>
+T map<K,T>::get(const K& k) const {
+ if (this->empty()) return T();
+ typename map_type::const_iterator i = cache().find(k);
+ if (i == map_->end()) return T();
+ return i->second;
+}
+
+template <class K, class T>
+void map<K,T>::put(const K& k, const T& v) {
+ cache_update()[k] = v;
+}
+
+template <class K, class T>
+size_t map<K,T>::erase(const K& k) {
+ if (this->empty()) {
+ return 0;
+ } else {
+ return cache_update().erase(k);
+ }
+}
+
+template <class K, class T>
+bool map<K,T>::exists(const K& k) const {
+ return this->empty() ? 0 : cache().find(k) != cache().end();
+}
+
+template <class K, class T>
+size_t map<K,T>::size() const {
+ return this->empty() ? 0 : cache().size();
+}
+
+template <class K, class T>
+void map<K,T>::clear() {
+ if (map_.get()) {
+ map_->clear();
+ }
+ value_.clear();
+}
+
+template <class K, class T>
+bool map<K,T>::empty() const {
+ if (map_.get()) {
+ return map_->empty();
+ }
+ if (value_.empty()) {
+ return true;
+ }
+ // We must decode the non-empty value to see if it is an empty map.
+ return cache().empty();
+}
+
+// Point to a different underlying pn_data_t, no copy
+template <class K, class T>
+void map<K,T>::reset(pn_data_t *d) {
+ value_.reset(d); // Points to d, not copy of d.
+ map_.reset();
+ // NOTE: for internal use. Don't verify that the data is valid here as that
+ // would forcibly decode message maps immediately, we want to decode on-demand.
+}
+
+template <class K, class T>
+PN_CPP_EXTERN proton::codec::decoder& operator>>(proton::codec::decoder& d, map<K,T>& m)
+{
+ // Decode to m.map_ rather than m.value_ to verify the data is of valid type.
+ m.value_.clear();
+ m.ensure();
+ d >> *m.map_;
+ return d;
+}
+
+template <class K, class T>
+PN_CPP_EXTERN proton::codec::encoder& operator<<(proton::codec::encoder& e, const map<K,T>& m)
+{
+ if (!m.value_.empty()) {
+ return e << m.value_; // Copy the value
+ }
+ // Encode the (possibly empty) map_.
+ m.ensure();
+ return e << *(m.map_);
+}
+
+// Force the necessary template instantiations so that the library exports the correct symbols
+template class PN_CPP_CLASS_EXTERN map<std::string, scalar>;
+typedef map<std::string, scalar> cm1;
+template PN_CPP_EXTERN void swap<>(cm1&, cm1&);
+template PN_CPP_EXTERN proton::codec::decoder& operator>> <>(proton::codec::decoder& d, cm1& m);
+template PN_CPP_EXTERN proton::codec::encoder& operator<< <>(proton::codec::encoder& e, const cm1& m);
+
+template class PN_CPP_CLASS_EXTERN map<annotation_key, value>;
+typedef map<annotation_key, value> cm2;
+template PN_CPP_EXTERN void swap<>(cm2&, cm2&);
+template PN_CPP_EXTERN proton::codec::decoder& operator>> <>(proton::codec::decoder& d, cm2& m);
+template PN_CPP_EXTERN proton::codec::encoder& operator<< <>(proton::codec::encoder& e, const cm2& m);
+
+template class PN_CPP_CLASS_EXTERN map<symbol, value>;
+typedef map<symbol, value> cm3;
+template PN_CPP_EXTERN void swap<>(cm3&, cm3&);
+template PN_CPP_EXTERN proton::codec::decoder& operator>> <>(proton::codec::decoder& d, cm3& m);
+template PN_CPP_EXTERN proton::codec::encoder& operator<< <>(proton::codec::encoder& e, const cm3& m);
+
+} // namespace proton
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/src/map_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/map_test.cpp b/proton-c/bindings/cpp/src/map_test.cpp
new file mode 100644
index 0000000..680ae5f
--- /dev/null
+++ b/proton-c/bindings/cpp/src/map_test.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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/map.hpp"
+#include "test_bits.hpp"
+
+#include <string>
+#include <vector>
+
+namespace {
+
+using namespace std;
+using namespace proton;
+
+void test_empty() {
+ proton::map<string, scalar> m;
+ ASSERT_EQUAL(0U, m.size());
+ ASSERT(m.empty());
+ ASSERT_EQUAL(0U, m.erase("x"));
+ ASSERT(!m.exists("x"));
+
+ std::map<string, scalar> sm;
+ proton::get(m, sm);
+ ASSERT(sm.empty());
+}
+
+void test_use() {
+ proton::map<string, scalar> m;
+
+ m.put("x", "y");
+ ASSERT_EQUAL(scalar("y"), m.get("x"));
+ ASSERT(!m.empty());
+ ASSERT(m.exists("x"));
+ ASSERT_EQUAL(1U, m.size());
+
+ m.put("a", "b");
+ ASSERT_EQUAL(scalar("b"), m.get("a"));
+ ASSERT_EQUAL(2U, m.size());
+
+ ASSERT_EQUAL(1U, m.erase("x"));
+ ASSERT_EQUAL(1U, m.size());
+ ASSERT(!m.exists("x"));
+ ASSERT_EQUAL(scalar("b"), m.get("a"));
+
+ m.clear();
+ ASSERT(m.empty());
+}
+
+void test_cppmap() {
+ std::map<string, scalar> sm;
+ sm["a"] = 2;
+ sm["b"] = 3;
+ proton::map<string, scalar> m;
+ m = sm;
+ ASSERT_EQUAL(scalar(2), m.get("a"));
+ ASSERT_EQUAL(scalar(3), m.get("b"));
+ ASSERT_EQUAL(2U, m.size());
+
+ std::map<string, scalar> sm2;
+ proton::get(m, sm2);
+ ASSERT_EQUAL(2U, sm2.size());
+ ASSERT_EQUAL(scalar(2), sm2["a"]);
+ ASSERT_EQUAL(scalar(3), sm2["b"]);
+
+ // Round trip:
+ value v = m.value();
+ proton::map<string, scalar> m2;
+ m2.value(v);
+
+ // Use a vector as map storage
+ vector<pair<string, scalar> > vm;
+
+ vm.push_back(std::make_pair(string("x"), 8));
+ vm.push_back(std::make_pair(string("y"), 9));
+ m.value(vm); // Can't use type-safe op=, not enabled
+ ASSERT_EQUAL(scalar(8), m.get("x"));
+ ASSERT_EQUAL(scalar(9), m.get("y"));
+ ASSERT_EQUAL(2U, m.size());
+
+ vm.clear();
+ proton::get(m, vm);
+ ASSERT_EQUAL(2U, vm.size());
+ ASSERT_EQUAL(string("x"), vm[0].first);
+ ASSERT_EQUAL(scalar(8), vm[0].second);
+}
+
+void test_value() {
+ proton::map<string, scalar> m;
+ value v;
+ v = "foo";
+ ASSERT_THROWS(conversion_error, m.value(v));
+ std::map<int, float> bad;
+ // Wrong type of map.
+ // Note we can't detect an empty map of bad type because AMQP maps allow
+ // mixed types, so there must be data to object to.
+ bad[1]=1.0;
+ ASSERT_THROWS(conversion_error, m.value(bad));
+}
+
+}
+
+int main(int, char**) {
+ int failed = 0;
+ RUN_TEST(failed, test_empty());
+ RUN_TEST(failed, test_use());
+ RUN_TEST(failed, test_cppmap());
+ RUN_TEST(failed, test_value());
+ return failed;
+}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/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 df7fae1..eb0695e 100644
--- a/proton-c/bindings/cpp/src/message.cpp
+++ b/proton-c/bindings/cpp/src/message.cpp
@@ -42,31 +42,32 @@
namespace proton {
struct message::impl {
- value body_;
- property_map application_properties_;
- annotation_map message_annotations_;
- annotation_map delivery_annotations_;
+ value body;
+ property_map properties;
+ annotation_map annotations;
+ annotation_map instructions;
impl(pn_message_t *msg) {
- body_.reset(pn_message_body(msg));
+ body.reset(pn_message_body(msg));
+ properties.reset(pn_message_properties(msg));
+ annotations.reset(pn_message_annotations(msg));
+ instructions.reset(pn_message_instructions(msg));
}
void clear() {
- application_properties_.clear();
- message_annotations_.clear();
- delivery_annotations_.clear();
+ properties.clear();
+ annotations.clear();
+ instructions.clear();
}
- friend void swap(impl& x, impl& y) {
- using std::swap;
- swap(x.body_, y.body_);
- swap(x.application_properties_, y.application_properties_);
- swap(x.message_annotations_, y.message_annotations_);
- swap(x.delivery_annotations_, y.delivery_annotations_);
- }
+ // Encode cached maps back to the underlying pn_data_t
+ void flush() {
+ properties.value();
+ annotations.value();
+ instructions.value();
+ }
};
-
message::message() : pn_msg_(0) {}
message::message(const message &m) : pn_msg_(0) { *this = m; }
@@ -88,9 +89,7 @@ message::~message() {
}
void swap(message& x, message& y) {
- using std::swap;
- swap(x.pn_msg_, y.pn_msg_);
- swap(x.impl(), y.impl());
+ std::swap(x.pn_msg_, y.pn_msg_);
}
pn_message_t *message::pn_msg() const {
@@ -116,7 +115,12 @@ message& message::operator=(const message& m) {
return *this;
}
-void message::clear() { if (pn_msg_) pn_message_clear(pn_msg_); }
+void message::clear() {
+ if (pn_msg_) {
+ impl().clear();
+ pn_message_clear(pn_msg_);
+ }
+}
namespace {
void check(int err) {
@@ -238,65 +242,35 @@ void message::inferred(bool b) { pn_message_set_inferred(pn_msg(), b); }
void message::body(const value& x) { body() = x; }
-const value& message::body() const { return impl().body_; }
-value& message::body() { return impl().body_; }
-
-// MAP CACHING: the properties and annotations 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.
-
-// Decode a map on demand
-template<class M, class F> M& get_map(pn_message_t* msg, F get, M& map) {
- codec::decoder d(make_wrapper(get(msg)));
- if (map.empty() && !d.empty()) {
- d.rewind();
- d >> map;
- d.clear(); // The map member is now the authority.
- }
- return map;
-}
-
-// Encode a map if necessary.
-template<class M, class F> M& put_map(pn_message_t* msg, F get, M& map) {
- codec::encoder e(make_wrapper(get(msg)));
- if (e.empty() && !map.empty()) {
- e << map;
- map.clear(); // The encoded pn_data_t is now the authority.
- }
- return map;
-}
+const value& message::body() const { return impl().body; }
+value& message::body() { return impl().body; }
message::property_map& message::properties() {
- return get_map(pn_msg(), pn_message_properties, impl().application_properties_);
+ return impl().properties;
}
const message::property_map& message::properties() const {
- return get_map(pn_msg(), pn_message_properties, impl().application_properties_);
+ return impl().properties;
}
-
message::annotation_map& message::message_annotations() {
- return get_map(pn_msg(), pn_message_annotations, impl().message_annotations_);
+ return impl().annotations;
}
const message::annotation_map& message::message_annotations() const {
- return get_map(pn_msg(), pn_message_annotations, impl().message_annotations_);
+ return impl().annotations;
}
-
message::annotation_map& message::delivery_annotations() {
- return get_map(pn_msg(), pn_message_instructions, impl().delivery_annotations_);
+ return impl().instructions;
}
const message::annotation_map& message::delivery_annotations() const {
- return get_map(pn_msg(), pn_message_instructions, impl().delivery_annotations_);
+ return impl().instructions;
}
void message::encode(std::vector<char> &s) const {
- put_map(pn_msg(), pn_message_properties, impl().application_properties_);
- put_map(pn_msg(), pn_message_annotations, impl().message_annotations_);
- put_map(pn_msg(), pn_message_instructions, impl().delivery_annotations_);
+ impl().flush();
size_t sz = std::max(s.capacity(), size_t(512));
while (true) {
s.resize(sz);
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/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 4d4e239..eafea2e 100644
--- a/proton-c/bindings/cpp/src/message_test.cpp
+++ b/proton-c/bindings/cpp/src/message_test.cpp
@@ -139,14 +139,16 @@ void test_message_maps() {
m.properties().put("foo", 12);
m.delivery_annotations().put("bar", "xyz");
-
m.message_annotations().put(23, "23");
+
ASSERT_EQUAL(m.properties().get("foo"), scalar(12));
ASSERT_EQUAL(m.delivery_annotations().get("bar"), scalar("xyz"));
ASSERT_EQUAL(m.message_annotations().get(23), scalar("23"));
message m2(m);
+ ASSERT_EQUAL(m.properties().get("foo"), scalar(12)); // Decoding shouldn't change it
+
ASSERT_EQUAL(m2.properties().get("foo"), scalar(12));
ASSERT_EQUAL(m2.delivery_annotations().get("bar"), scalar("xyz"));
ASSERT_EQUAL(m2.message_annotations().get(23), scalar("23"));
@@ -155,13 +157,14 @@ void test_message_maps() {
m.delivery_annotations().put(24, 1000);
m.message_annotations().erase(23);
- m2 = m;
- ASSERT_EQUAL(1u, m2.properties().size());
- ASSERT_EQUAL(m2.properties().get("foo"), scalar("newfoo"));
- ASSERT_EQUAL(2u, m2.delivery_annotations().size());
- ASSERT_EQUAL(m2.delivery_annotations().get("bar"), scalar("xyz"));
- ASSERT_EQUAL(m2.delivery_annotations().get(24), scalar(1000));
- ASSERT(m2.message_annotations().empty());
+ message m3 = m;
+ size_t size = m3.properties().size();
+ ASSERT_EQUAL(1u, size);
+ ASSERT_EQUAL(m3.properties().get("foo"), scalar("newfoo"));
+ ASSERT_EQUAL(2u, m3.delivery_annotations().size());
+ ASSERT_EQUAL(m3.delivery_annotations().get("bar"), scalar("xyz"));
+ ASSERT_EQUAL(m3.delivery_annotations().get(24), scalar(1000));
+ ASSERT(m3.message_annotations().empty());
}
}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/src/node_options.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/node_options.cpp b/proton-c/bindings/cpp/src/node_options.cpp
index 5bb2f8e..894ce9c 100644
--- a/proton-c/bindings/cpp/src/node_options.cpp
+++ b/proton-c/bindings/cpp/src/node_options.cpp
@@ -103,8 +103,7 @@ class source_options::impl {
pn_terminus_set_distribution_mode(unwrap(s), pn_distribution_mode_t(distribution_mode.value));
if (filters.set && !filters.value.empty()) {
// Applied at most once via source_option. No need to clear.
- codec::encoder e(make_wrapper(pn_terminus_filter(unwrap(s))));
- e << filters.value;
+ value(pn_terminus_filter(unwrap(s))) = filters.value;
}
}
};
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/src/scalar_base.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/scalar_base.cpp b/proton-c/bindings/cpp/src/scalar_base.cpp
index 840a6e8..0d66cdc 100644
--- a/proton-c/bindings/cpp/src/scalar_base.cpp
+++ b/proton-c/bindings/cpp/src/scalar_base.cpp
@@ -147,8 +147,7 @@ bool operator<(const scalar_base& x, const scalar_base& y) {
std::ostream& operator<<(std::ostream& o, const scalar_base& s) {
switch (s.type()) {
- case NULL_TYPE: return o; // NULL is empty, doesn't print (like empty string)
- // Print byte types as integer, not char.
+ case NULL_TYPE: return o << "<null>";
case BYTE: return o << static_cast<int>(internal::get<int8_t>(s));
case UBYTE: return o << static_cast<unsigned int>(internal::get<uint8_t>(s));
// Other types printed using normal C++ operator <<
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/src/source.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/source.cpp b/proton-c/bindings/cpp/src/source.cpp
index a407d0b..524428f 100644
--- a/proton-c/bindings/cpp/src/source.cpp
+++ b/proton-c/bindings/cpp/src/source.cpp
@@ -30,11 +30,21 @@
namespace proton {
// Set parent_ non-null when the local terminus is authoritative and may need to be looked up.
-source::source(pn_terminus_t *t) : terminus(make_wrapper(t)) {}
+source::source(pn_terminus_t *t) : terminus(make_wrapper(t)),
+ filters_(pn_terminus_filter(object_))
+{}
-source::source(const sender& snd) : terminus(make_wrapper(pn_link_remote_source(unwrap(snd)))) { parent_ = unwrap(snd); }
+source::source(const sender& snd) :
+ terminus(make_wrapper(pn_link_remote_source(unwrap(snd)))),
+ filters_(pn_terminus_filter(object_))
+{
+ parent_ = unwrap(snd);
+}
-source::source(const receiver& rcv) : terminus(make_wrapper(pn_link_remote_source(unwrap(rcv)))) {}
+source::source(const receiver& rcv) :
+ terminus(make_wrapper(pn_link_remote_source(unwrap(rcv)))),
+ filters_(pn_terminus_filter(object_))
+{}
std::string source::address() const {
pn_terminus_t *authoritative = object_;
@@ -47,14 +57,8 @@ enum source::distribution_mode source::distribution_mode() const {
return (enum distribution_mode)pn_terminus_get_distribution_mode(object_);
}
-source::filter_map source::filters() const {
- codec::decoder d(make_wrapper(pn_terminus_filter(object_)));
- filter_map map;
- if (!d.empty()) {
- d.rewind();
- d >> map;
- }
- return map;
+const source::filter_map& source::filters() const {
+ return filters_;
}
}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/864519dd/proton-c/bindings/cpp/src/value.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/value.cpp b/proton-c/bindings/cpp/src/value.cpp
index edf9074..03fc224 100644
--- a/proton-c/bindings/cpp/src/value.cpp
+++ b/proton-c/bindings/cpp/src/value.cpp
@@ -183,6 +183,9 @@ bool operator<(const value& x, const value& y) {
}
std::ostream& operator<<(std::ostream& o, const value& x) {
+ if (x.empty()) {
+ return o << "<empty-value>";
+ }
if (type_id_is_scalar(x.type()) || x.empty())
return o << proton::get<scalar>(x); // Print as a scalar
// Use pn_inspect for complex types.
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org