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 2016/05/31 17:42:47 UTC

qpid-proton git commit: PROTON-1216: c++: proton::coerce should convert binary to string.

Repository: qpid-proton
Updated Branches:
  refs/heads/master fc82f55b8 -> 8d0c5afd0


PROTON-1216: c++: proton::coerce should convert binary to string.

Make scalar and value coerce<> consistent with C++ implicit conversions and each other.

- No source api change (still have deprecated functions)
- Better test coverage, consistent tests for value and scalar where applicable.
- Fixed unknown integer type conversions for scalar.
- Fixed examples using deprecated APIs (deprecated APIs are still in place)
- Consistent printing of scalar values, repect the std::ios::boolalpha flag for printing bool.


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

Branch: refs/heads/master
Commit: 8d0c5afd081a4732c0cb3a1ccd6a0cbcba7187ee
Parents: fc82f55
Author: Alan Conway <ac...@redhat.com>
Authored: Tue May 31 13:11:30 2016 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue May 31 13:11:30 2016 -0400

----------------------------------------------------------------------
 examples/cpp/encode_decode.cpp                  |   1 +
 examples/cpp/simple_recv.cpp                    |   2 +-
 .../cpp/include/proton/annotation_key.hpp       |   6 +-
 proton-c/bindings/cpp/include/proton/error.hpp  |   3 -
 .../cpp/include/proton/internal/scalar_base.hpp |  29 ++-
 .../cpp/include/proton/internal/type_traits.hpp |  21 +-
 .../bindings/cpp/include/proton/message_id.hpp  |   2 +-
 proton-c/bindings/cpp/include/proton/scalar.hpp |   3 +-
 .../bindings/cpp/include/proton/scalar_base.cpp | 151 ++++++++++++++
 proton-c/bindings/cpp/include/proton/value.hpp  |  18 +-
 proton-c/bindings/cpp/src/interop_test.cpp      |   6 +-
 proton-c/bindings/cpp/src/scalar_base.cpp       |  30 ++-
 proton-c/bindings/cpp/src/scalar_test.cpp       | 100 +--------
 proton-c/bindings/cpp/src/scalar_test.hpp       | 209 +++++++++++++++++++
 proton-c/bindings/cpp/src/test_bits.hpp         |   4 +-
 proton-c/bindings/cpp/src/value.cpp             |   4 +-
 proton-c/bindings/cpp/src/value_test.cpp        | 150 ++-----------
 17 files changed, 485 insertions(+), 254 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/examples/cpp/encode_decode.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/encode_decode.cpp b/examples/cpp/encode_decode.cpp
index 67fb0fc..17971d9 100644
--- a/examples/cpp/encode_decode.cpp
+++ b/examples/cpp/encode_decode.cpp
@@ -243,6 +243,7 @@ static void print_next(proton::codec::decoder& d) {
 void print(proton::value& v) {
     proton::codec::decoder d(v);
     d.rewind();
+    std::cout << std::boolalpha; // Print boolean as true/false.
     while (d.more()) {
         print_next(d);
         if (d.more()) std::cout << ", ";

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/examples/cpp/simple_recv.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/simple_recv.cpp b/examples/cpp/simple_recv.cpp
index 2495a28..7621941 100644
--- a/examples/cpp/simple_recv.cpp
+++ b/examples/cpp/simple_recv.cpp
@@ -50,7 +50,7 @@ class simple_recv : public proton::messaging_handler {
     }
 
     void on_message(proton::delivery &d, proton::message &msg) OVERRIDE {
-        if (msg.id().get<uint64_t>() < received) {
+        if (proton::get<uint64_t>(msg.id()) < received) {
             return; // Ignore duplicate
         }
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/proton-c/bindings/cpp/include/proton/annotation_key.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/annotation_key.hpp b/proton-c/bindings/cpp/include/proton/annotation_key.hpp
index 019e3d3..c0c8ca6 100644
--- a/proton-c/bindings/cpp/include/proton/annotation_key.hpp
+++ b/proton-c/bindings/cpp/include/proton/annotation_key.hpp
@@ -59,12 +59,12 @@ class annotation_key : public internal::scalar_base {
 };
 
 /// @cond INTERNAL
-/// XXX Document this?
+/// Primary template for get<T>(message_id), specialized for legal types.
 template <class T> T get(const annotation_key& x);
 /// @endcond
 
 /// Get the uint64_t value or throw conversion_error.
-///    
+///
 /// @related annotation_key
 template<> inline uint64_t get<uint64_t>(const annotation_key& x) { return internal::get<uint64_t>(x); }
 
@@ -74,7 +74,7 @@ template<> inline uint64_t get<uint64_t>(const annotation_key& x) { return inter
 template<> inline symbol get<symbol>(const annotation_key& x) { return internal::get<symbol>(x); }
 
 /// Get the @ref binary value or throw conversion_error.
-///    
+///
 /// @copydoc scalar::coerce
 /// @related annotation_key
 template<class T> T coerce(const annotation_key& x) { return internal::coerce<T>(x); }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/proton-c/bindings/cpp/include/proton/error.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/error.hpp b/proton-c/bindings/cpp/include/proton/error.hpp
index 1dbbc9b..6896620 100644
--- a/proton-c/bindings/cpp/include/proton/error.hpp
+++ b/proton-c/bindings/cpp/include/proton/error.hpp
@@ -24,15 +24,12 @@
 
 #include "./internal/config.hpp"
 #include "./internal/export.hpp"
-#include "./value.hpp"
 
 #include <stdexcept>
 #include <string>
 
 namespace proton {
 
-class value;
-
 /// The base Proton error.
 ///
 /// All exceptions thrown from functions in the proton namespace are

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/proton-c/bindings/cpp/include/proton/internal/scalar_base.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/internal/scalar_base.hpp b/proton-c/bindings/cpp/include/proton/internal/scalar_base.hpp
index 8f57cb6..bcd0410 100644
--- a/proton-c/bindings/cpp/include/proton/internal/scalar_base.hpp
+++ b/proton-c/bindings/cpp/include/proton/internal/scalar_base.hpp
@@ -36,6 +36,7 @@
 
 #include <iosfwd>
 #include <string>
+#include <typeinfo>
 
 namespace proton {
 class message;
@@ -47,6 +48,9 @@ class encoder;
 
 namespace internal {
 
+class scalar_base;
+template<class T> T get(const scalar_base& s);
+
 /// Base class for scalar types.
 class scalar_base : private comparable<scalar_base> {
   public:
@@ -97,6 +101,9 @@ class scalar_base : private comparable<scalar_base> {
     PN_CPP_EXTERN void put_(const char* s); ///< Treated as an AMQP string
     PN_CPP_EXTERN void put_(const null&);
 
+    template<class T> void put(const T& x) { putter<T>::put(*this, x); }
+
+  private:
     PN_CPP_EXTERN void get_(bool&) const;
     PN_CPP_EXTERN void get_(uint8_t&) const;
     PN_CPP_EXTERN void get_(int8_t&) const;
@@ -119,7 +126,20 @@ class scalar_base : private comparable<scalar_base> {
     PN_CPP_EXTERN void get_(binary&) const;
     PN_CPP_EXTERN void get_(null&) const;
 
-  private:
+    // use template structs, functions cannot be  partially specialized.
+    template <class T, class Enable=void> struct putter {
+        static void put(scalar_base& s, const T& x) { s.put_(x); }
+    };
+    template <class T> struct putter<T, typename enable_if<is_unknown_integer<T>::value>::type> {
+        static void put(scalar_base& s, const T& x) { s.put_(static_cast<typename known_integer<T>::type>(x)); }
+    };
+    template <class T, class Enable=void> struct getter {
+        static T get(const scalar_base& s) { T x; s.get_(x); return x; }
+    };
+    template <class T> struct getter<T, typename enable_if<is_unknown_integer<T>::value>::type> {
+        static T get(const scalar_base& s) { typename known_integer<T>::type x; s.get_(x); return x; }
+    };
+
     void ok(pn_type_t) const;
     void set(const pn_atom_t&);
     void set(const binary& x, pn_type_t t);
@@ -131,11 +151,10 @@ class scalar_base : private comparable<scalar_base> {
   friend class proton::message;
   friend class codec::encoder;
   friend class codec::decoder;
+  template<class T> friend T get(const scalar_base& s) { return scalar_base::getter<T>::get(s); }
     /// @endcond
 };
 
-template<class T> T get(const scalar_base& s) { T x; s.get(x); return x; }
-
 template <class R, class F> R visit(const scalar_base& s, F f) {
     switch(s.type()) {
       case BOOLEAN: return f(s.get<bool>());
@@ -162,6 +181,8 @@ template <class R, class F> R visit(const scalar_base& s, F f) {
     }
 }
 
+PN_CPP_EXTERN conversion_error make_coercion_error(const char* cpp_type, type_id amqp_type);
+
 template<class T> struct coerce_op {
     template <class U>
     typename enable_if<is_convertible<U, T>::value, T>::type operator()(const U& x) {
@@ -169,7 +190,7 @@ template<class T> struct coerce_op {
     }
     template <class U>
     typename enable_if<!is_convertible<U, T>::value, T>::type operator()(const U&) {
-        throw conversion_error("cannot coerce from " + type_name(type_id_of<U>::value));
+        throw make_coercion_error(typeid(T).name(), type_id_of<U>::value);
     }
 };
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/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 999b321..0490823 100644
--- a/proton-c/bindings/cpp/include/proton/internal/type_traits.hpp
+++ b/proton-c/bindings/cpp/include/proton/internal/type_traits.hpp
@@ -117,7 +117,20 @@ template<> struct type_id_of<binary> : public type_id_constant<BINARY, binary> {
 template <class T, class Enable=void> struct has_type_id : public false_type {};
 template <class T> struct has_type_id<T, typename type_id_of<T>::type>  : public true_type {};
 
-// Map arbitrary integral types to known AMQP integral types.
+// The known/unknown integer type magic is required because the C++ standard is
+// vague a about the equivalence of integral types for overloading. E.g. char is
+// sometimes equivalent to signed char, sometimes unsigned char, sometimes
+// neither. int8_t or uint8_t may or may not be equivalent to a char type.
+// int64_t may or may not be equivalent to long long etc. C++ compilers are also
+// allowed to add their own non-standard integer types like __int64, which may
+// or may not be equivalent to any of the standard integer types.
+//
+// The solution is to use a fixed, standard set of integer types that are
+// guaranteed to be distinct for overloading (see type_id_of) and to use template
+// specialization to convert other integer types to a known integer type with the
+// same sizeof and is_signed.
+
+// Map arbitrary integral types to known integral types.
 template<size_t SIZE, bool IS_SIGNED> struct integer_type;
 template<> struct integer_type<1, true> { typedef int8_t type; };
 template<> struct integer_type<2, true> { typedef int16_t type; };
@@ -133,7 +146,11 @@ template <class T> struct is_unknown_integer {
     static const bool value = !has_type_id<T>::value && is_integral<T>::value;
 };
 
-// Helper base for SFINAE test templates.
+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;
     typedef double no;

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/proton-c/bindings/cpp/include/proton/message_id.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/message_id.hpp b/proton-c/bindings/cpp/include/proton/message_id.hpp
index a37198e..84f8ab0 100644
--- a/proton-c/bindings/cpp/include/proton/message_id.hpp
+++ b/proton-c/bindings/cpp/include/proton/message_id.hpp
@@ -68,7 +68,7 @@ class message_id : public internal::scalar_base {
 };
 
 /// @cond INTERNAL
-// XXX Document this?
+/// Base template for get(message_id), specialized for legal message_id types.
 template <class T> T get(const message_id& x);
 /// @endcond
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/proton-c/bindings/cpp/include/proton/scalar.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/scalar.hpp b/proton-c/bindings/cpp/include/proton/scalar.hpp
index 4fd6dd9..eed5255 100644
--- a/proton-c/bindings/cpp/include/proton/scalar.hpp
+++ b/proton-c/bindings/cpp/include/proton/scalar.hpp
@@ -41,7 +41,7 @@ class scalar : public internal::scalar_base {
     template <class T> scalar(const T& x) { *this = x; }
 
     /// Assign from any scalar type.
-    template <class T> scalar& operator=(const T& x) { put_(x); return *this; }
+    template <class T> scalar& operator=(const T& x) { put(x); return *this; }
 
     /// True if type() == NULL_TYPE.
     bool empty() const { return type() == NULL_TYPE; }
@@ -71,6 +71,7 @@ template<class T> T get(const scalar& s) { return internal::get<T>(s); }
 /// according to `std::is_convertible`
 /// @related scalar
 template<class T> T coerce(const scalar& x) { return internal::coerce<T>(x); }
+template<class T> T coerce(scalar& x) { return internal::coerce<T>(x); }
 
 } // proton
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/proton-c/bindings/cpp/include/proton/scalar_base.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/scalar_base.cpp b/proton-c/bindings/cpp/include/proton/scalar_base.cpp
new file mode 100644
index 0000000..20694db
--- /dev/null
+++ b/proton-c/bindings/cpp/include/proton/scalar_base.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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 "msg.hpp"
+#include "types_internal.hpp"
+
+#include "proton/binary.hpp"
+#include "proton/decimal.hpp"
+#include "proton/internal/scalar_base.hpp"
+#include "proton/internal/type_traits.hpp"
+#include "proton/symbol.hpp"
+#include "proton/timestamp.hpp"
+#include "proton/uuid.hpp"
+
+#include <ostream>
+
+namespace proton {
+namespace internal {
+
+scalar_base::scalar_base() { atom_.type = PN_NULL; }
+scalar_base::scalar_base(const pn_atom_t& a) { set(a); }
+scalar_base::scalar_base(const scalar_base& x) { set(x.atom_); }
+
+scalar_base& scalar_base::operator=(const scalar_base& x) {
+    if (this != &x)
+        set(x.atom_);
+    return *this;
+}
+
+type_id scalar_base::type() const { return type_id(atom_.type); }
+
+void scalar_base::set(const binary& x, pn_type_t t) {
+    atom_.type = t;
+    bytes_ = x;
+    atom_.u.as_bytes = pn_bytes(bytes_);
+}
+
+void scalar_base::set(const pn_atom_t& atom) {
+    if (type_id_is_string_like(type_id(atom.type))) {
+        set(bin(atom.u.as_bytes), atom.type);
+    } else {
+        atom_ = atom;
+        bytes_.clear();
+    }
+}
+
+void scalar_base::put_(bool x) { atom_.u.as_bool = x; atom_.type = PN_BOOL; }
+void scalar_base::put_(uint8_t x) { atom_.u.as_ubyte = x; atom_.type = PN_UBYTE; }
+void scalar_base::put_(int8_t x) { atom_.u.as_byte = x; atom_.type = PN_BYTE; }
+void scalar_base::put_(uint16_t x) { atom_.u.as_ushort = x; atom_.type = PN_USHORT; }
+void scalar_base::put_(int16_t x) { atom_.u.as_short = x; atom_.type = PN_SHORT; }
+void scalar_base::put_(uint32_t x) { atom_.u.as_uint = x; atom_.type = PN_UINT; }
+void scalar_base::put_(int32_t x) { atom_.u.as_int = x; atom_.type = PN_INT; }
+void scalar_base::put_(uint64_t x) { atom_.u.as_ulong = x; atom_.type = PN_ULONG; }
+void scalar_base::put_(int64_t x) { atom_.u.as_long = x; atom_.type = PN_LONG; }
+void scalar_base::put_(wchar_t x) { atom_.u.as_char = x; atom_.type = PN_CHAR; }
+void scalar_base::put_(float x) { atom_.u.as_float = x; atom_.type = PN_FLOAT; }
+void scalar_base::put_(double x) { atom_.u.as_double = x; atom_.type = PN_DOUBLE; }
+void scalar_base::put_(timestamp x) { atom_.u.as_timestamp = x.milliseconds(); atom_.type = PN_TIMESTAMP; }
+void scalar_base::put_(const decimal32& x) { byte_copy(atom_.u.as_decimal32, x); atom_.type = PN_DECIMAL32;; }
+void scalar_base::put_(const decimal64& x) { byte_copy(atom_.u.as_decimal64, x); atom_.type = PN_DECIMAL64; }
+void scalar_base::put_(const decimal128& x) { byte_copy(atom_.u.as_decimal128, x); atom_.type = PN_DECIMAL128; }
+void scalar_base::put_(const uuid& x) { byte_copy(atom_.u.as_uuid, x); atom_.type = PN_UUID; }
+void scalar_base::put_(const std::string& x) { set(binary(x), PN_STRING); }
+void scalar_base::put_(const symbol& x) { set(binary(x), PN_SYMBOL); }
+void scalar_base::put_(const binary& x) { set(x, PN_BINARY); }
+void scalar_base::put_(const char* x) { set(binary(std::string(x)), PN_STRING); }
+void scalar_base::put_(const null&) { atom_.type = PN_NULL; }
+
+void scalar_base::ok(pn_type_t t) const {
+    if (atom_.type != t) throw make_conversion_error(type_id(t), type());
+}
+
+void scalar_base::get_(bool& x) const { ok(PN_BOOL); x = atom_.u.as_bool; }
+void scalar_base::get_(uint8_t& x) const { ok(PN_UBYTE); x = atom_.u.as_ubyte; }
+void scalar_base::get_(int8_t& x) const { ok(PN_BYTE); x = atom_.u.as_byte; }
+void scalar_base::get_(uint16_t& x) const { ok(PN_USHORT); x = atom_.u.as_ushort; }
+void scalar_base::get_(int16_t& x) const { ok(PN_SHORT); x = atom_.u.as_short; }
+void scalar_base::get_(uint32_t& x) const { ok(PN_UINT); x = atom_.u.as_uint; }
+void scalar_base::get_(int32_t& x) const { ok(PN_INT); x = atom_.u.as_int; }
+void scalar_base::get_(wchar_t& x) const { ok(PN_CHAR); x = wchar_t(atom_.u.as_char); }
+void scalar_base::get_(uint64_t& x) const { ok(PN_ULONG); x = atom_.u.as_ulong; }
+void scalar_base::get_(int64_t& x) const { ok(PN_LONG); x = atom_.u.as_long; }
+void scalar_base::get_(timestamp& x) const { ok(PN_TIMESTAMP); x = atom_.u.as_timestamp; }
+void scalar_base::get_(float& x) const { ok(PN_FLOAT); x = atom_.u.as_float; }
+void scalar_base::get_(double& x) const { ok(PN_DOUBLE); x = atom_.u.as_double; }
+void scalar_base::get_(decimal32& x) const { ok(PN_DECIMAL32); byte_copy(x, atom_.u.as_decimal32); }
+void scalar_base::get_(decimal64& x) const { ok(PN_DECIMAL64); byte_copy(x, atom_.u.as_decimal64); }
+void scalar_base::get_(decimal128& x) const { ok(PN_DECIMAL128); byte_copy(x, atom_.u.as_decimal128); }
+void scalar_base::get_(uuid& x) const { ok(PN_UUID); byte_copy(x, atom_.u.as_uuid); }
+void scalar_base::get_(std::string& x) const { ok(PN_STRING); x = std::string(bytes_.begin(), bytes_.end()); }
+void scalar_base::get_(symbol& x) const { ok(PN_SYMBOL); x = symbol(bytes_.begin(), bytes_.end()); }
+void scalar_base::get_(binary& x) const { ok(PN_BINARY); x = bytes_; }
+void scalar_base::get_(null&) const { ok(PN_NULL); }
+
+namespace {
+
+struct equal_op {
+    const scalar_base& x;
+    equal_op(const scalar_base& s) : x(s) {}
+    template<class T> bool operator()(const T& y) { return (x.get<T>() == y); }
+};
+
+struct less_op {
+    const scalar_base& x;
+    less_op(const scalar_base& s) : x(s) {}
+    template<class T> bool operator()(const T& y) { return (y < x.get<T>()); }
+};
+
+struct ostream_op {
+    std::ostream& o;
+    ostream_op(std::ostream& o_) : o(o_) {}
+    template<class T> std::ostream& operator()(const T& x) { return o << x; }
+};
+
+} // namespace
+
+bool operator==(const scalar_base& x, const scalar_base& y) {
+    if (x.type() != y.type()) return false;
+    if (x.type() == NULL_TYPE) return true;
+    return internal::visit<bool>(x, equal_op(y));
+}
+
+bool operator<(const scalar_base& x, const scalar_base& y) {
+    if (x.type() != y.type()) return x.type() < y.type();
+    if (x.type() == NULL_TYPE) return false;
+    return internal::visit<bool>(x, less_op(y));
+}
+
+std::ostream& operator<<(std::ostream& o, const scalar_base& s) {
+    if (s.type() == NULL_TYPE) return o << "<null>";
+    return internal::visit<std::ostream&>(s, ostream_op(o));
+}
+
+}} // namespaces

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/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 2e8b302..4149f60 100644
--- a/proton-c/bindings/cpp/include/proton/value.hpp
+++ b/proton-c/bindings/cpp/include/proton/value.hpp
@@ -25,6 +25,7 @@
 #include "./codec/encoder.hpp"
 #include "./codec/decoder.hpp"
 #include "./internal/type_traits.hpp"
+#include "./scalar.hpp"
 #include "./types_fwd.hpp"
 
 #include <iosfwd>
@@ -129,7 +130,7 @@ template<class T> T get(const value& v) { T x; get(v, x); return x; }
 /// Like get(const value&) but assigns the value to a reference
 /// instead of returning it.  May be more efficient for complex values
 /// (arrays, maps, etc.)
-///    
+///
 /// @related proton::value
 template<class T> void get(const value& v, T& x) { codec::decoder d(v, true); d >> x; }
 
@@ -142,10 +143,21 @@ template<class T> T coerce(const value& v) { T x; coerce(v, x); return x; }
 /// (arrays, maps, etc.)
 ///
 /// @related proton::value
-template<class T> void coerce(const value& v, T& x) { codec::decoder d(v, false); d >> x; }
+template<class T> void coerce(const value& v, T& x) {
+    codec::decoder d(v, false);
+    if (type_id_is_scalar(v.type())) {
+        scalar s;
+        d >> s;
+        x = internal::coerce<T>(s);
+    } else {
+        d >> x;
+    }
+}
 
-/// @cond INTERNAL
+/// Special case for get<null>(), just checks that value contains NULL.
 template<> inline void get<null>(const value& v, null&) { assert_type_equal(NULL_TYPE, v.type()); }
+
+/// @cond INTERNAL
 template<class T> void value::get(T &x) const { x = proton::get<T>(*this); }
 template<class T> T value::get() const { return proton::get<T>(*this); }
 inline int64_t value::as_int() const { return proton::coerce<int64_t>(*this); }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/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 22581f0..45f6a0d 100644
--- a/proton-c/bindings/cpp/src/interop_test.cpp
+++ b/proton-c/bindings/cpp/src/interop_test.cpp
@@ -46,10 +46,10 @@ string read(string filename) {
 
 // Test data ostream operator
 void test_data_ostream() {
-    value dv;
-    decoder d(dv);
+    data dt(data::create());
+    decoder d(dt);
     d.decode(read("primitives"));
-    ASSERT_EQUAL("true, false, 42, 42, -42, 12345, -12345, 12345, -12345, 0.125, 0.125", str(dv));
+    ASSERT_EQUAL("true, false, 42, 42, -42, 12345, -12345, 12345, -12345, 0.125, 0.125", str(dt));
 }
 
 // Test extracting to exact AMQP types works corectly, extrating to invalid types fails.

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/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 20694db..c94bc34 100644
--- a/proton-c/bindings/cpp/src/scalar_base.cpp
+++ b/proton-c/bindings/cpp/src/scalar_base.cpp
@@ -30,6 +30,11 @@
 
 #include <ostream>
 
+#if PN_CPP_HAS_CPP11
+#include <cxxabi.h>
+#include <memory>
+#endif
+
 namespace proton {
 namespace internal {
 
@@ -114,13 +119,13 @@ namespace {
 struct equal_op {
     const scalar_base& x;
     equal_op(const scalar_base& s) : x(s) {}
-    template<class T> bool operator()(const T& y) { return (x.get<T>() == y); }
+    template<class T> bool operator()(const T& y) { return (get<T>(x) == y); }
 };
 
 struct less_op {
     const scalar_base& x;
     less_op(const scalar_base& s) : x(s) {}
-    template<class T> bool operator()(const T& y) { return (y < x.get<T>()); }
+    template<class T> bool operator()(const T& y) { return (y < get<T>(x)); }
 };
 
 struct ostream_op {
@@ -144,8 +149,25 @@ bool operator<(const scalar_base& x, const scalar_base& y) {
 }
 
 std::ostream& operator<<(std::ostream& o, const scalar_base& s) {
-    if (s.type() == NULL_TYPE) return o << "<null>";
-    return internal::visit<std::ostream&>(s, ostream_op(o));
+    switch (s.type()) {
+      case NULL_TYPE: return o << "<null>";
+        // Print byte types as integer, not char.
+      case BYTE: return o << static_cast<int>(get<int8_t>(s));
+      case UBYTE: return o << static_cast<unsigned int>(get<uint8_t>(s));
+        // Other types printed using normal C++ operator <<
+      default: return internal::visit<std::ostream&>(s, ostream_op(o));
+    }
+}
+
+conversion_error make_coercion_error(const char* cpp_mangled, type_id amqp) {
+#if PN_CPP_HAS_CPP11
+    std::unique_ptr<char, decltype(&::free)> demangled(
+        abi::__cxa_demangle(cpp_mangled, NULL, NULL, NULL), ::free);
+    std::string cpp_name = demangled ? demangled.get() : cpp_mangled;
+#else
+    std::string cpp_name = cpp_mangled;
+#endif
+    return conversion_error("invalid proton::coerce<" + cpp_name + ">(" + type_name(amqp) + ")");
 }
 
 }} // namespaces

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/proton-c/bindings/cpp/src/scalar_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/scalar_test.cpp b/proton-c/bindings/cpp/src/scalar_test.cpp
index f6df832..3d6d6de 100644
--- a/proton-c/bindings/cpp/src/scalar_test.cpp
+++ b/proton-c/bindings/cpp/src/scalar_test.cpp
@@ -17,19 +17,7 @@
  * under the License.
  */
 
-#include "test_bits.hpp"
-
-#include "proton/binary.hpp"
-#include "proton/error.hpp"
-#include "proton/internal/type_traits.hpp"
-
-#include "proton/scalar.hpp"
-#include "proton/value.hpp"
-#include "proton/message_id.hpp"
-#include "proton/annotation_key.hpp"
-#include "proton/decimal.hpp"
-
-#include <sstream>
+#include "scalar_test.hpp"
 
 namespace {
 
@@ -37,59 +25,7 @@ using namespace std;
 using namespace proton;
 using namespace test;
 
-// Inserting and extracting simple C++ values.
-template <class T> void type_test(T x, type_id tid, T y) {
-    scalar s(x);
-    ASSERT_EQUAL(tid, s.type());
-    ASSERT(!s.empty());
-    ASSERT_EQUAL(x, s.get<T>());
-
-    scalar v2;
-    ASSERT(v2.type() == NULL_TYPE);
-    v2 = x;
-    ASSERT_EQUAL(tid, v2.type());
-    ASSERT_EQUAL(x, v2.get<T>());
-    ASSERT_EQUAL(s, v2);
-    ASSERT_EQUAL(str(x), str(s));
-
-    v2 = y;
-    ASSERT(s != v2);
-    ASSERT(s < v2);
-    ASSERT(v2 > s);
-}
-
-#define ASSERT_MISMATCH(EXPR, WANT, GOT)                                \
-    try {                                                               \
-        (void)(EXPR);                                                   \
-        FAIL("expected conversion_error: " #EXPR);                      \
-    } catch (const conversion_error&) {}
-
-void coerce_test() {
-    scalar a;
-    ASSERT_EQUAL(NULL_TYPE, a.type());
-    ASSERT(a.empty());
-    ASSERT_MISMATCH(a.get<float>(), FLOAT, NULL_TYPE);
-
-    a = binary("foo");
-    ASSERT_MISMATCH(a.get<int16_t>(), SHORT, BINARY);
-    ASSERT_MISMATCH(coerce<int64_t>(a), LONG, BINARY);
-    ASSERT_MISMATCH(coerce<double>(a), DOUBLE, BINARY);
-    ASSERT_MISMATCH(a.get<std::string>(), STRING, BINARY); // No strict conversion
-    ASSERT_EQUAL(coerce<std::string>(a), std::string("foo")); // OK string-like conversion
-
-    a = int16_t(42);
-    ASSERT_MISMATCH(a.get<std::string>(), STRING, SHORT);
-    ASSERT_MISMATCH(a.get<timestamp>(), TIMESTAMP, SHORT);
-    ASSERT_MISMATCH(coerce<std::string>(a), STRING, SHORT);
-    ASSERT_EQUAL(coerce<int64_t>(a), 42);
-    ASSERT_EQUAL(coerce<uint64_t>(a), 42u);
-    ASSERT_EQUAL(coerce<double>(a), 42);
-
-    a = int16_t(-42);
-    ASSERT_EQUAL(coerce<int64_t>(a), -42);
-    ASSERT_EQUAL(coerce<uint64_t>(a), uint64_t(-42));
-    ASSERT_EQUAL(coerce<double>(a), -42);
-}
+// NOTE: proton::coerce<> and bad proton::get() are tested in value_test to avoid redundant test code.
 
 void encode_decode_test() {
     value v;
@@ -98,23 +34,23 @@ void encode_decode_test() {
     ASSERT_EQUAL(v, a);
     ASSERT_EQUAL(std::string("foo"), get<std::string>(v));
     scalar a2 = get<scalar>(v);
-    ASSERT_EQUAL(std::string("foo"), a2.get<std::string>());
+    ASSERT_EQUAL(std::string("foo"), get<std::string>(a2));
 }
 
 void message_id_test() {
     ASSERT_EQUAL(23, coerce<int64_t>(message_id(23)));
-    ASSERT_EQUAL(23u, message_id(23).get<uint64_t>());
+    ASSERT_EQUAL(23u, get<uint64_t>(message_id(23)));
     ASSERT(message_id("foo") != message_id(binary("foo")));
     ASSERT_EQUAL(scalar("foo"), message_id("foo"));
     ASSERT_EQUAL("foo", coerce<std::string>(message_id("foo")));
     ASSERT(message_id("a") < message_id("z"));
     uuid r = uuid::random();
-    ASSERT_EQUAL(r, message_id(r).get<uuid>());
+    ASSERT_EQUAL(r, get<uuid>(message_id(r)));
 }
 
 void annotation_key_test() {
     ASSERT_EQUAL(23, coerce<int64_t>(annotation_key(23)));
-    ASSERT_EQUAL(23u, annotation_key(23).get<uint64_t>());
+    ASSERT_EQUAL(23u, get<uint64_t>(annotation_key(23)));
     ASSERT_EQUAL("foo", coerce<std::string>(annotation_key("foo")));
     ASSERT_EQUAL(scalar(symbol("foo")), annotation_key("foo"));
 }
@@ -125,30 +61,10 @@ template <class T> T make(const char c) { T x; std::fill(x.begin(), x.end(), c);
 
 int main(int, char**) {
     int failed = 0;
-    RUN_TEST(failed, type_test(false, BOOLEAN, true));
-    RUN_TEST(failed, type_test(uint8_t(42), UBYTE, uint8_t(50)));
-    RUN_TEST(failed, type_test(int8_t('x'), BYTE, int8_t('y')));
-    RUN_TEST(failed, type_test(uint16_t(4242), USHORT, uint16_t(5252)));
-    RUN_TEST(failed, type_test(int16_t(-4242), SHORT, int16_t(3)));
-    RUN_TEST(failed, type_test(uint32_t(4242), UINT, uint32_t(5252)));
-    RUN_TEST(failed, type_test(int32_t(-4242), INT, int32_t(3)));
-    RUN_TEST(failed, type_test(uint64_t(4242), ULONG, uint64_t(5252)));
-    RUN_TEST(failed, type_test(int64_t(-4242), LONG, int64_t(3)));
-    RUN_TEST(failed, type_test(wchar_t(23), CHAR, wchar_t(24)));
-    RUN_TEST(failed, type_test(float(1.234), FLOAT, float(2.345)));
-    RUN_TEST(failed, type_test(double(11.2233), DOUBLE, double(12)));
-    RUN_TEST(failed, type_test(timestamp(0), TIMESTAMP, timestamp(1)));
-    RUN_TEST(failed, type_test(make<decimal32>(0), DECIMAL32, make<decimal32>(1)));
-    RUN_TEST(failed, type_test(make<decimal64>(0), DECIMAL64, make<decimal64>(1)));
-    RUN_TEST(failed, type_test(make<decimal128>(0), DECIMAL128, make<decimal128>(1)));
-    RUN_TEST(failed, type_test(uuid::copy("a"), UUID, uuid::copy("x")));
-    RUN_TEST(failed, type_test(std::string("aaa"), STRING, std::string("aaaa")));
-    RUN_TEST(failed, type_test(symbol("aaa"), SYMBOL, symbol("aaaa")));
-    RUN_TEST(failed, type_test(binary("aaa"), BINARY, binary("aaaa")));
-    RUN_TEST(failed, type_test(std::string("xxx"), STRING, std::string("yyy")));
+    scalar_test_group<scalar>(failed);
+
     RUN_TEST(failed, encode_decode_test());
     RUN_TEST(failed, message_id_test());
     RUN_TEST(failed, annotation_key_test());
-    RUN_TEST(failed, coerce_test());
     return failed;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/proton-c/bindings/cpp/src/scalar_test.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/scalar_test.hpp b/proton-c/bindings/cpp/src/scalar_test.hpp
new file mode 100644
index 0000000..87a529c
--- /dev/null
+++ b/proton-c/bindings/cpp/src/scalar_test.hpp
@@ -0,0 +1,209 @@
+#ifndef SCALAR_TEST_HPP
+#define SCALAR_TEST_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.
+ */
+
+// Template tests used by both scalar_test.cpp and value_test.hpp to test conversion
+// of scalar values via a proton::scalar or a proton::value.
+
+#include "test_bits.hpp"
+
+#include "proton/types.hpp"
+#include "proton/error.hpp"
+
+#include <sstream>
+
+
+namespace test {
+
+using namespace proton;
+
+// Inserting and extracting simple C++ values using same-type get<T> and coerce<T>
+template <class V, class T> void simple_type_test(T x, type_id tid, const std::string& s, T y) {
+    V vx(x);                    // Construct from C++ value
+    ASSERT_EQUAL(tid, vx.type());
+    ASSERT(!vx.empty());
+    ASSERT_EQUAL(x, get<T>(vx));
+    ASSERT_EQUAL(x, coerce<T>(vx));
+
+    V vxa = x;                  // Assign from C++ value
+    ASSERT_EQUAL(tid, vxa.type());
+    ASSERT(!vx.empty());
+    ASSERT_EQUAL(vx, vxa);
+    ASSERT_EQUAL(x, get<T>(vxa));
+    ASSERT_EQUAL(x, coerce<T>(vxa));
+
+    V v2;                       // Default construct
+    ASSERT(v2.type() == NULL_TYPE);
+    ASSERT(v2.empty());
+    v2 = x;                     // Assign from C++ value
+    ASSERT_EQUAL(tid, v2.type());
+    ASSERT_EQUAL(vx, v2);
+    ASSERT_EQUAL(x, get<T>(v2));
+    ASSERT_EQUAL(x, coerce<T>(v2));
+
+    V v3(vx);                   // Copy construct
+    ASSERT_EQUAL(tid, v3.type());
+    ASSERT_EQUAL(vx, v3);
+    ASSERT_EQUAL(x, get<T>(v3));
+    ASSERT_EQUAL(x, coerce<T>(v3));
+
+    V v4 = vx;                  // Copy assign
+    ASSERT_EQUAL(tid, v4.type());
+    ASSERT_EQUAL(x, get<T>(v4));
+    ASSERT_EQUAL(x, coerce<T>(v4));
+
+    ASSERT_EQUAL(s, str(vx));   // Stringify
+    V vy(y);
+    ASSERT(vx != vy);           // Compare
+    ASSERT(vx < vy);
+    ASSERT(vy > vx);
+}
+
+// Test native C/C++ integer types via their mapped integer type ([u]int_x_t)
+template <class V, class T> void simple_integral_test() {
+    typedef typename internal::integer_type<sizeof(T), internal::is_signed<T>::value>::type int_type;
+    simple_type_test<V>(T(3), internal::type_id_of<int_type>::value, "3", T(4));
+}
+
+// Test invalid gets, valid same-type get<T> is tested by simple_type_test
+// Templated to test both scalar and value.
+template<class V>  void bad_get_test() {
+    try { get<bool>(V(int8_t(1))); FAIL("byte as bool"); } catch (conversion_error) {}
+    try { get<uint8_t>(V(true)); FAIL("bool as uint8_t"); } catch (conversion_error) {}
+    try { get<uint8_t>(V(int8_t(1))); FAIL("int8 as uint8"); } catch (conversion_error) {}
+    try { get<int16_t>(V(uint16_t(1))); FAIL("uint16 as int16"); } catch (conversion_error) {}
+    try { get<int16_t>(V(int32_t(1))); FAIL("int32 as int16"); } catch (conversion_error) {}
+    try { get<symbol>(V(std::string())); FAIL("string as symbol"); } catch (conversion_error) {}
+    try { get<std::string>(V(binary())); FAIL("binary as string"); } catch (conversion_error) {}
+    try { get<binary>(V(symbol())); FAIL("symbol as binary"); } catch (conversion_error) {}
+    try { get<binary>(V(timestamp())); FAIL("timestamp as binary"); } catch (conversion_error) {}
+    try { get<int>(V(timestamp())); FAIL("timestamp as int"); } catch (conversion_error) {}
+    try { get<timestamp>(V(0)); FAIL("int as timestamp"); } catch (conversion_error) {}
+    try { get<timestamp>(V(std::string())); FAIL("string as timestamp"); } catch (conversion_error) {}
+}
+
+// Test some valid coercions and some bad ones with mixed types.
+// Templated to test both scalar and value.
+template<class V> void coerce_test() {
+    // Valid C++ conversions should work with coerce.
+    ASSERT_EQUAL(false, coerce<bool>(V(0)));
+    ASSERT_EQUAL(true, coerce<bool>(V(-1)));
+    ASSERT_EQUAL(true, coerce<bool>(V(int64_t(0xFFFF0000))));
+
+    ASSERT_EQUAL(1, coerce<uint8_t>(V(uint64_t(1)))); // In range.
+    ASSERT_EQUAL(1, coerce<uint8_t>(V(uint32_t(0xFF01)))); // int truncate.
+    ASSERT_EQUAL(0xFFFF, coerce<uint16_t>(V(int8_t(-1)))); // Sign extend.
+    ASSERT_EQUAL(-1, coerce<int32_t>(V(uint64_t(0xFFFFFFFFul)))); // 2s complement
+
+    ASSERT_EQUALISH(1.2, coerce<float>(V(double(1.2))), 0.001);
+    ASSERT_EQUALISH(3.4, coerce<double>(V(float(3.4))), 0.001);
+    ASSERT_EQUALISH(23.0, coerce<double>(V(uint64_t(23))), 0.001); // int to double.
+    ASSERT_EQUAL(-1945, coerce<int>(V(float(-1945.123))));    // round to int.
+
+    // String-like conversions.
+    ASSERT_EQUAL(std::string("foo"), coerce<std::string>(V(symbol("foo"))));
+    ASSERT_EQUAL(std::string("foo"), coerce<std::string>(V(binary("foo"))));
+
+    // Bad coercions, types are not `is_convertible`
+    V s("foo");
+    try { coerce<bool>(s); FAIL("string as bool"); } catch (conversion_error) {}
+    try { coerce<int>(s); FAIL("string as int"); } catch (conversion_error) {}
+    try { coerce<double>(s); FAIL("string as double"); } catch (conversion_error) {}
+
+    try { coerce<std::string>(V(0)); FAIL("int as string"); } catch (conversion_error) {}
+    try { coerce<symbol>(V(true)); FAIL("bool as symbol"); } catch (conversion_error) {}
+    try { coerce<binary>(V(0.0)); FAIL("double as binary"); } catch (conversion_error) {}
+    try { coerce<symbol>(V(binary())); FAIL("binary as symbol"); } catch (conversion_error) {}
+    try { coerce<binary>(V(symbol())); FAIL("symbol as binary"); } catch (conversion_error) {}
+    try { coerce<binary>(s); } catch (conversion_error) {}
+    try { coerce<symbol>(s); } catch (conversion_error) {}
+}
+
+template <class V> void null_test() {
+    V v;
+    ASSERT(v.empty());
+    ASSERT_EQUAL(NULL_TYPE, v.type());
+    get<null>(v);
+    null n;
+    get(v, n);
+    V v2(n);
+    ASSERT(v.empty());
+    ASSERT_EQUAL(NULL_TYPE, v.type());
+    v = "foo";
+    ASSERT_EQUAL(STRING, v.type());
+    try { get<null>(v); FAIL("Expected conversion_error"); } catch (conversion_error) {}
+    v = null();
+    get<null>(v);
+}
+
+// Nasty hack for uninterpreted decimal<> types.
+template <class T> T make(const char c) { T x; std::fill(x.begin(), x.end(), c); return x; }
+
+template <class V> void scalar_test_group(int& failed) {
+    // Direct AMQP-mapped types.
+    RUN_TEST(failed, simple_type_test<V>(false, BOOLEAN, "false", true));
+    RUN_TEST(failed, simple_type_test<V>(uint8_t(42), UBYTE, "42", uint8_t(50)));
+    RUN_TEST(failed, simple_type_test<V>(int8_t(-42), BYTE, "-42", int8_t(-40)));
+    RUN_TEST(failed, simple_type_test<V>(uint16_t(4242), USHORT, "4242", uint16_t(5252)));
+    RUN_TEST(failed, simple_type_test<V>(int16_t(-4242), SHORT, "-4242", int16_t(3)));
+    RUN_TEST(failed, simple_type_test<V>(uint32_t(4242), UINT, "4242", uint32_t(5252)));
+    RUN_TEST(failed, simple_type_test<V>(int32_t(-4242), INT, "-4242", int32_t(3)));
+    RUN_TEST(failed, simple_type_test<V>(uint64_t(4242), ULONG, "4242", uint64_t(5252)));
+    RUN_TEST(failed, simple_type_test<V>(int64_t(-4242), LONG, "-4242", int64_t(3)));
+    RUN_TEST(failed, simple_type_test<V>(wchar_t('X'), CHAR, "88", wchar_t('Y')));
+    RUN_TEST(failed, simple_type_test<V>(float(1.234), FLOAT, "1.234", float(2.345)));
+    RUN_TEST(failed, simple_type_test<V>(double(11.2233), DOUBLE, "11.2233", double(12)));
+    RUN_TEST(failed, simple_type_test<V>(timestamp(1234), TIMESTAMP, "1234", timestamp(12345)));
+    RUN_TEST(failed, simple_type_test<V>(make<decimal32>(1), DECIMAL32, "decimal32(0x01010101)", make<decimal32>(2)));
+    RUN_TEST(failed, simple_type_test<V>(make<decimal64>(3), DECIMAL64, "decimal64(0x0303030303030303)", make<decimal64>(4)));
+    RUN_TEST(failed, simple_type_test<V>(make<decimal128>(5), DECIMAL128, "decimal128(0x05050505050505050505050505050505)", make<decimal128>(6)));
+    RUN_TEST(failed, simple_type_test<V>(
+                 uuid::copy("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff"),
+                 UUID, "00112233-4455-6677-8899-aabbccddeeff",
+                 uuid::copy("\xff\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff")));
+    RUN_TEST(failed, simple_type_test<V>(std::string("xxx"), STRING, "xxx", std::string("yyy")));
+    RUN_TEST(failed, simple_type_test<V>(symbol("aaa"), SYMBOL, "aaa", symbol("aaaa")));
+    RUN_TEST(failed, simple_type_test<V>(binary("\010aaa"), BINARY, "b\"\\x08aaa\"", binary("aaaa")));
+
+    // Test native C++ integral types.
+    RUN_TEST(failed, (simple_integral_test<V, char>()));
+    RUN_TEST(failed, (simple_integral_test<V, signed char>()));
+    RUN_TEST(failed, (simple_integral_test<V, unsigned char>()));
+    RUN_TEST(failed, (simple_integral_test<V, short>()));
+    RUN_TEST(failed, (simple_integral_test<V, int>()));
+    RUN_TEST(failed, (simple_integral_test<V, long>()));
+    RUN_TEST(failed, (simple_integral_test<V, unsigned short>()));
+    RUN_TEST(failed, (simple_integral_test<V, unsigned int>()));
+    RUN_TEST(failed, (simple_integral_test<V, unsigned long>()));
+#if PN_CPP_HAS_LONG_LONG
+    RUN_TEST(failed, (simple_integral_test<V, long long>()));
+    RUN_TEST(failed, (simple_integral_test<V, unsigned long long>()));
+#endif
+
+
+    RUN_TEST(failed, (coerce_test<V>()));
+    RUN_TEST(failed, (null_test<V>()));
+    RUN_TEST(failed, (bad_get_test<V>()));
+}
+
+}
+
+#endif // SCALAR_TEST_HPP

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/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 6dc629d..6486e48 100644
--- a/proton-c/bindings/cpp/src/test_bits.hpp
+++ b/proton-c/bindings/cpp/src/test_bits.hpp
@@ -67,7 +67,9 @@ inline void assert_equalish(double want, double got, double delta, const std::st
             ++BAD_COUNT;                                                \
     } while(0)
 
-template<class T> std::string str(const T& x) { std::ostringstream s; s << x; return s.str(); }
+template<class T> std::string str(const T& x) {
+    std::ostringstream s; s << std::boolalpha << x; return s.str();
+}
 
 // A way to easily create literal collections that can be compared to std:: collections
 // and to print std collections

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/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 dd4f108..c5c71a0 100644
--- a/proton-c/bindings/cpp/src/value.cpp
+++ b/proton-c/bindings/cpp/src/value.cpp
@@ -176,8 +176,9 @@ std::ostream& operator<<(std::ostream& o, const internal::value_base& x) {
     if (x.empty())
         return o << "<null>";
     proton::decoder d(x);
-    // Print std::string and proton::foo types using their own operator << consistent with C++.
+    // Print the following types with operator<<() consistent with C++.
     switch (d.next_type()) {
+      case BOOLEAN: return o << get<bool>(d); // Respect std::boolalpha settings.
       case STRING: return o << get<std::string>(d);
       case SYMBOL: return o << get<symbol>(d);
       case DECIMAL32: return o << get<decimal32>(d);
@@ -185,6 +186,7 @@ std::ostream& operator<<(std::ostream& o, const internal::value_base& x) {
       case DECIMAL128: return o << get<decimal128>(d);
       case UUID: return o << get<uuid>(d);
       case TIMESTAMP: return o << get<timestamp>(d);
+      case CHAR: return o << get<wchar_t>(d);
       default:
         // Use pn_inspect for other types.
         return o << d;

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/8d0c5afd/proton-c/bindings/cpp/src/value_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/value_test.cpp b/proton-c/bindings/cpp/src/value_test.cpp
index e37b792..966f415 100644
--- a/proton-c/bindings/cpp/src/value_test.cpp
+++ b/proton-c/bindings/cpp/src/value_test.cpp
@@ -17,10 +17,7 @@
  * under the License.
  */
 
-#include "test_bits.hpp"
-
-#include "proton/types.hpp"
-#include "proton/error.hpp"
+#include "scalar_test.hpp"
 
 namespace {
 
@@ -28,55 +25,29 @@ using namespace std;
 using namespace proton;
 using namespace test;
 
-// Inserting and extracting simple C++ values.
-template <class T> void simple_type_test(T x, type_id tid, const std::string& s, T y) {
-    value vx(x);
-    ASSERT_EQUAL(tid, vx.type());
-    ASSERT_EQUAL(x, get<T>(vx));
-
-    value vxa = x;
-    ASSERT_EQUAL(tid, vxa.type());
-    ASSERT_EQUAL(x, get<T>(vxa));
-
-    value v2;
-    v2 = x;
-    ASSERT_EQUAL(tid, v2.type());
-    ASSERT_EQUAL(x, get<T>(v2));
-
-    value v3(vx);
-    ASSERT_EQUAL(tid, v3.type());
-    ASSERT_EQUAL(x, get<T>(v3));
-
-    value v4 = vx;
-    ASSERT_EQUAL(tid, v4.type());
-    ASSERT_EQUAL(x, get<T>(v4));
-
-
-    ASSERT_EQUAL(vx, v2);
-    ASSERT_EQUAL(s, str(vx));
-    value vy(y);
-    ASSERT(vx != vy);
-    ASSERT(vx < vy);
-    ASSERT(vy > vx);
-}
-
-template <class T> void simple_integral_test() {
-    typedef typename internal::integer_type<sizeof(T), internal::is_signed<T>::value>::type int_type;
-    simple_type_test(T(3), internal::type_id_of<int_type>::value, "3", T(4));
-}
-
 // Inserting and extracting arrays from a container T of type U
 template <class T> void sequence_test(type_id tid, const many<typename T::value_type>& values) {
     T x(values.begin(), values.end());
 
-    value vx(x);                // construt
+    value vx(x);                // construct
     ASSERT_EQUAL(tid, vx.type());
     ASSERT_EQUAL(x, get<T>(vx));
-
+    ASSERT_EQUAL(x, coerce<T>(vx));
+    {
+        T y;
+        get(vx, y);             // Two argument get.
+        ASSERT_EQUAL(x, y);
+    }
+    {
+        T y;
+        coerce(vx, y);          // Two argument coerce.
+        ASSERT_EQUAL(x, y);
+    }
     value v2;                   // assign
     v2 = x;
     ASSERT_EQUAL(tid, v2.type());
     ASSERT_EQUAL(x, get<T>(v2));
+    ASSERT_EQUAL(x, coerce<T>(v2));
     ASSERT_EQUAL(vx, v2);
 
     T y(x);
@@ -96,99 +67,11 @@ template <class T, class U> void map_test(const U& values) {
     ASSERT_EQUAL(m, m2);
 }
 
-template <class T> T make(const char c) {
-    T x; std::fill(x.begin(), x.end(), c); return x;
-}
-
-void null_test() {
-    value v;
-    ASSERT(v.empty());
-    ASSERT_EQUAL(NULL_TYPE, v.type());
-    get<null>(v);
-    null n;
-    get(v, n);
-    value v2(n);
-    ASSERT(v.empty());
-    ASSERT_EQUAL(NULL_TYPE, v.type());
-    v = "foo";
-    ASSERT_EQUAL(STRING, v.type());
-    try { get<null>(v); FAIL("Expected conversion_error"); } catch (conversion_error) {}
-    v = null();
-    get<null>(v);
-}
-
-void get_coerce_test() {
-    // Valid conversions
-    ASSERT_EQUAL(true, coerce<bool>(value(true)));
-
-    ASSERT_EQUAL(1, coerce<uint8_t>(value(uint8_t(1))));
-    ASSERT_EQUAL(-1, coerce<int8_t>(value(int8_t(-1))));
-
-    ASSERT_EQUAL(2, coerce<uint16_t>(value(uint8_t(2))));
-    ASSERT_EQUAL(-2, coerce<int16_t>(value(int8_t(-2))));
-
-    ASSERT_EQUAL(3u, coerce<uint32_t>(value(uint16_t(3))));
-    ASSERT_EQUAL(-3, coerce<int32_t>(value(int16_t(-3))));
-
-    ASSERT_EQUAL(4u, coerce<uint64_t>(value(uint32_t(4))));
-    ASSERT_EQUAL(-4, coerce<int64_t>(value(int32_t(-4))));
-
-    ASSERT_EQUALISH(1.2, coerce<float>(value(double(1.2))), 0.001);
-    ASSERT_EQUALISH(3.4, coerce<double>(value(float(3.4))), 0.001);
-
-    ASSERT_EQUAL(std::string("foo"), coerce<std::string>(value(symbol("foo"))));
-
-    // Bad conversions
-    try { get<bool>(value(int8_t(1))); FAIL("byte as bool"); } catch (conversion_error) {}
-    try { get<uint8_t>(value(true)); FAIL("bool as uint8_t"); } catch (conversion_error) {}
-    try { get<uint8_t>(value(int8_t(1))); FAIL("int8 as uint8"); } catch (conversion_error) {}
-    try { get<int16_t>(value(uint16_t(1))); FAIL("uint16 as int16"); } catch (conversion_error) {}
-    try { get<int16_t>(value(int32_t(1))); FAIL("int32 as int16"); } catch (conversion_error) {}
-    try { get<symbol>(value(std::string())); FAIL("string as symbol"); } catch (conversion_error) {}
-}
-
 }
 
 int main(int, char**) {
     int failed = 0;
-    RUN_TEST(failed, simple_type_test(false, BOOLEAN, "false", true));
-    RUN_TEST(failed, simple_type_test(uint8_t(42), UBYTE, "42", uint8_t(50)));
-    RUN_TEST(failed, simple_type_test(int8_t(-42), BYTE, "-42", int8_t(-40)));
-    RUN_TEST(failed, simple_type_test(uint16_t(4242), USHORT, "4242", uint16_t(5252)));
-    RUN_TEST(failed, simple_type_test(int16_t(-4242), SHORT, "-4242", int16_t(3)));
-    RUN_TEST(failed, simple_type_test(uint32_t(4242), UINT, "4242", uint32_t(5252)));
-    RUN_TEST(failed, simple_type_test(int32_t(-4242), INT, "-4242", int32_t(3)));
-    RUN_TEST(failed, simple_type_test(uint64_t(4242), ULONG, "4242", uint64_t(5252)));
-    RUN_TEST(failed, simple_type_test(int64_t(-4242), LONG, "-4242", int64_t(3)));
-    RUN_TEST(failed, simple_type_test(wchar_t('X'), CHAR, "X", wchar_t('Y')));
-    RUN_TEST(failed, simple_type_test(float(1.234), FLOAT, "1.234", float(2.345)));
-    RUN_TEST(failed, simple_type_test(double(11.2233), DOUBLE, "11.2233", double(12)));
-    RUN_TEST(failed, simple_type_test(timestamp(1234), TIMESTAMP, "1234", timestamp(12345)));
-    RUN_TEST(failed, simple_type_test(make<decimal32>(1), DECIMAL32, "decimal32(0x01010101)", make<decimal32>(2)));
-    RUN_TEST(failed, simple_type_test(make<decimal64>(3), DECIMAL64, "decimal64(0x0303030303030303)", make<decimal64>(4)));
-    RUN_TEST(failed, simple_type_test(make<decimal128>(5), DECIMAL128, "decimal128(0x05050505050505050505050505050505)", make<decimal128>(6)));
-    RUN_TEST(failed, simple_type_test(
-                 uuid::copy("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff"),
-                 UUID, "00112233-4455-6677-8899-aabbccddeeff",
-                 uuid::copy("\xff\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff")));
-    RUN_TEST(failed, simple_type_test(std::string("xxx"), STRING, "xxx", std::string("yyy")));
-    RUN_TEST(failed, simple_type_test(symbol("aaa"), SYMBOL, "aaa", symbol("aaaa")));
-    RUN_TEST(failed, simple_type_test(binary("\010aaa"), BINARY, "b\"\\x08aaa\"", binary("aaaa")));
-
-    // Test native C++ integral types.
-    RUN_TEST(failed, simple_integral_test<char>());
-    RUN_TEST(failed, simple_integral_test<signed char>());
-    RUN_TEST(failed, simple_integral_test<unsigned char>());
-    RUN_TEST(failed, simple_integral_test<short>());
-    RUN_TEST(failed, simple_integral_test<int>());
-    RUN_TEST(failed, simple_integral_test<long>());
-    RUN_TEST(failed, simple_integral_test<unsigned short>());
-    RUN_TEST(failed, simple_integral_test<unsigned int>());
-    RUN_TEST(failed, simple_integral_test<unsigned long>());
-#if PN_CPP_HAS_LONG_LONG
-    RUN_TEST(failed, simple_integral_test<long long>());
-    RUN_TEST(failed, simple_integral_test<unsigned long long>());
-#endif
+    scalar_test_group<value>(failed);
 
     // Sequence tests
     RUN_TEST(failed, sequence_test<std::list<bool> >(ARRAY, many<bool>() + false + true));
@@ -221,8 +104,5 @@ int main(int, char**) {
     RUN_TEST(failed, sequence_test<std::forward_list<binary> >(ARRAY, many<binary>() + binary("xx") + binary("yy")));
     RUN_TEST(failed, (map_test<std::unordered_map<std::string, uint64_t> >(pairs)));
 #endif
-
-    RUN_TEST(failed, get_coerce_test());
-    RUN_TEST(failed, null_test());
     return failed;
 }


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