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 <[email protected]> Authored: Tue May 31 13:11:30 2016 -0400 Committer: Alan Conway <[email protected]> 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: [email protected] For additional commands, e-mail: [email protected]
