You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2015/12/17 21:36:26 UTC

qpid-proton git commit: NO-JIRA: cpp: Add proton::atom type to efficiently represent arbitrary atomic types.

Repository: qpid-proton
Updated Branches:
  refs/heads/master d3fe232c9 -> 02d6ba686


NO-JIRA: cpp: Add proton::atom type to efficiently represent arbitrary atomic types.

The type is not yet used but is unit tested.
This type will be used for message maps that are restricted to have atomic types.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/02d6ba68
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/02d6ba68
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/02d6ba68

Branch: refs/heads/master
Commit: 02d6ba686dba7f29f98d2f52503ca820b9b9cac9
Parents: d3fe232
Author: Alan Conway <ac...@redhat.com>
Authored: Wed Dec 16 17:46:27 2015 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Thu Dec 17 15:33:52 2015 -0500

----------------------------------------------------------------------
 proton-c/bindings/cpp/CMakeLists.txt            |   2 +
 proton-c/bindings/cpp/include/proton/atom.hpp   | 122 ++++++++++++
 proton-c/bindings/cpp/include/proton/data.hpp   |   2 +-
 .../bindings/cpp/include/proton/type_traits.hpp |   9 +-
 proton-c/bindings/cpp/include/proton/types.hpp  |  30 ++-
 proton-c/bindings/cpp/src/atom.cpp              | 184 +++++++++++++++++++
 proton-c/bindings/cpp/src/atom_test.cpp         | 105 +++++++++++
 proton-c/bindings/cpp/src/data.cpp              |   2 +-
 proton-c/bindings/cpp/src/decoder.cpp           |   2 +-
 .../bindings/cpp/src/encode_decode_test.cpp     |   4 +-
 proton-c/bindings/cpp/src/interop_test.cpp      |   8 +-
 proton-c/bindings/cpp/src/message_test.cpp      |   2 +-
 proton-c/bindings/cpp/src/test_bits.hpp         |  25 ++-
 proton-c/bindings/cpp/src/types.cpp             |  37 +++-
 14 files changed, 499 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/proton-c/bindings/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/CMakeLists.txt b/proton-c/bindings/cpp/CMakeLists.txt
index 6352f66..78df0b5 100644
--- a/proton-c/bindings/cpp/CMakeLists.txt
+++ b/proton-c/bindings/cpp/CMakeLists.txt
@@ -26,6 +26,7 @@ include_directories(
 
 set(qpid-proton-cpp-source
   src/acceptor.cpp
+  src/atom.cpp
   src/blocking_connection.cpp
   src/blocking_connection_impl.cpp
   src/blocking_fetcher.cpp
@@ -169,3 +170,4 @@ endmacro(add_cpp_test)
 add_cpp_test(interop_test ${CMAKE_SOURCE_DIR}/tests)
 add_cpp_test(message_test)
 add_cpp_test(encode_decode_test)
+add_cpp_test(atom_test)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/proton-c/bindings/cpp/include/proton/atom.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/atom.hpp b/proton-c/bindings/cpp/include/proton/atom.hpp
new file mode 100644
index 0000000..ce2a385
--- /dev/null
+++ b/proton-c/bindings/cpp/include/proton/atom.hpp
@@ -0,0 +1,122 @@
+#ifndef ATOM_HPP
+#define ATOM_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 <iosfwd>
+
+namespace proton {
+
+class atom;
+
+/** atom holds an instance of an atomic proton type. */
+class atom : public comparable<atom> {
+  public:
+    PN_CPP_EXTERN atom();
+    // Use default assign and copy.
+
+    /// Type for the value in the atom, NULL_TYPE if empty()
+    PN_CPP_EXTERN type_id type() const;
+    /// True if the atom is empty.
+    PN_CPP_EXTERN bool empty() const;
+
+    ///@name Create an atom, type() is deduced from the C++ type of the value.
+    ///@{
+    PN_CPP_EXTERN explicit atom(bool);
+    PN_CPP_EXTERN explicit atom(uint8_t);
+    PN_CPP_EXTERN explicit atom(int8_t);
+    PN_CPP_EXTERN explicit atom(uint16_t);
+    PN_CPP_EXTERN explicit atom(int16_t);
+    PN_CPP_EXTERN explicit atom(uint32_t);
+    PN_CPP_EXTERN explicit atom(int32_t);
+    PN_CPP_EXTERN explicit atom(uint64_t);
+    PN_CPP_EXTERN explicit atom(int64_t);
+    PN_CPP_EXTERN explicit atom(wchar_t);
+    PN_CPP_EXTERN explicit atom(float);
+    PN_CPP_EXTERN explicit atom(double);
+    PN_CPP_EXTERN explicit atom(amqp_timestamp);
+    PN_CPP_EXTERN explicit atom(const amqp_decimal32&);
+    PN_CPP_EXTERN explicit atom(const amqp_decimal64&);
+    PN_CPP_EXTERN explicit atom(const amqp_decimal128&);
+    PN_CPP_EXTERN explicit atom(const amqp_uuid&);
+    PN_CPP_EXTERN explicit atom(const amqp_string&);
+    PN_CPP_EXTERN explicit atom(const amqp_symbol&);
+    PN_CPP_EXTERN explicit atom(const amqp_binary&);
+    PN_CPP_EXTERN explicit atom(const std::string& s); ///< Treated as an AMQP string
+    PN_CPP_EXTERN explicit atom(const char* s);        ///< Treated as an AMQP string
+    ///@}
+
+    /// Assign to an atom using the same rules as construction.
+    template <class T> atom& operator=(T x) { return *this = atom(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.
+    ///@{
+    PN_CPP_EXTERN void get(bool&) const;
+    PN_CPP_EXTERN void get(uint8_t&) const;
+    PN_CPP_EXTERN void get(int8_t&) const;
+    PN_CPP_EXTERN void get(uint16_t&) const;
+    PN_CPP_EXTERN void get(int16_t&) const;
+    PN_CPP_EXTERN void get(uint32_t&) const;
+    PN_CPP_EXTERN void get(int32_t&) const;
+    PN_CPP_EXTERN void get(uint64_t&) const;
+    PN_CPP_EXTERN void get(int64_t&) const;
+    PN_CPP_EXTERN void get(wchar_t&) const;
+    PN_CPP_EXTERN void get(float&) const;
+    PN_CPP_EXTERN void get(double&) const;
+    PN_CPP_EXTERN void get(amqp_timestamp&) const;
+    PN_CPP_EXTERN void get(amqp_decimal32&) const;
+    PN_CPP_EXTERN void get(amqp_decimal64&) const;
+    PN_CPP_EXTERN void get(amqp_decimal128&) const;
+    PN_CPP_EXTERN void get(amqp_uuid&) const;
+    PN_CPP_EXTERN void get(amqp_string&) const;
+    PN_CPP_EXTERN void get(amqp_symbol&) const;
+    PN_CPP_EXTERN void get(amqp_binary&) const;
+    PN_CPP_EXTERN void get(std::string&) const; ///< Treated as an AMQP string
+    ///@}
+
+    ///@ get<T>() is like get(T&) but returns the value..
+    template<class T> T get() const { T x; get(x); return x; }
+
+    ///@name as_ methods do "loose" conversion, they will convert the atom's
+    ///value to the requested type if possible, else throw type_mismatch
+    ///@{
+    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 bool operator==(const atom& x) const;
+    /// Note if the values are of different type(), operator< will compare the type()
+    PN_CPP_EXTERN bool operator<(const atom& x) const;
+
+  PN_CPP_EXTERN friend std::ostream& operator<<(std::ostream&, const atom&);
+
+  private:
+    void ok(pn_type_t) const;
+    void set(const std::string&);
+    pn_atom_t atom_;
+    std::string str_;           // Owner of string-like data.
+};
+
+}
+#endif // ATOM_HPP

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/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 84d5ee4..5ffd93d 100644
--- a/proton-c/bindings/cpp/include/proton/data.hpp
+++ b/proton-c/bindings/cpp/include/proton/data.hpp
@@ -40,7 +40,7 @@ class data : public object<pn_data_t> {
   public:
     data(pn_data_t* d) : object<pn_data_t>(d) {}
 
-    data& operator=(const data&);      // FIXME aconway 2015-12-01:
+    data& operator=(const data&);
 
     PN_CPP_EXTERN static data create();
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/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 d12e0ed..494f7db 100644
--- a/proton-c/bindings/cpp/include/proton/type_traits.hpp
+++ b/proton-c/bindings/cpp/include/proton/type_traits.hpp
@@ -76,9 +76,9 @@ template <class T> struct is_same<T,T> { static const bool value=true; };
 template< class T > struct remove_const          { typedef T type; };
 template< class T > struct remove_const<const T> { typedef T type; };
 
-// Metafunction returning AMQP type for basic C++ types
+// Metafunction returning AMQP type for atomic C++ types
 template <class T, class Enable=void> struct type_id_of;
-template<> struct type_id_of<amqp_null> { static const type_id value=NULL_; };
+template<> struct type_id_of<amqp_null> { static const type_id value=NULL_TYPE; };
 template<> struct type_id_of<amqp_boolean> { static const type_id value=BOOLEAN; };
 template<> struct type_id_of<amqp_ubyte> { static const type_id value=UBYTE; };
 template<> struct type_id_of<amqp_byte> { static const type_id value=BYTE; };
@@ -105,8 +105,8 @@ template <class T> struct has_type_id<T, typename enable_if<!!type_id_of<T>::val
     static const bool value = true;
 };
 
-// Map arbitrary integral types to know AMQP integral types.
-template<size_t N, bool S> struct integer_type;
+// Map arbitrary integral types to known AMQP integral types.
+template<size_t SIZE, bool IS_SIGNED> struct integer_type;
 template<> struct integer_type<1, true> { typedef amqp_byte type; };
 template<> struct integer_type<2, true> { typedef amqp_short type; };
 template<> struct integer_type<4, true> { typedef amqp_int type; };
@@ -121,6 +121,7 @@ template <class T> struct is_unknown_integer {
     static const bool value = !has_type_id<T>::value && is_integral<T>::value;
 };
 
+
 }
 ///@endcond
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/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 b9fedd1..262f352 100644
--- a/proton-c/bindings/cpp/include/proton/types.hpp
+++ b/proton-c/bindings/cpp/include/proton/types.hpp
@@ -24,6 +24,7 @@
  */
 
 #include "proton/export.hpp"
+#include "proton/error.hpp"
 #include "proton/comparable.hpp"
 
 #include <proton/codec.h>
@@ -39,7 +40,7 @@ namespace proton {
 
 /** type_id identifies an AMQP type. */
 enum type_id {
-    NULL_=PN_NULL,              ///< The null type, contains no data.
+    NULL_TYPE=PN_NULL,          ///< The null type, contains no data.
     BOOLEAN=PN_BOOL,            ///< Boolean true or false.
     UBYTE=PN_UBYTE,             ///< Unsigned 8 bit integer.
     BYTE=PN_BYTE,               ///< Signed 8 bit integer.
@@ -66,6 +67,13 @@ enum type_id {
     MAP=PN_MAP                  ///< A sequence of key:value pairs, may be of mixed types.
 };
 
+/// 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()) throw();
+    type_id want; ///< Expected type_id
+    type_id got;  ///< Actual type_id
+};
+
 PN_CPP_EXTERN pn_bytes_t pn_bytes(const std::string&);
 PN_CPP_EXTERN std::string str(const pn_bytes_t& b);
 ///@endcond
@@ -144,10 +152,13 @@ typedef opaque<pn_uuid_t> amqp_uuid;
 PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const amqp_uuid&);
 /// AMQP 32-bit decimal floating point (IEEE 854).
 typedef opaque<pn_decimal32_t> amqp_decimal32;
+PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const amqp_decimal32&);
 /// AMQP 64-bit decimal floating point (IEEE 854).
 typedef opaque<pn_decimal64_t> amqp_decimal64;
+PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const amqp_decimal64&);
 /// AMQP 128-bit decimal floating point (IEEE 854).
 typedef opaque<pn_decimal128_t> amqp_decimal128;
+PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const amqp_decimal128&);
 
 /// AMQP timestamp, milliseconds since the epoch 00:00:00 (UTC), 1 January 1970.
 struct amqp_timestamp : public comparable<amqp_timestamp> {
@@ -157,6 +168,7 @@ struct amqp_timestamp : public comparable<amqp_timestamp> {
     bool operator==(const amqp_timestamp& x) { return milliseconds == x.milliseconds; }
     bool operator<(const amqp_timestamp& x) { return milliseconds < x.milliseconds; }
 };
+PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const amqp_timestamp&);
 
 ///@cond INTERNAL
 template<class T, type_id A> struct cref {
@@ -180,9 +192,21 @@ template <type_id A, class T> cref<T, A> as(const T& value) { return cref<T, A>(
 
 // TODO aconway 2015-06-16: described types.
 
-/** Return the name of a type. */
+/// 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.
+///@{
+PN_CPP_EXTERN bool type_id_atom(type_id);
+PN_CPP_EXTERN bool type_id_integral(type_id);
+PN_CPP_EXTERN bool type_id_signed(type_id); ///< CHAR and BOOL are not signed.
+PN_CPP_EXTERN bool type_id_floating_point(type_id);
+PN_CPP_EXTERN bool type_id_decimal(type_id);
+PN_CPP_EXTERN bool type_id_string_like(type_id);   ///< STRING, SYMBOL, BINARY
+PN_CPP_EXTERN bool type_id_container(type_id);
+///@}
+
 /** Print the name of a type. */
 PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, type_id);
 
@@ -192,7 +216,7 @@ PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, type_id);
  * for examples of use.
  */
 struct start {
-    PN_CPP_EXTERN start(type_id type=NULL_, type_id element=NULL_, bool described=false, size_t size=0);
+    PN_CPP_EXTERN start(type_id type=NULL_TYPE, type_id element=NULL_TYPE, bool described=false, size_t size=0);
     type_id type;            ///< The container type: ARRAY, LIST, MAP or DESCRIBED.
     type_id element;         ///< the element type for array only.
     bool is_described;       ///< true if first value is a descriptor.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/proton-c/bindings/cpp/src/atom.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/atom.cpp b/proton-c/bindings/cpp/src/atom.cpp
new file mode 100644
index 0000000..64d3c88
--- /dev/null
+++ b/proton-c/bindings/cpp/src/atom.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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/atom.hpp"
+#include "proton/type_traits.hpp"
+
+#include <ostream>
+
+namespace proton {
+
+atom::atom() { atom_.type = PN_NULL; }
+
+type_id atom::type() const { return type_id(atom_.type); }
+bool atom::empty() const { return type() == NULL_TYPE; }
+
+atom::atom(bool x) { atom_.u.as_bool = x; atom_.type = PN_BOOL; }
+atom::atom(uint8_t x) { atom_.u.as_ubyte = x; atom_.type = PN_UBYTE; }
+atom::atom(int8_t x) { atom_.u.as_byte = x; atom_.type = PN_BYTE; }
+atom::atom(uint16_t x) { atom_.u.as_ushort = x; atom_.type = PN_USHORT; }
+atom::atom(int16_t x) { atom_.u.as_short = x; atom_.type = PN_SHORT; }
+atom::atom(uint32_t x) { atom_.u.as_uint = x; atom_.type = PN_UINT; }
+atom::atom(int32_t x) { atom_.u.as_int = x; atom_.type = PN_INT; }
+atom::atom(uint64_t x) { atom_.u.as_ulong = x; atom_.type = PN_ULONG; }
+atom::atom(int64_t x) { atom_.u.as_long = x; atom_.type = PN_LONG; }
+atom::atom(wchar_t x) { atom_.u.as_char = x; atom_.type = PN_CHAR; }
+atom::atom(float x) { atom_.u.as_float = x; atom_.type = PN_FLOAT; }
+atom::atom(double x) { atom_.u.as_double = x; atom_.type = PN_DOUBLE; }
+atom::atom(amqp_timestamp x) { atom_.u.as_timestamp = x; atom_.type = PN_TIMESTAMP; }
+atom::atom(const amqp_decimal32& x) { atom_.u.as_decimal32 = x; atom_.type = PN_DECIMAL32; }
+atom::atom(const amqp_decimal64& x) { atom_.u.as_decimal64 = x; atom_.type = PN_DECIMAL64; }
+atom::atom(const amqp_decimal128& x) { atom_.u.as_decimal128 = x; atom_.type = PN_DECIMAL128; }
+atom::atom(const amqp_uuid& x) { atom_.u.as_uuid = x; atom_.type = PN_UUID; }
+
+void atom::set(const std::string& x) { str_ = x; atom_.u.as_bytes = pn_bytes(str_); }
+atom::atom(const amqp_string& x) { set(x); atom_.type = PN_STRING; }
+atom::atom(const amqp_symbol& x) { set(x); atom_.type = PN_SYMBOL; }
+atom::atom(const amqp_binary& x) { set(x); atom_.type = PN_BINARY; }
+atom::atom(const std::string& x) { *this = amqp_string(x); }
+atom::atom(const char* x) { *this = amqp_string(x); }
+
+void atom::ok(pn_type_t t) const {
+    if (atom_.type != t) throw type_mismatch(type_id(t), type());
+}
+
+void atom::get(bool& x) const { ok(PN_BOOL); x = atom_.u.as_bool; }
+void atom::get(uint8_t& x) const { ok(PN_UBYTE); x = atom_.u.as_ubyte; }
+void atom::get(int8_t& x) const { ok(PN_BYTE); x = atom_.u.as_byte; }
+void atom::get(uint16_t& x) const { ok(PN_USHORT); x = atom_.u.as_ushort; }
+void atom::get(int16_t& x) const { ok(PN_SHORT); x = atom_.u.as_short; }
+void atom::get(uint32_t& x) const { ok(PN_UINT); x = atom_.u.as_uint; }
+void atom::get(int32_t& x) const { ok(PN_INT); x = atom_.u.as_int; }
+void atom::get(wchar_t& x) const { ok(PN_CHAR); x = atom_.u.as_char; }
+void atom::get(uint64_t& x) const { ok(PN_ULONG); x = atom_.u.as_ulong; }
+void atom::get(int64_t& x) const { ok(PN_LONG); x = atom_.u.as_long; }
+void atom::get(amqp_timestamp& x) const { ok(PN_TIMESTAMP); x = atom_.u.as_timestamp; }
+void atom::get(float& x) const { ok(PN_FLOAT); x = atom_.u.as_float; }
+void atom::get(double& x) const { ok(PN_DOUBLE); x = atom_.u.as_double; }
+void atom::get(amqp_decimal32& x) const { ok(PN_DECIMAL32); x = atom_.u.as_decimal32; }
+void atom::get(amqp_decimal64& x) const { ok(PN_DECIMAL64); x = atom_.u.as_decimal64; }
+void atom::get(amqp_decimal128& x) const { ok(PN_DECIMAL128); x = atom_.u.as_decimal128; }
+void atom::get(amqp_uuid& x) const { ok(PN_UUID); x = atom_.u.as_uuid; }
+void atom::get(amqp_string& x) const { ok(PN_STRING); x = str_; }
+void atom::get(amqp_symbol& x) const { ok(PN_SYMBOL); x = str_; }
+void atom::get(amqp_binary& x) const { ok(PN_BINARY); x = str_; }
+void atom::get(std::string& x) const { x = get<amqp_string>(); }
+
+int64_t atom::as_int() const {
+    if (type_id_floating_point(type())) return as_double();
+    switch (atom_.type) {
+      case PN_BOOL: return atom_.u.as_bool;
+      case PN_UBYTE: return atom_.u.as_ubyte;
+      case PN_BYTE: return atom_.u.as_byte;
+      case PN_USHORT: return atom_.u.as_ushort;
+      case PN_SHORT: return atom_.u.as_short;
+      case PN_UINT: return atom_.u.as_uint;
+      case PN_INT: return atom_.u.as_int;
+      case PN_CHAR: return atom_.u.as_char;
+      case PN_ULONG: return atom_.u.as_ulong;
+      case PN_LONG: return atom_.u.as_long;
+      default: throw type_mismatch(LONG, type(), "cannot convert");
+    }
+}
+
+uint64_t atom::as_uint() const {
+    if  (!type_id_integral(type()))
+        throw type_mismatch(ULONG, type(), "cannot convert");
+    return uint64_t(as_int());
+}
+
+double atom::as_double() const {
+    if (type_id_integral(type())) {
+        return type_id_signed(type()) ? double(as_int()) : double(as_uint());
+    }
+    switch (atom_.type) {
+      case PN_DOUBLE: return atom_.u.as_double;
+      case PN_FLOAT: return atom_.u.as_float;
+      default: throw type_mismatch(DOUBLE, type(), "cannot convert");
+    }
+}
+
+std::string atom::as_string() const {
+    if (type_id_string_like(type()))
+        return str_;
+    throw type_mismatch(DOUBLE, type(), "cannot convert");
+}
+
+
+namespace {
+template <class T, class F> T type_switch(const atom& a, F f) {
+    switch(a.type()) {
+      case BOOLEAN: return f(a.get<bool>());
+      case UBYTE: return f(a.get<uint8_t>());
+      case BYTE: return f(a.get<int8_t>());
+      case USHORT: return f(a.get<uint16_t>());
+      case SHORT: return f(a.get<int16_t>());
+      case UINT: return f(a.get<uint32_t>());
+      case INT: return f(a.get<int32_t>());
+      case CHAR: return f(a.get<wchar_t>());
+      case ULONG: return f(a.get<uint64_t>());
+      case LONG: return f(a.get<int64_t>());
+      case TIMESTAMP: return f(a.get<amqp_timestamp>());
+      case FLOAT: return f(a.get<float>());
+      case DOUBLE: return f(a.get<double>());
+      case DECIMAL32: return f(a.get<amqp_decimal32>());
+      case DECIMAL64: return f(a.get<amqp_decimal64>());
+      case DECIMAL128: return f(a.get<amqp_decimal128>());
+      case UUID: return f(a.get<amqp_uuid>());
+      case BINARY: return f(a.get<amqp_binary>());
+      case STRING: return f(a.get<amqp_string>());
+      case SYMBOL: return f(a.get<amqp_symbol>());
+      default:
+        throw error("bad atom type");
+    }
+}
+
+struct equal_op {
+    const atom& a;
+    equal_op(const atom& a_) : a(a_) {}
+    template<class T> bool operator()(T x) { return x == a.get<T>(); }
+};
+
+struct less_op {
+    const atom& a;
+    less_op(const atom& a_) : a(a_) {}
+    template<class T> bool operator()(T x) { return x < a.get<T>(); }
+};
+
+struct ostream_op {
+    std::ostream& o;
+    ostream_op(std::ostream& o_) : o(o_) {}
+    template<class T> std::ostream& operator()(T x) { return o << x; }
+};
+
+} // namespace
+
+bool atom::operator==(const atom& x) const {
+    return type_switch<bool>(*this, equal_op(x));
+}
+
+bool atom::operator<(const atom& x) const {
+    return type_switch<bool>(*this, less_op(x));
+}
+
+std::ostream& operator<<(std::ostream& o, const atom& a) {
+    return type_switch<std::ostream&>(a, ostream_op(o));
+}
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/proton-c/bindings/cpp/src/atom_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/atom_test.cpp b/proton-c/bindings/cpp/src/atom_test.cpp
new file mode 100644
index 0000000..0d85a36
--- /dev/null
+++ b/proton-c/bindings/cpp/src/atom_test.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "test_bits.hpp"
+#include "proton/type_traits.hpp"
+
+#include <proton/atom.hpp>
+
+using namespace std;
+using namespace proton;
+
+// Inserting and extracting simple C++ values.
+template <class T> void type_test(T x, type_id tid, T y) {
+    atom v(x);
+    ASSERT_EQUAL(tid, v.type());
+    ASSERT(!v.empty());
+    ASSERT_EQUAL(x, v.get<T>());
+
+    atom v2;
+    ASSERT(v2.type() == NULL_TYPE);
+    v2 = x;
+    ASSERT_EQUAL(tid, v2.type());
+    ASSERT_EQUAL(x, v2.get<T>());
+    ASSERT_EQUAL(v, v2);
+    ASSERT_EQUAL(str(x), str(v));
+
+    v2 = y;
+    ASSERT(v != v2);
+    ASSERT(v < v2);
+    ASSERT(v2 > v);
+}
+
+#define ASSERT_MISMATCH(EXPR) \
+    try { (void)(EXPR); FAIL("expected type_mismatch: " #EXPR); } catch (type_mismatch) {}
+
+void convert_test() {
+    atom a;
+    ASSERT_EQUAL(NULL_TYPE, a.type());
+    ASSERT(a.empty());
+    ASSERT_MISMATCH(a.get<float>());
+
+    a = amqp_binary("foo");
+    ASSERT_MISMATCH(a.get<int16_t>());
+    ASSERT_MISMATCH(a.as_int());
+    ASSERT_MISMATCH(a.as_double());
+    ASSERT_MISMATCH(a.get<amqp_string>());           // No strict conversion
+    ASSERT_EQUAL(a.as_string(), std::string("foo")); // OK string-like conversion
+
+    a = int16_t(42);
+    ASSERT_MISMATCH(a.get<std::string>());
+    ASSERT_MISMATCH(a.get<amqp_timestamp>());
+    ASSERT_MISMATCH(a.as_string());
+    ASSERT_EQUAL(a.as_int(), 42);
+    ASSERT_EQUAL(a.as_uint(), 42);
+    ASSERT_EQUAL(a.as_double(), 42);
+
+    a = int16_t(-42);
+    ASSERT_EQUAL(a.as_int(), -42);
+    ASSERT_EQUAL(a.as_uint(), uint64_t(-42));
+    ASSERT_EQUAL(a.as_double(), -42);
+}
+
+int main(int, char**) {
+    int failed = 0;
+    RUN_TEST(failed, type_test(false, BOOLEAN, true));
+    RUN_TEST(failed, type_test(amqp_ubyte(42), UBYTE, amqp_ubyte(50)));
+    RUN_TEST(failed, type_test(amqp_byte('x'), BYTE, amqp_byte('y')));
+    RUN_TEST(failed, type_test(amqp_ushort(4242), USHORT, amqp_ushort(5252)));
+    RUN_TEST(failed, type_test(amqp_short(-4242), SHORT, amqp_short(3)));
+    RUN_TEST(failed, type_test(amqp_uint(4242), UINT, amqp_uint(5252)));
+    RUN_TEST(failed, type_test(amqp_int(-4242), INT, amqp_int(3)));
+    RUN_TEST(failed, type_test(amqp_ulong(4242), ULONG, amqp_ulong(5252)));
+    RUN_TEST(failed, type_test(amqp_long(-4242), LONG, amqp_long(3)));
+    RUN_TEST(failed, type_test(wchar_t(23), CHAR, wchar_t(24)));
+    RUN_TEST(failed, type_test(amqp_float(1.234), FLOAT, amqp_float(2.345)));
+    RUN_TEST(failed, type_test(amqp_double(11.2233), DOUBLE, amqp_double(12)));
+    RUN_TEST(failed, type_test(amqp_timestamp(0), TIMESTAMP, amqp_timestamp(1)));
+    RUN_TEST(failed, type_test(amqp_decimal32(0), DECIMAL32, amqp_decimal32(1)));
+    RUN_TEST(failed, type_test(amqp_decimal64(0), DECIMAL64, amqp_decimal64(1)));
+    pn_decimal128_t da = {0}, db = {1};
+    RUN_TEST(failed, type_test(amqp_decimal128(da), DECIMAL128, amqp_decimal128(db)));
+    pn_uuid_t ua = {0}, ub = {1};
+    RUN_TEST(failed, type_test(amqp_uuid(ua), UUID, amqp_uuid(ub)));
+    RUN_TEST(failed, type_test(amqp_string("aaa"), STRING, amqp_string("aaaa")));
+    RUN_TEST(failed, type_test(amqp_symbol("aaa"), SYMBOL, amqp_symbol("aaaa")));
+    RUN_TEST(failed, type_test(amqp_binary("aaa"), BINARY, amqp_binary("aaaa")));
+    RUN_TEST(failed, type_test(std::string("xxx"), STRING, std::string("yyy")));
+    return failed;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/proton-c/bindings/cpp/src/data.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/data.cpp b/proton-c/bindings/cpp/src/data.cpp
index 70f3e8f..e0ae9bc 100644
--- a/proton-c/bindings/cpp/src/data.cpp
+++ b/proton-c/bindings/cpp/src/data.cpp
@@ -110,7 +110,7 @@ int compare_next(data& a, data& b) {
     if (cmp) return cmp;
 
     switch (ta) {
-      case NULL_: return 0;
+      case NULL_TYPE: return 0;
       case ARRAY:
       case LIST:
       case MAP:

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/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 611d275..0c6a464 100644
--- a/proton-c/bindings/cpp/src/decoder.cpp
+++ b/proton-c/bindings/cpp/src/decoder.cpp
@@ -181,7 +181,7 @@ decoder operator>>(decoder d, message_id& id) {
 
 decoder operator>>(decoder d, amqp_null) {
     save_state ss(d.pn_object());
-    bad_type(NULL_, pre_get(d.pn_object()));
+    bad_type(NULL_TYPE, pre_get(d.pn_object()));
     return d;
 }
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/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 6f5eb88..3e15a58 100644
--- a/proton-c/bindings/cpp/src/encode_decode_test.cpp
+++ b/proton-c/bindings/cpp/src/encode_decode_test.cpp
@@ -90,7 +90,7 @@ void map_test() {
 
 int main(int, char**) {
     int failed = 0;
-    failed += RUN_TEST(map_test);
-    failed += RUN_TEST(value_tests);
+    RUN_TEST(failed, map_test());
+    RUN_TEST(failed, value_tests());
     return failed;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/proton-c/bindings/cpp/src/interop_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/interop_test.cpp b/proton-c/bindings/cpp/src/interop_test.cpp
index b7d6c5b..69a098d 100644
--- a/proton-c/bindings/cpp/src/interop_test.cpp
+++ b/proton-c/bindings/cpp/src/interop_test.cpp
@@ -113,9 +113,9 @@ int main(int argc, char** argv) {
     if (argc != 2) FAIL("Usage: " << argv[0] << " tests-dir");
     tests_dir = argv[1];
 
-    failed += RUN_TEST(test_data_ostream);
-    failed += RUN_TEST(test_decoder_primitves_exact);
-    failed += RUN_TEST(test_encoder_primitives);
-    failed += RUN_TEST(test_value_conversions);
+    RUN_TEST(failed, test_data_ostream());
+    RUN_TEST(failed, test_decoder_primitves_exact());
+    RUN_TEST(failed, test_encoder_primitives());
+    RUN_TEST(failed, test_value_conversions());
     return failed;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/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 928e5a6..ecdea75 100644
--- a/proton-c/bindings/cpp/src/message_test.cpp
+++ b/proton-c/bindings/cpp/src/message_test.cpp
@@ -84,6 +84,6 @@ void test_message() {
 
 int main(int argc, char** argv) {
     int failed = 0;
-    failed += RUN_TEST(test_message);
+    RUN_TEST(failed, test_message);
     return failed;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/proton-c/bindings/cpp/src/test_bits.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/test_bits.hpp b/proton-c/bindings/cpp/src/test_bits.hpp
index 674ac22..49b67e9 100644
--- a/proton-c/bindings/cpp/src/test_bits.hpp
+++ b/proton-c/bindings/cpp/src/test_bits.hpp
@@ -32,19 +32,18 @@ struct fail : public std::logic_error { fail(const std::string& what) : logic_er
 #define ASSERT_EQUAL(WANT, GOT) if (!((WANT) == (GOT))) \
         FAIL(#WANT << " !=  " << #GOT << ": " << WANT << " != " << GOT)
 
-int run_test(void (*testfn)(), const char* name) {
-    try {
-        testfn();
-        return 0;
-    } catch(const fail& e) {
-        std::cout << "FAIL " << name << std::endl << e.what();
-    } catch(const std::exception& e) {
-        std::cout << "ERROR " << name << std::endl << e.what();
-    }
-    return 1;
-}
-
-#define RUN_TEST(TEST) run_test(TEST, #TEST)
+#define RUN_TEST(BAD_COUNT, TEST)                                       \
+    do {                                                                \
+        try {                                                           \
+            TEST;                                                       \
+            break;                                                      \
+        } catch(const fail& e) {                                        \
+            std::cout << "FAIL " << #TEST << std::endl << e.what() << std::endl; \
+        } catch(const std::exception& e) {                              \
+            std::cout << "ERROR " << #TEST << std::endl << __FILE__ << ":" << __LINE__ << ": " << e.what() << std::endl; \
+        }                                                               \
+            ++BAD_COUNT;                                                \
+    } while(0)
 
 template<class T> std::string str(const T& x) { std::ostringstream s; s << x; return s.str(); }
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02d6ba68/proton-c/bindings/cpp/src/types.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/types.cpp b/proton-c/bindings/cpp/src/types.cpp
index 4b1d21d..713f15d 100644
--- a/proton-c/bindings/cpp/src/types.cpp
+++ b/proton-c/bindings/cpp/src/types.cpp
@@ -22,6 +22,7 @@
 #include <ostream>
 #include <iomanip>
 #include <algorithm>
+#include <sstream>
 
 namespace proton {
 
@@ -30,8 +31,26 @@ inline std::ostream& print_segment(std::ostream& o, const amqp_uuid& u, size_t b
     for (const char* p = &u[begin]; p < &u[end]; ++p) o << std::setw(2) << std::setfill('0') << ((int)*p & 0xff);
     return o << sep;
 }
+
+std::string mismatch_message(type_id want, type_id got, const std::string& msg=std::string()) throw()
+{
+    std::ostringstream s;
+    s << "type mismatch: want " << type_name(want) << " got " << type_name(got);
+    if (!msg.empty()) s << ": " << msg;
+    return s.str();
+}
 }
 
+type_mismatch::type_mismatch(type_id want_, type_id got_, const std::string &msg)
+    throw() : error(mismatch_message(want_, got_, msg)), want(want_), got(got_)
+{}
+
+
+std::ostream& operator<<(std::ostream& o, const amqp_decimal32&) { return o << "<decimal32>"; }
+std::ostream& operator<<(std::ostream& o, const amqp_decimal64&) { return o << "<decimal64>"; }
+std::ostream& operator<<(std::ostream& o, const amqp_decimal128&) { return o << "<decimal128>"; }
+std::ostream& operator<<(std::ostream& o, const amqp_timestamp& ts) { return o << "timestamp:" << ts.milliseconds; }
+
 std::ostream& operator<<(std::ostream& o, const amqp_uuid& u) {
     std::ios_base::fmtflags ff = o.flags();
     o.flags(std::ios_base::hex);
@@ -46,7 +65,7 @@ std::ostream& operator<<(std::ostream& o, const amqp_uuid& u) {
 
 std::string type_name(type_id t) {
     switch (t) {
-      case NULL_: return "null";
+      case NULL_TYPE: return "null";
       case BOOLEAN: return "boolean";
       case UBYTE: return "ubyte";
       case BYTE: return "byte";
@@ -75,11 +94,19 @@ std::string type_name(type_id t) {
     }
 }
 
+bool type_id_signed_int(type_id t) { return t == BYTE || t == SHORT || t == INT || t == LONG; }
+bool type_id_unsigned_int(type_id t) { return t == UBYTE || t == USHORT || t == UINT || t == ULONG; }
+bool type_id_integral(type_id t) { return t == BOOLEAN || t == CHAR || type_id_unsigned_int(t) || type_id_signed_int(t); }
+bool type_id_floating_point(type_id t) { return t == FLOAT || t == DOUBLE; }
+bool type_id_decimal(type_id t) { return t == DECIMAL32 || t == DECIMAL64 || t == DECIMAL128; }
+bool type_id_signed(type_id t) { return type_id_signed_int(t) || type_id_floating_point(t) || type_id_decimal(t); }
+bool type_id_string_like(type_id t) { return t == BINARY || t == STRING || t == SYMBOL; }
+bool type_id_container(type_id t) { return t == LIST || t == MAP || t == ARRAY || t == DESCRIBED; }
+bool type_id_atom(type_id t) { return type_id_integral(t) || type_id_floating_point(t) || type_id_decimal(t) || type_id_string_like(t) || t == TIMESTAMP || t == UUID; }
+
+
 std::ostream& operator<<(std::ostream& o,type_id t) { return o << type_name(t); }
 
-PN_CPP_EXTERN bool is_container(type_id t) {
-    return (t == LIST || t == MAP || t == ARRAY || t == DESCRIBED);
-}
 
 pn_bytes_t pn_bytes(const std::string& s) {
     pn_bytes_t b = { s.size(), const_cast<char*>(&s[0]) };
@@ -92,6 +119,6 @@ start::start(type_id t, type_id e, bool d, size_t s) : type(t), element(e), is_d
 start start::array(type_id element, bool described) { return start(ARRAY, element, described); }
 start start::list() { return start(LIST); }
 start start::map() { return start(MAP); }
-start start::described() { return start(DESCRIBED, NULL_, true); }
+start start::described() { return start(DESCRIBED, NULL_TYPE, true); }
 
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org