NO-JIRA: C++: Conversions for complex types

Intuitive automatic conversions between standard C++ containers and  AMQP types.
User can override for non-standard containers or non-default use of standard 
containers.

Examples in encode_decode.cpp.


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

Branch: refs/heads/go1
Commit: 01e136cc660a721da9178a98ff43bac143a4c8ce
Parents: ecbe90a
Author: Alan Conway <[email protected]>
Authored: Thu Nov 5 09:43:45 2015 -0500
Committer: Alan Conway <[email protected]>
Committed: Tue Nov 10 17:32:40 2015 -0500

----------------------------------------------------------------------
 examples/cpp/direct_send.cpp                    |   2 +-
 examples/cpp/encode_decode.cpp                  | 159 +++++-----
 examples/cpp/example_test.py                    |  24 +-
 examples/cpp/simple_send.cpp                    |   2 +-
 .../bindings/cpp/include/proton/decoder.hpp     | 287 +++++++++++++++----
 .../bindings/cpp/include/proton/encoder.hpp     | 103 +++++--
 proton-c/bindings/cpp/include/proton/engine.hpp |   2 +-
 .../bindings/cpp/include/proton/message.hpp     |  18 +-
 .../bindings/cpp/include/proton/message_id.hpp  |   3 +-
 .../cpp/include/proton/request_response.hpp     |   3 -
 .../bindings/cpp/include/proton/type_traits.hpp |  19 +-
 proton-c/bindings/cpp/include/proton/types.hpp  |  43 +--
 proton-c/bindings/cpp/include/proton/value.hpp  |  32 ++-
 proton-c/bindings/cpp/src/decoder.cpp           |  23 +-
 proton-c/bindings/cpp/src/encoder.cpp           |   8 +-
 proton-c/bindings/cpp/src/interop_test.cpp      |   6 +-
 proton-c/bindings/cpp/src/message.cpp           |   2 +-
 proton-c/bindings/cpp/src/value.cpp             |  16 +-
 18 files changed, 501 insertions(+), 251 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/examples/cpp/direct_send.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/direct_send.cpp b/examples/cpp/direct_send.cpp
index 62e6ab9..13ca510 100644
--- a/examples/cpp/direct_send.cpp
+++ b/examples/cpp/direct_send.cpp
@@ -53,7 +53,7 @@ class simple_send : public proton::messaging_handler {
             msg.id(sent + 1);
             std::map<std::string, int> m;
             m["sequence"] = sent+1;
-            msg.body(proton::as<proton::MAP>(m));
+            msg.body(m);
             sender.send(msg);
             sent++;
         }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/examples/cpp/encode_decode.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/encode_decode.cpp b/examples/cpp/encode_decode.cpp
index 4f88c93..3d6fe48 100644
--- a/examples/cpp/encode_decode.cpp
+++ b/examples/cpp/encode_decode.cpp
@@ -24,8 +24,8 @@
 #include <map>
 #include <sstream>
 #include <vector>
+#include <list>
 
-using namespace std;
 
 // Examples of how to use the encoder and decoder to create and examine AMQP 
values.
 //
@@ -34,12 +34,18 @@ using namespace std;
 // values from a decoder in terms of their simple components.
 void print(proton::value&);
 
-// Some helper templates to print map and vector results.
+// Some helper templates to print map and std::vector results.
 namespace std {
-template<class T, class U> ostream& operator<<(ostream& o, const pair<T,U>& p) 
{
+template<class T, class U> ostream& operator<<(ostream& o, const 
std::pair<T,U>& p) {
     return o << p.first << ":" << p.second;
 }
-template<class T> ostream& operator<<(ostream& o, const vector<T>& v) {
+template<class T> ostream& operator<<(ostream& o, const std::vector<T>& v) {
+    o << "[ ";
+    ostream_iterator<T> oi(o, " ");
+    copy(v.begin(), v.end(), oi);
+    return o << "]";
+}
+template<class T> ostream& operator<<(ostream& o, const std::list<T>& v) {
     o << "[ ";
     ostream_iterator<T> oi(o, " ");
     copy(v.begin(), v.end(), oi);
@@ -47,85 +53,109 @@ template<class T> ostream& operator<<(ostream& o, const 
vector<T>& v) {
 }
 template<class K, class T> ostream& operator<<(ostream& o, const map<K, T>& m) 
{
     o << "{ ";
-    ostream_iterator<pair<K,T> > oi(o, " ");
+    ostream_iterator<std::pair<K,T> > oi(o, " ");
     copy(m.begin(), m.end(), oi);
     return o << "}";
 }
 }
 
-// Insert/extract C++ containers.
-void insert_extract_containers() {
-    cout << endl << "== Array, list and map." << endl;
+// Insert/extract native C++ containers with uniform type values.
+void uniform_containers() {
+    std::cout << std::endl << "== Array, list and map of uniform type." << 
std::endl;
+    proton::value v;
 
-    vector<int> a;
+    std::vector<int> a;
     a.push_back(1);
     a.push_back(2);
     a.push_back(3);
-    vector<int> l;
-    l.push_back(4);
-    l.push_back(5);
-    map<string, int> m;
-    m["one"] = 1;
-    m["two"] = 2;
+    // By default a C++ container is encoded as an AMQP array.
+    v = a;
+    print(v);
+    std::list<int> a1 = v;          // Decode as a C++ std::list instead
+    std::cout << a1 << std::endl;
 
-    proton::value v;
-    v.encoder() << proton::as<proton::ARRAY>(a) << proton::as<proton::LIST>(l) 
<< proton::as<proton::MAP>(m);
+    // You can specify that a container should be encoded as an AMQP list 
instead.
+    v = proton::as<proton::LIST>(a1);
     print(v);
+    std::cout << v.get<std::vector<int> >() << std::endl;
 
-    vector<int> a1, l1;
-    map<string, int> m1;
-    v.decoder().rewind();
-    v.decoder() >> proton::as<proton::ARRAY>(a1) >> 
proton::as<proton::LIST>(l1) >> proton::as<proton::MAP>(m1);
-    cout << "Extracted: " << a1 << ", " << l1 << ", " << m1 << endl;
+    // C++ map types (types with key_type, mapped_type) convert to an AMQP map 
by default.
+    std::map<std::string, int> m;
+    m["one"] = 1;
+    m["two"] = 2;
+    v = m;
+    print(v);
+    std::cout << v.get<std::map<std::string, int> >() << std::endl;
+
+    // You can convert a sequence of pairs to an AMQP map if you need to 
control the
+    // encoded ordering.
+    std::vector<std::pair<std::string, int> > pairs;
+    pairs.push_back(std::make_pair("z", 3));
+    pairs.push_back(std::make_pair("a", 4));
+    v = proton::as<proton::MAP>(pairs);
+    print(v);
+    // You can also decode an AMQP map as a sequence of pairs using decoder() 
and proton::to_pairs
+    std::vector<std::pair<std::string, int> > pairs2;
+    v.decoder() >> proton::to_pairs(pairs2);
+    std::cout << pairs2 << std::endl;
 }
 
-// Containers with mixed types, use value to represent arbitrary AMQP types.
+// Containers with mixed types use value to represent arbitrary AMQP types.
 void mixed_containers() {
-    cout << endl << "== List and map of mixed type values." << endl;
-    vector<proton::value> l;
+    std::cout << std::endl << "== List and map of mixed type values." << 
std::endl;
+    proton::value v;
+
+    std::vector<proton::value> l;
     l.push_back(proton::value(42));
     l.push_back(proton::value(proton::amqp_string("foo")));
-    map<proton::value, proton::value> m;
+    // By default, a sequence of proton::value is treated as an AMQP list.
+    v = l;
+    print(v);
+    std::vector<proton::value> l2 = v;
+    std::cout << l2 << std::endl;
+
+    std::map<proton::value, proton::value> m;
     m[proton::value("five")] = proton::value(5);
     m[proton::value(4)] = proton::value("four");
-    proton::value v;
-    v.encoder() << proton::as<proton::LIST>(l) << proton::as<proton::MAP>(m);
+    v = m;
     print(v);
-
-    vector<proton::value> l1;
-    map<proton::value, proton::value> m1;
-    v.decoder().rewind();
-    v.decoder() >> proton::as<proton::LIST>(l1) >> proton::as<proton::MAP>(m1);
-    cout << "Extracted: " << l1 << ", " << m1 << endl;
+    std::map<proton::value, proton::value> m2 = v;
+    std::cout << m2 << std::endl;
 }
 
 // Insert using stream operators (see print_next for example of extracting 
with stream ops.)
-void insert_extract_stream_operators() {
-    cout << endl << "== Insert with stream operators." << endl;
+void insert_stream_operators() {
+    std::cout << std::endl << "== Insert with stream operators." << std::endl;
     proton::value v;
-    // Note: array elements must be encoded with the exact type, they are not
-    // automaticlly converted. Mismatched types for array elements will not
-    // be detected until v.encode() is called.
-    v.encoder() << proton::start::array(proton::INT) << proton::amqp_int(1) << 
proton::amqp_int(2) << proton::amqp_int(3) << proton::finish();
+
+    // Create an array of INT with values [1, 2, 3]
+    v.encoder() << proton::start::array(proton::INT)
+                << proton::amqp_int(1) << proton::amqp_int(2) << 
proton::amqp_int(3)
+                << proton::finish();
     print(v);
 
-    v.clear();
-    v.encoder() << proton::start::list() << proton::amqp_int(42) << false << 
proton::amqp_symbol("x") << proton::finish();
+    // Create a mixed-type list of the values [42, false, "x"].
+    v.encoder() << proton::start::list()
+                << proton::amqp_int(42) << false << proton::amqp_symbol("x")
+                << proton::finish();
     print(v);
 
-    v.clear();
-    v.encoder() << proton::start::map() << "k1" << proton::amqp_int(42) << 
proton::amqp_symbol("k2") << false << proton::finish();
+    // Create a map { "k1":42, "k2": false }
+    v.encoder() << proton::start::map()
+                << "k1" << proton::amqp_int(42)
+                << proton::amqp_symbol("k2") << false
+                << proton::finish();
     print(v);
 }
 
 int main(int, char**) {
     try {
-        insert_extract_containers();
+        uniform_containers();
         mixed_containers();
-        insert_extract_stream_operators();
+        insert_stream_operators();
         return 0;
-    } catch (const exception& e) {
-        cerr << endl << "error: " << e.what() << endl;
+    } catch (const std::exception& e) {
+        std::cerr << std::endl << "error: " << e.what() << std::endl;
     }
     return 1;
 }
@@ -141,47 +171,47 @@ void print_next(proton::decoder& d) {
     switch (type) {
       case proton::ARRAY: {
           d >> s;
-          cout << "array<" << s.element;
+          std::cout << "array<" << s.element;
           if (s.is_described) {
-              cout  << ", descriptor=";
+              std::cout  << ", descriptor=";
               print_next(d);
           }
-          cout << ">[";
+          std::cout << ">[";
           for (size_t i = 0; i < s.size; ++i) {
-              if (i) cout << ", ";
+              if (i) std::cout << ", ";
               print_next(d);
           }
-          cout << "]";
+          std::cout << "]";
           d >> proton::finish();
           break;
       }
       case proton::LIST: {
           d >> s;
-          cout << "list[";
+          std::cout << "list[";
           for (size_t i = 0; i < s.size; ++i) {
-              if (i) cout << ", ";
+              if (i) std::cout << ", ";
               print_next(d);
           }
-          cout << "]";
+          std::cout << "]";
           d >> proton::finish();
           break;
       }
       case proton::MAP: {
           d >> s;
-          cout << "map{";
+          std::cout << "map{";
           for (size_t i = 0; i < s.size/2; ++i) {
-              if (i) cout << ", ";
+              if (i) std::cout << ", ";
               print_next(d);
-              cout << ":";        // key:value
+              std::cout << ":";        // key:value
               print_next(d);
           }
-          cout << "}";
+          std::cout << "}";
           d >> proton::finish();
           break;
       }
       case proton::DESCRIBED: {
           d >> s;
-          cout << "described(";
+          std::cout << "described(";
           print_next(d);      // Descriptor
           print_next(d);      // value
           d >> proton::finish();
@@ -192,7 +222,7 @@ void print_next(proton::decoder& d) {
         // we will take a short cut and extract to another value and print 
that.
         proton::value v2;
         d >> v2;
-        cout << type << "(" << v2 << ")";
+        std::cout << type << "(" << v2 << ")";
     }
 }
 
@@ -200,10 +230,9 @@ void print_next(proton::decoder& d) {
 void print(proton::value& v) {
     proton::decoder& d = v.decoder();
     d.rewind();
-    cout << "Values: ";
     while (d.more()) {
         print_next(d);
-        if (d.more()) cout << ", ";
+        if (d.more()) std::cout << ", ";
     }
-    cout << endl;
+    std::cout << std::endl;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/examples/cpp/example_test.py
----------------------------------------------------------------------
diff --git a/examples/cpp/example_test.py b/examples/cpp/example_test.py
index f51cf57..bc552ab 100644
--- a/examples/cpp/example_test.py
+++ b/examples/cpp/example_test.py
@@ -199,18 +199,26 @@ class ExampleTest(unittest.TestCase):
 
     def test_encode_decode(self):
         expect="""
-== Array, list and map.
-Values: array<int>[int(1), int(2), int(3)], list[int(4), int(5)], 
map{string(one):int(1), string(two):int(2)}
-Extracted: [ 1 2 3 ], [ 4 5 ], { one:1 two:2 }
+== Array, list and map of uniform type.
+array<int>[int(1), int(2), int(3)]
+[ 1 2 3 ]
+list[int(1), int(2), int(3)]
+[ 1 2 3 ]
+map{string(one):int(1), string(two):int(2)}
+{ one:1 two:2 }
+map{string(z):int(3), string(a):int(4)}
+[ z:3 a:4 ]
 
 == List and map of mixed type values.
-Values: list[int(42), string(foo)], map{int(4):string(four), 
string(five):int(5)}
-Extracted: [ 42 foo ], { 4:four five:5 }
+list[int(42), string(foo)]
+[ 42 foo ]
+map{int(4):string(four), string(five):int(5)}
+{ 4:four five:5 }
 
 == Insert with stream operators.
-Values: array<int>[int(1), int(2), int(3)]
-Values: list[int(42), boolean(false), symbol(x)]
-Values: map{string(k1):int(42), symbol(k2):boolean(false)}
+array<int>[int(1), int(2), int(3)]
+list[int(42), boolean(false), symbol(x)]
+map{string(k1):int(42), symbol(k2):boolean(false)}
 """
         self.maxDiff = None
         self.assertEqual(expect, execute("encode_decode"))

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/examples/cpp/simple_send.cpp
----------------------------------------------------------------------
diff --git a/examples/cpp/simple_send.cpp b/examples/cpp/simple_send.cpp
index cb33207..31e2c73 100644
--- a/examples/cpp/simple_send.cpp
+++ b/examples/cpp/simple_send.cpp
@@ -51,7 +51,7 @@ class simple_send : public proton::messaging_handler {
             msg.id(sent + 1);
             std::map<std::string, int> m;
             m["sequence"] = sent+1;
-            msg.body(proton::as<proton::MAP>(m));
+            msg.body(m);
             sender.send(msg);
             sent++;
         }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/decoder.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/decoder.hpp 
b/proton-c/bindings/cpp/include/proton/decoder.hpp
index 33ddb4f..1d105e9 100644
--- a/proton-c/bindings/cpp/include/proton/decoder.hpp
+++ b/proton-c/bindings/cpp/include/proton/decoder.hpp
@@ -23,51 +23,130 @@
 #include "proton/type_traits.hpp"
 #include "proton/types.hpp"
 #include "proton/facade.hpp"
+
 #include <iosfwd>
 
+#ifndef PN_NO_CONTAINER_CONVERT
+
+#include <vector>
+#include <deque>
+#include <list>
+#include <map>
+
+#if PN_HAS_CPP11
+#include <array>
+#include <forward_list>
+#include <unordered_map>
+#endif // PN_HAS_CPP11
+
+#endif // PN_NO_CONTAINER_CONVERT
+
 struct pn_data_t;
 
 namespace proton {
 
-class data;
 class message_id;
 
 /** Raised by decoder operations on error.*/
 struct decode_error : public error { PN_CPP_EXTERN explicit decode_error(const 
std::string&) throw(); };
 
-/** Skips a value with `decoder >> skip()`. */
+/** Skips a value with `dec >> skip()`. */
 struct skip{};
 
-/** Rewind the decoder with `decoder >> rewind()`. */
+/** Assert the next type of value in the decoder: `dec >> assert_type(t)`
+ *  throws if decoder.type() != t
+ */
+struct assert_type {
+    type_id type;
+    assert_type(type_id t) : type(t) {}
+};
+
+/** Rewind the decoder with `dec >> rewind()`. */
 struct rewind{};
 
 /**
- * Stream-like decoder from AMQP bytes to a stream of C++ values.
- *
- * types.h defines C++ types corresponding to AMQP types.
- *
- * decoder operator>> will extract AMQP types into corresponding C++ types, and
- * do simple conversions, e.g. from AMQP integer types to corresponding or
- * larger C++ integer types.
- *
- * You can require an exact AMQP type using the `as<type>(value)` helper. E.g.
- *
- *     amqp_int i;
- *     decoder >> as<INT>(i):       // Will throw if decoder does not contain 
an INT
- *
- * You can also use the `as` helper to extract an AMQP list, array or map into 
C++ containers.
- *
- *
- *     std::vector<amqp_int> v;
- *     decoder >> as<LIST>(v);     // Extract a list of INT.
- *
- * AMQP maps can be inserted/extracted to any container with pair<X,Y> as
- * value_type, which includes std::map and std::unordered_map but also for
- * example std::vector<std::pair<X,Y> >. This allows you to preserve order when
- * extracting AMQP maps.
- *
- * You can also extract container values element-by-element, see 
decoder::operator>>(decoder&, start&)
- *
+Stream-like decoder from AMQP bytes to C++ values.
+
+@see types.hpp defines C++ types corresponding to AMQP types.
+
+The decoder operator>> will extract AMQP types into any compatible C++
+type or throw an exception if the types are not compatible.
+
++-------------------------+-------------------------------+
+|AMQP type                |Compatible C++ types           |
++=========================+===============================+
+|BOOLEAN                  |amqp_boolean, bool             |
++-------------------------+-------------------------------+
+|signed integer type I    |C++ signed integer type T where|
+|                         |sizeof(T) >= sizeof(I)         |
++-------------------------+-------------------------------+
+|unsigned integer type U  |C++ unsigned integer type T    |
+|                         |where sizeof(T) >= sizeof(U)   |
++-------------------------+-------------------------------+
+|CHAR                     |amqp_char, wchar_t             |
++-------------------------+-------------------------------+
+|FLOAT                    |amqp_float, float              |
++-------------------------+-------------------------------+
+|DOUBLE                   |amqp_double, double            |
++-------------------------+-------------------------------+
+|STRING                   |amqp_string, std::string       |
++-------------------------+-------------------------------+
+|SYMBOL                   |amqp_symbol, std::string       |
++-------------------------+-------------------------------+
+|BINARY                   |amqp_binary, std::string       |
++-------------------------+-------------------------------+
+|DECIMAL<n>               |amqp_decimal<n>                |
++-------------------------+-------------------------------+
+|TIMESTAMP                |amqp_timestamp                 |
++-------------------------+-------------------------------+
+|UUID                     |amqp_uuid                      |
++-------------------------+-------------------------------+
+
+The special proton::value type can hold any AMQP type, simple or compound.
+
+By default operator >> will do any conversion that does not lose data. For 
example
+any AMQP signed integer type can be extracted as follows:
+
+    int64_t i;
+    dec >> i;
+
+You can assert the exact AMQP type with proton::assert_type, for example
+the following will throw if the AMQP type is not an AMQP INT (32 bits)
+
+    amqp_int i;
+    dec >> assert_type(INT) >> i;       // Will throw if decoder does not 
contain an INT
+
+You can extract AMQP ARRAY, LIST or MAP into standard C++ containers of 
compatible types, for example:
+
+    std::vector<int32_t> v;
+    dec >> v;
+
+This will work if the decoder contains an AMQP ARRAY or LIST of SHORT or INT 
values. It won't work
+for LONG or other types. It will also work for a MAP with keys and values that 
are SHORT OR INT,
+the map will be "flattened" into a sequence [ key1, value1, key2, value2 ] 
This will work with
+std::dequeue, std::array, std::list or std::forward_list.
+
+You can extract a MAP into a std::map or std::unordered_map
+
+    std::map<std::string, std::string> v;
+    dec >> v;
+
+This will work for any AMQP map with keys and values that are STRING, SYMBOL 
or BINARY.
+
+If you have non-standard container types that meet the most basic requirements 
for
+the container or associative-container concepts, you can use them via helper 
functions:
+
+    my_sequence_type<int64_t> s;
+    dec >> proton::to_sequence(s); // Decode sequence of integers
+    my_map_type<amqp_string, bool> s;
+    dec >> proton::to_map(s); // Decode map of string: bool.
+
+Finally you can extract an AMQP LIST with mixed type elements into a container 
of proton::value, e.g.
+
+    std::vector<proton::value> v;
+    dec >> v;
+
+You can also extract container values element-by-element, see 
decoder::operator>>(decoder&, start&)
 */
 class decoder : public facade<pn_data_t, decoder> {
   public:
@@ -124,14 +203,11 @@ class decoder : public facade<pn_data_t, decoder> {
     PN_CPP_EXTERN friend decoder& operator>>(decoder&, amqp_uuid&);
     PN_CPP_EXTERN friend decoder& operator>>(decoder&, std::string&);
     PN_CPP_EXTERN friend decoder& operator>>(decoder&, message_id&);
-    PN_CPP_EXTERN friend decoder& operator>>(decoder&, class data&);
+    PN_CPP_EXTERN friend decoder& operator>>(decoder&, value&);
     ///@}
 
     /** Extract and return a value of type T. */
-    template <class T> T get() { T value; *this >> value; return value; }
-
-    /** Extract and return a value of type T, as AMQP type. */
-    template <class T, type_id A> T get_as() { T value; *this >> as<A>(value); 
return value; }
+    template <class T> T extract() { T value; *this >> value; return value; }
 
     /** Call decoder::start() in constructor, decoder::finish in destructor().
      *
@@ -142,21 +218,15 @@ class decoder : public facade<pn_data_t, decoder> {
         ~scope() { decoder_ >> finish(); }
     };
 
-    template <type_id A, class T> friend decoder& operator>>(decoder& d, 
ref<T, A> ref) {
-        d.check_type(A);
-        d >> ref.value;
-        return d;
-    }
-
     /** start extracting a container value, one of array, list, map, described.
      * The basic pattern is:
      *
      *     start s;
-     *     decoder >> s;
+     *     dec >> s;
      *     // check s.type() to see if this is an ARRAY, LIST, MAP or 
DESCRIBED type.
      *     if (s.described) extract the descriptor...
      *     for (size_t i = 0; i < s.size(); ++i) Extract each element...
-     *     decoder >> finish();
+     *     dec >> finish();
      *
      * The first value of an ARRAY is a descriptor if start::descriptor is 
true,
      * followed by start.size elements of type start::element.
@@ -182,6 +252,9 @@ class decoder : public facade<pn_data_t, decoder> {
     /** Skip a value */
     PN_CPP_EXTERN friend decoder& operator>>(decoder&, skip);
 
+    /** Throw an exception if decoder.type() != assert_type.type */
+    PN_CPP_EXTERN friend decoder& operator>>(decoder&, assert_type);
+
     /** Rewind to the beginning */
     PN_CPP_EXTERN friend decoder& operator>>(decoder&, struct rewind);
 
@@ -191,46 +264,138 @@ class decoder : public facade<pn_data_t, decoder> {
   friend class encoder;
 };
 
+
 // operator >> for integer types that are not covered by the standard 
overrides.
 template <class T>
-typename enable_if<is_unknown_integer<T>::value, decoder&>::type 
operator>>(decoder& d, T& i)  {
+typename enable_if<is_unknown_integer<T>::value, decoder&>::type
+operator>>(decoder& d, T& i)  {
     typename integer_type<sizeof(T), is_signed<T>::value>::type v;
     d >> v;                     // Extract as a known integer type
     i = v;
     return d;
 }
 
-template <class T> decoder& operator>>(decoder& d, ref<T, ARRAY> ref)  {
+///@cond INTERNAL
+template <class T> struct sequence_ref {
+    sequence_ref(T& v) : value(v) {}
+    T& value;
+};
+
+template <class T> struct map_ref {
+    map_ref(T& v) : value(v) {}
+    T& value;
+};
+
+template <class T> struct pairs_ref {
+    pairs_ref(T& v) : value(v) {}
+    T& value;
+};
+///@endcond
+
+/**
+ * Return a wrapper for a C++ container to be decoded as a sequence. The AMQP
+ * ARRAY, LIST, and MAP types can all be decoded as a sequence, a map will be
+ * decoded as alternating key and value (provided they can both be converted to
+ * the container's value_type)
+ *
+ * The following expressions must be valid for T t;
+ *     T::iterator
+ *     t.clear()
+ *     t.resize()
+ *     t.begin()
+ *     t.end()
+ */
+template <class T> sequence_ref<T> to_sequence(T& v) { return 
sequence_ref<T>(v); }
+
+/** Return a wrapper for a C++ map container to be decoded from an AMQP MAP.
+ * The following expressions must be valid for T t;
+ *     T::key_type
+ *     T::mapped_type
+ *     t.clear()
+ *     T::key_type k; T::mapped_type v; t[k] = v;
+ */
+template <class T> map_ref<T> to_map(T& v) { return map_ref<T>(v); }
+
+/** Return a wrapper for a C++ container of std::pair that can be decoded from 
AMQP maps,
+ * preserving the encoded map order.
+ *
+ * The following expressions must be valid for T t;
+ *     T::iterator
+ *     t.clear()
+ *     t.resize()
+ *     t.begin()
+ *     t.end()
+ *     T::iterator i; i->first; i->second
+ */
+template <class T> pairs_ref<T> to_pairs(T& v) { return pairs_ref<T>(v); }
+
+/** Extract any AMQP sequence (ARRAY, LIST or MAP) to a C++ container of T if
+ * the elements types are convertible to T. A MAP is extracted as [key1, 
value1,
+ * key2, value2...]
+ */
+template <class T> decoder& operator>>(decoder& d, sequence_ref<T> ref)  {
     decoder::scope s(d);
     if (s.is_described) d >> skip();
-    ref.value.clear();
-    ref.value.resize(s.size);
-    for (typename T::iterator i = ref.value.begin(); i != ref.value.end(); 
++i) {
+    T& v = ref.value;
+    v.clear();
+    v.resize(s.size);
+    for (typename T::iterator i = v.begin(); i != v.end(); ++i)
         d >> *i;
-    }
     return d;
 }
 
-template <class T> decoder& operator>>(decoder& d, ref<T, LIST> ref)  {
+void assert_map_scope(const decoder::scope& s);
+
+/** Extract an AMQP MAP to a C++ map */
+template <class T> decoder& operator>>(decoder& d, map_ref<T> ref)  {
     decoder::scope s(d);
-    ref.value.clear();
-    ref.value.resize(s.size);
-    for (typename T::iterator i = ref.value.begin(); i != ref.value.end(); ++i)
-        d >> *i;
+    assert_map_scope(s);
+    T& m = ref.value;
+    m.clear();
+    for (size_t i = 0; i < s.size/2; ++i) {
+        typename remove_const<typename T::key_type>::type k;
+        typename remove_const<typename T::mapped_type>::type v;
+        d >> k >> v;
+        m[k] = v;
+    }
     return d;
 }
 
-template <class T> decoder& operator>>(decoder& d, ref<T, MAP> ref)  {
-    decoder::scope m(d);
-    ref.value.clear();
-    for (size_t i = 0; i < m.size/2; ++i) {
-        typename T::key_type k;
-        typename T::mapped_type v;
-        d >> k >> v;
-        ref.value[k] = v;
+/** Extract an AMQP MAP to a C++ container of std::pair, preserving order. */
+template <class T> decoder& operator>>(decoder& d, pairs_ref<T> ref)  {
+    decoder::scope s(d);
+    assert_map_scope(s);
+    T& m = ref.value;
+    m.clear();
+    m.resize(s.size/2);
+    for (typename T::iterator i = m.begin(); i != m.end(); ++i) {
+        d >> i->first >> i->second;
     }
     return d;
 }
 
+#ifndef PN_NO_CONTAINER_CONVERT
+
+// Decode to sequence.
+template <class T, class A> decoder& operator>>(decoder &d, std::vector<T, A>& 
v) { return d >> to_sequence(v); }
+template <class T, class A> decoder& operator>>(decoder &d, std::deque<T, A>& 
v) { return d >> to_sequence(v); }
+template <class T, class A> decoder& operator>>(decoder &d, std::list<T, A>& 
v) { return d >> to_sequence(v); }
+
+// Decode to map.
+template <class K, class T, class C, class A> decoder& operator>>(decoder &d, 
std::map<K, T, C, A>& v) { return d >> to_map(v); }
+
+#if PN_HAS_CPP11
+
+// Decode to sequence.
+template <class T, class A> decoder& operator>>(decoder &d, 
std::forward_list<T, A>& v) { return d >> to_sequence(v); }
+template <class T, std::size_t N> decoder& operator>>(decoder &d, 
std::array<T, N>& v) { return d >> to_sequence(v); }
+
+// Decode to map.
+template <class K, class T, class C, class A> decoder& operator>>(decoder &d, 
std::unordered_map<K, T, C, A>& v) { return d >> to_map(v); }
+
+#endif // PN_HAS_CPP11
+#endif // PN_NO_CONTAINER_CONVERT
+
 }
+
 #endif // DECODER_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/encoder.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/encoder.hpp 
b/proton-c/bindings/cpp/include/proton/encoder.hpp
index 24684c1..7dd7ddb 100644
--- a/proton-c/bindings/cpp/include/proton/encoder.hpp
+++ b/proton-c/bindings/cpp/include/proton/encoder.hpp
@@ -25,39 +25,68 @@
 #include "proton/facade.hpp"
 #include <iosfwd>
 
+#ifndef PN_NO_CONTAINER_CONVERT
+
+#include <vector>
+#include <deque>
+#include <list>
+#include <map>
+
+#if PN_HAS_CPP11
+#include <array>
+#include <forward_list>
+#include <unordered_map>
+#endif // PN_HAS_CPP11
+
+#endif // PN_NO_CONTAINER_CONVERT
+
 struct pn_data_t;
 
 namespace proton {
 
-class data;
 class message_id;
 
 /** Raised by encoder operations on error */
 struct encode_error : public error { PN_CPP_EXTERN explicit encode_error(const 
std::string&) throw(); };
 
 /**
- * Stream C++ data values into an AMQP encoder using operator<<.
+ * Stream-like encoder from C++ values to AMQP values.
  *
- * types.h defines C++ typedefs and types for AMQP each type. These types 
insert
- * as the corresponding AMQP type. Conversion rules apply to other types:
+ * types.hpp defines a C++ type for each AMQP type. For simple types they are
+ * just typedefs for corresponding native C++ types. These types encode as the
+ * corresponding AMQP type.
  *
- * - Integer types insert as the AMQP integer of matching size and signedness.
- * - std::string or char* insert as AMQP strings.
+ * There are some special case conversions:
  *
- * C++ containers can be inserted as AMQP containers with the as() helper
- * functions. For example:
+ * - Integer types other than those mentioned in types.hpp encode as the AMQP
+ *   integer type of matching size and signedness.
+ * - std::string or char* insert as AMQP STRING.
  *
- *     std::vector<amqp_symbol> v;
- *     encoder << as<amqp_list>(v);
+ * For example to encode an AMQP INT, BOOLEAN and STRING these are equivalent:
  *
- * AMQP maps can be inserted from any container with std::pair<X,Y> as the
- * value_type. That includes std::map and std::unordered_map but also for
- * example std::vector<std::pair<X,Y> >. This allows you to control the order
- * of elements when inserting AMQP maps.
+ *     enc << proton::amqp_int(1) << proton::amqp_boolean(true) << 
proton::amqp_string("foo");
+ *     enc << int32_t(1) << true << "foo";
  *
- * You can also insert containers element-by-element, see operator<<(encoder&, 
const start&)
+ * You can force the encoding using the `proton::as` template function, for 
example:
+ *
+ *     uint64_t i = 100;
+ *     enc << as<proton::SHORT>(i);
+ *
+ * C++ standard containers can be inserted. By default:
+ *
+ * - std::map and std::unordered_map encode as AMQP MAP
+ * - std::vector, std::deque, std::list, std::array or std::forward_list 
encode as an AMQP ARRAY.
+ * - std::vector<proton::value> etc. encode as AMQP LIST
+ *
+ * Again you can force the encoding using proton::as<LIST>() or 
proton::as<ARRAY>()
+ *
+ * Note that you can encode a sequence of pairs as a map, which allows you to 
control the
+ * encoded order if that is important:
+ *
+ *     std::vector<std::pair<T1, T2> > v;
+ *     enc << proton::as<MAP>(v);
  *
- *@throw decoder::error if the curent value is not a container type.
+ * You can also insert containers element-by-element, see operator<<(encoder&, 
const start&)
  */
 class encoder : public facade<pn_data_t, encoder> {
   public:
@@ -107,7 +136,7 @@ class encoder : public facade<pn_data_t, encoder> {
   friend PN_CPP_EXTERN encoder& operator<<(encoder&, amqp_symbol);
   friend PN_CPP_EXTERN encoder& operator<<(encoder&, amqp_binary);
   friend PN_CPP_EXTERN encoder& operator<<(encoder&, const message_id&);
-  friend PN_CPP_EXTERN encoder& operator<<(encoder&, const class data&);
+  friend PN_CPP_EXTERN encoder& operator<<(encoder&, const value&);
     ///@}
 
     /**
@@ -118,7 +147,7 @@ class encoder : public facade<pn_data_t, encoder> {
      * and insert it into the encoder, followed by the contained elements.  For
      * example:
      *
-     *      encoder << start::list() << amqp_int(1) << amqp_symbol("two") << 
3.0 << finish();
+     *      enc << start::list() << amqp_int(1) << amqp_symbol("two") << 3.0 
<< finish();
      */
   friend PN_CPP_EXTERN encoder& operator<<(encoder&, const start&);
 
@@ -135,9 +164,6 @@ class encoder : public facade<pn_data_t, encoder> {
   template <class T> friend encoder& operator<<(encoder&, cref<T, MAP>);
     // TODO aconway 2015-06-16: described values.
     ///@}
-
-    /** Copy data from a raw pn_data_t */
-  friend PN_CPP_EXTERN encoder& operator<<(encoder&, pn_data_t*);
 };
 
 // Need to disambiguate char* conversion to bool and std::string as 
amqp_string.
@@ -179,11 +205,36 @@ template <class T> encoder& operator<<(encoder& e, 
cref<T, MAP> m){
     e << finish();
     return e;
 }
-///@cond INTERNAL Convert a ref to a cref.
-template <class T, type_id A> encoder& operator<<(encoder& e, ref<T, A> ref) {
-    return e << cref<T,A>(ref);
-}
-///@endcond
 
+#ifndef PN_NO_CONTAINER_CONVERT
+// Encode as ARRAY
+template <class T, class A> encoder& operator<<(encoder &e, const 
std::vector<T, A>& v) { return e << as<ARRAY>(v); }
+template <class T, class A> encoder& operator<<(encoder &e, const 
std::deque<T, A>& v) { return e << as<ARRAY>(v); }
+template <class T, class A> encoder& operator<<(encoder &e, const std::list<T, 
A>& v) { return e << as<ARRAY>(v); }
+
+// Encode as LIST
+template <class A> encoder& operator<<(encoder &e, const std::vector<value, 
A>& v) { return e << as<LIST>(v); }
+template <class A> encoder& operator<<(encoder &e, const std::deque<value, A>& 
v) { return e << as<LIST>(v); }
+template <class A> encoder& operator<<(encoder &e, const std::list<value, A>& 
v) { return e << as<LIST>(v); }
+
+// Encode as MAP
+template <class K, class T, class C, class A> encoder& operator<<(encoder &e, 
const std::map<K, T, C, A>& v) { return e << as<MAP>(v); }
+
+#if PN_HAS_CPP11
+
+// Encode as ARRAY.
+template <class T, class A> encoder& operator<<(encoder &e, const 
std::forward_list<T, A>& v) { return e << as<ARRAY>(v); }
+template <class T, std::size_t N> encoder& operator<<(encoder &e, const 
std::array<T, N>& v) { return e << as<ARRAY>(v); }n
+
+// Encode as LIST.
+template <class value, class A> encoder& operator<<(encoder &e, const 
std::forward_list<value, A>& v) { return e << as<LIST>(v); }
+template <class value, std::size_t N> encoder& operator<<(encoder &e, const 
std::array<value, N>& v) { return e << as<LIST>(v); }
+
+// Encode as map.
+template <class K, class T, class C, class A> encoder& operator<<(encoder &e, 
const std::unordered_map<K, T, C, A>& v) { return e << as<MAP>(v); }
+
+#endif // PN_HAS_CPP11
+
+#endif // PN_NO_CONTAINER_CONVERT
 }
 #endif // ENCODER_H

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/engine.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/engine.hpp 
b/proton-c/bindings/cpp/include/proton/engine.hpp
index 672e911..f27033d 100644
--- a/proton-c/bindings/cpp/include/proton/engine.hpp
+++ b/proton-c/bindings/cpp/include/proton/engine.hpp
@@ -31,7 +31,7 @@ namespace proton {
 class handler;
 class connection;
 
-/// Pointers to a data buffer.
+/// Pointers to a byte range to use as a buffer.
 template <class T> class buffer {
   public:
     explicit buffer(T* begin__=0, T* end__=0) : begin_(begin__), end_(end__) {}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/message.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/message.hpp 
b/proton-c/bindings/cpp/include/proton/message.hpp
index e9862c6..d996f85 100644
--- a/proton-c/bindings/cpp/include/proton/message.hpp
+++ b/proton-c/bindings/cpp/include/proton/message.hpp
@@ -26,6 +26,7 @@
 #include "proton/message_id.hpp"
 #include "proton/data.hpp"
 #include "proton/pn_unique_ptr.hpp"
+#include "proton/value.hpp"
 
 #include <string>
 #include <utility>
@@ -95,32 +96,31 @@ class message
     PN_CPP_EXTERN std::string reply_to_group_id() const;
     ///@}
 
-    /** Set the body. If data has more than one value, each is encoded as an 
AMQP section. */
-    PN_CPP_EXTERN void body(const data&);
+    /** Set the body. */
+    PN_CPP_EXTERN void body(const value&);
 
-    /** Set the body to any type T that can be converted to proton::data */
-    template <class T> void body(const T& v) { body().clear(); 
body().encoder() << v; }
-
-    /** Get the body values. */
+    /** Get the body. Note data can be copied to a proton::value */
     PN_CPP_EXTERN const data& body() const;
 
     /** Get a reference to the body data, can be modified in-place. */
     PN_CPP_EXTERN data& body();
 
-    /** Encode into memory starting at buffer.first and ending before 
buffer.second */
+    // FIXME aconway 2015-11-10: use buffer
+    /** Encode message into memory starting at buffer.first and ending before 
buffer.second */
     PN_CPP_EXTERN void encode(std::pair<char*, char*> buffer);
 
     /** Encode into a string, growing the string if necessary. */
-    PN_CPP_EXTERN void encode(std::string &data) const;
+    PN_CPP_EXTERN void encode(std::string &bytes) const;
 
     /** Return encoded message as a string */
     PN_CPP_EXTERN std::string encode() const;
 
+    // FIXME aconway 2015-11-10: use buffer
     /** Decode from memory starting at buffer.first and ending before 
buffer.second */
     PN_CPP_EXTERN void decode(std::pair<const char*, const char*> buffer);
 
     /** Decode from string data into the message. */
-    PN_CPP_EXTERN void decode(const std::string &data);
+    PN_CPP_EXTERN void decode(const std::string &bytes);
 
     /// Decode the message from link corresponding to delivery.
     PN_CPP_EXTERN void decode(proton::link&, proton::delivery&);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/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 a472afd..4ff8b52 100644
--- a/proton-c/bindings/cpp/include/proton/message_id.hpp
+++ b/proton-c/bindings/cpp/include/proton/message_id.hpp
@@ -29,6 +29,8 @@ namespace proton {
 class message_id : public comparable<message_id> {
   public:
     message_id() {}
+    message_id(const value& x) : value_(x) {}
+    message_id(const data& x) : value_(x) {}
     message_id(const uint64_t& x) : value_(x) {}
     message_id(const amqp_uuid& x) : value_(x) {}
     message_id(const amqp_binary& x) : value_(x) {}
@@ -71,7 +73,6 @@ class message_id : public comparable<message_id> {
   friend PN_CPP_EXTERN decoder& operator>>(decoder&, message_id&);
 
   private:
-    message_id(const value& v) : value_(v) {}
     value value_;
   friend class message;
 };

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/request_response.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/request_response.hpp 
b/proton-c/bindings/cpp/include/proton/request_response.hpp
index 957c91a..58b8156 100644
--- a/proton-c/bindings/cpp/include/proton/request_response.hpp
+++ b/proton-c/bindings/cpp/include/proton/request_response.hpp
@@ -29,9 +29,6 @@
 
 #include <string>
 
-struct pn_message_t;
-struct pn_data_t;
-
 namespace proton {
 
 /**

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/type_traits.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/type_traits.hpp 
b/proton-c/bindings/cpp/include/proton/type_traits.hpp
index bebf0b1..d12e0ed 100644
--- a/proton-c/bindings/cpp/include/proton/type_traits.hpp
+++ b/proton-c/bindings/cpp/include/proton/type_traits.hpp
@@ -30,7 +30,9 @@
 #include "proton/types.hpp"
 
 namespace proton {
-template <bool, class T=void> struct enable_if;
+class value;
+
+template <bool, class T=void> struct enable_if {};
 template <class T> struct enable_if<true, T> { typedef T type; };
 
 struct true_type { static const bool value = true; };
@@ -67,8 +69,15 @@ template <> struct is_signed<unsigned long long> : public 
false_type {};
 template <> struct is_signed<signed long long> : public true_type {};
 #endif
 
-// Metafunction returning exact AMQP type associated with a C++ type
-template <class T> struct type_id_of;
+template <class T, class U> struct is_same { static const bool value=false; };
+template <class T> struct is_same<T,T> { static const bool value=true; };
+
+
+template< class T > struct remove_const          { typedef T type; };
+template< class T > struct remove_const<const T> { typedef T type; };
+
+// Metafunction returning AMQP type for basic C++ types
+template <class T, class Enable=void> struct type_id_of;
 template<> struct type_id_of<amqp_null> { static const type_id value=NULL_; };
 template<> struct type_id_of<amqp_boolean> { static const type_id 
value=BOOLEAN; };
 template<> struct type_id_of<amqp_ubyte> { static const type_id value=UBYTE; };
@@ -91,12 +100,12 @@ template<> struct type_id_of<amqp_binary> { static const 
type_id value=BINARY; }
 template<> struct type_id_of<amqp_string> { static const type_id value=STRING; 
};
 template<> struct type_id_of<amqp_symbol> { static const type_id value=SYMBOL; 
};
 
-template <class T, class Enable=void> struct has_type_id { static const bool 
value = false; };
+template <class T, class Enable=void> struct has_type_id : public false_type 
{};
 template <class T> struct has_type_id<T, typename 
enable_if<!!type_id_of<T>::value>::type>  {
     static const bool value = true;
 };
 
-// amqp_map to known integer types by sizeof and signedness.
+// Map arbitrary integral types to know AMQP integral types.
 template<size_t N, bool S> struct integer_type;
 template<> struct integer_type<1, true> { typedef amqp_byte type; };
 template<> struct integer_type<2, true> { typedef amqp_short type; };

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/include/proton/types.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/types.hpp 
b/proton-c/bindings/cpp/include/proton/types.hpp
index 0f82ae3..b9fedd1 100644
--- a/proton-c/bindings/cpp/include/proton/types.hpp
+++ b/proton-c/bindings/cpp/include/proton/types.hpp
@@ -159,51 +159,22 @@ struct amqp_timestamp : public comparable<amqp_timestamp> 
{
 };
 
 ///@cond INTERNAL
-template<class T, type_id A> struct type_pair {
+template<class T, type_id A> struct cref {
     typedef T cpp_type;
-    type_id type;
-};
-
-template<class T, type_id A> struct ref : public type_pair<T, A> {
-    ref(T& v) : value(v) {}
-    T& value;
-};
+    static const type_id type;
 
-template<class T, type_id A> struct cref : public type_pair<T, A> {
     cref(const T& v) : value(v) {}
-    cref(const ref<T,A>& ref) : value(ref.value) {}
     const T& value;
 };
+template <class T, type_id A> const type_id cref<T, A>::type = A;
 ///@endcond INTERNAL
 
-/** A holder for AMQP values. A holder is always encoded/decoded as its 
amqp_value, no need
- * for the as<TYPE>() helper functions.
- *
- * For example to encode an array of arrays using std::vector:
- *
- *     typedef holder<std::vector<amqp_string>, ARRAY> Inner;
- *     typedef holder<std::vector<Inner>, ARRAY> Outer;
- *     Outer o ...
- *     encoder << o;
- *
- */
-template<class T, type_id A> struct holder : public type_pair<T, A> {
-    T value;
-};
-
-/** Create a reference to value as AMQP type A for decoding.
- * For example to decode an array of amqp_int:
- *
- *     std::vector<amqp_int> v;
- *     decoder >> as<ARRAY>(v);
- */
-template <type_id A, class T> ref<T, A> as(T& value) { return ref<T, 
A>(value); }
-
-/** Create a const reference to value as AMQP type A for encoding.
- * For example to encode an array of amqp_int:
+/**
+ * Indicate the desired AMQP type to use when encoding T.
+ * For example to encode a vector as a list:
  *
  *     std::vector<amqp_int> v;
- *     encoder << as<ARRAY>(v);
+ *     encoder << as<LIST>(v);
  */
 template <type_id A, class T> cref<T, A> as(const T& value) { return cref<T, 
A>(value); }
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/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 8816064..ddc5501 100644
--- a/proton-c/bindings/cpp/include/proton/value.hpp
+++ b/proton-c/bindings/cpp/include/proton/value.hpp
@@ -30,11 +30,23 @@ class data;
 class encoder;
 class decoder;
 
-/** AMQP data  with normal value semantics: copy, assign etc. */
+/**
+ * Holder for an AMQP value.
+ *
+ * proton::value can hold any AMQP data value, simple or compound.  It has
+ * assignment and conversion operators to convert its contents easily to and
+ * from native C++ types.
+ *
+ * See proton::encoder and proton::decoder for details of the conversion rules.
+ * Assigning to a proton::value follows the encoder rules, converting from a
+ * proton::value (or calling proton::value::get) follows the decoder rules.
+ */
 class value : public comparable<value> {
   public:
     PN_CPP_EXTERN value();
     PN_CPP_EXTERN value(const value& x);
+    PN_CPP_EXTERN value(const data&);
+
     template <class T> value(const T& x) : data_(data::create()) { *data_ = x; 
}
 
     PN_CPP_EXTERN value& operator=(const value& x);
@@ -44,23 +56,26 @@ class value : public comparable<value> {
     PN_CPP_EXTERN void clear();
     PN_CPP_EXTERN bool empty() const;
 
-    /** Encoder to encode complex data into this value.
-     * Note if you enocde more than one value, all but the first will be 
ignored.
-     */
+    // FIXME aconway 2015-11-06: rename encode/decode
+
+    /** Encoder to encode complex data into this value. Note this clears the 
value. */
     PN_CPP_EXTERN class encoder& encoder();
 
-    /** Decoder to decode complex data from this value */
-    PN_CPP_EXTERN class decoder& decoder();
+    /** Decoder to decode complex data from this value. Note this rewinds the 
decoder. */
+    PN_CPP_EXTERN class decoder& decoder() const;
 
     /** Type of the current value*/
     PN_CPP_EXTERN type_id type() const;
 
     /** Get the value. */
-    template<class T> void get(T &t) const { rewind() >> t; }
+    template<class T> void get(T &t) const { decoder() >> t; }
 
     /** Get the value. */
     template<class T> T get() const { T t; get(t); return t; }
 
+    /** Automatic conversion */
+    template<class T> operator T() const { return get<T>(); }
+
     PN_CPP_EXTERN bool operator==(const value& x) const;
     PN_CPP_EXTERN bool operator<(const value& x) const;
 
@@ -69,9 +84,6 @@ class value : public comparable<value> {
   friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream& o, const value& 
dv);
 
   private:
-    value(const data&);
-    class decoder& rewind() const { data_->decoder().rewind(); return 
data_->decoder(); }
-
     pn_unique_ptr<data> data_;
   friend class message;
 };

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/src/decoder.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/decoder.cpp 
b/proton-c/bindings/cpp/src/decoder.cpp
index d148b12..1fe13a2 100644
--- a/proton-c/bindings/cpp/src/decoder.cpp
+++ b/proton-c/bindings/cpp/src/decoder.cpp
@@ -147,16 +147,19 @@ decoder& operator>>(decoder& d, finish) { 
pn_data_exit(pn_cast(&d)); return d; }
 
 decoder& operator>>(decoder& d, skip) { pn_data_next(pn_cast(&d)); return d; }
 
+decoder& operator>>(decoder& d, assert_type a) { bad_type(a.type, d.type()); 
return d; }
+
 decoder& operator>>(decoder& d, rewind) { d.rewind(); return d; }
 
-decoder& operator>>(decoder& d, data& v) {
-    if (pn_cast(&d) == pn_cast(&v)) throw decode_error("extract into self");
-    v.clear();
+decoder& operator>>(decoder& d, value& v) {
+    pn_data_t *ddata = pn_cast(&d);
+    pn_data_t *vdata = pn_cast(&v.encoder());
+    if (ddata == vdata) throw decode_error("extract into self");
     {
-        narrow n(pn_cast(&d));
-        check(pn_data_appendn(pn_cast(&v), pn_cast(&d), 1));
+        narrow n(ddata);
+        check(pn_data_appendn(vdata, ddata, 1));
     }
-    if (!pn_data_next(pn_cast(&d))) throw decode_error("no more data");
+    if (!pn_data_next(ddata)) throw decode_error("no more data");
     return d;
 }
 
@@ -340,4 +343,12 @@ decoder& operator>>(decoder& d, std::string& value) {
     return d;
 }
 
+void assert_map_scope(const decoder::scope& s) {
+    if (s.type != MAP)
+        throw decode_error("cannot decode "+type_name(s.type)+" as map");
+    if (s.size % 2 != 0)
+        throw decode_error("odd number of elements in map");
+}
+
+
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/src/encoder.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/encoder.cpp 
b/proton-c/bindings/cpp/src/encoder.cpp
index 0230e88..c8e6928 100644
--- a/proton-c/bindings/cpp/src/encoder.cpp
+++ b/proton-c/bindings/cpp/src/encoder.cpp
@@ -130,9 +130,11 @@ encoder& operator<<(encoder& e, amqp_string value) { 
return insert(e, pn_cast(&e
 encoder& operator<<(encoder& e, amqp_symbol value) { return insert(e, 
pn_cast(&e), value, pn_data_put_symbol); }
 encoder& operator<<(encoder& e, amqp_binary value) { return insert(e, 
pn_cast(&e), value, pn_data_put_binary); }
 
-encoder& operator<<(encoder& e, const data& v) {
-    if (pn_cast(&e) == pn_cast(&v)) throw encode_error("cannot insert into 
self");
-    check(pn_data_append(pn_cast(&e), pn_cast(&v)), pn_cast(&e));
+encoder& operator<<(encoder& e, const value& v) {
+    pn_data_t *edata = pn_cast(&e);
+    pn_data_t *vdata = pn_cast(&v.decoder());
+    if (edata == vdata) throw encode_error("cannot insert into self");
+    check(pn_data_append(edata, vdata), edata);
     return e;
 }
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/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 ac2701f..0bac03c 100644
--- a/proton-c/bindings/cpp/src/interop_test.cpp
+++ b/proton-c/bindings/cpp/src/interop_test.cpp
@@ -39,7 +39,11 @@ string read(string filename) {
     return string(istreambuf_iterator<char>(ifs), istreambuf_iterator<char>());
 }
 
-template <class T> T get(decoder& d) { return d.get_as<T, 
type_id_of<T>::value>(); }
+template <class T> T get(decoder& d) {
+    T v;
+    d >> assert_type(type_id_of<T>::value) >> v;
+    return v;
+}
 
 template <class T> std::string str(const T& value) {
     ostringstream oss;

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/proton-c/bindings/cpp/src/message.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/message.cpp 
b/proton-c/bindings/cpp/src/message.cpp
index 63f6520..7ebb821 100644
--- a/proton-c/bindings/cpp/src/message.cpp
+++ b/proton-c/bindings/cpp/src/message.cpp
@@ -161,7 +161,7 @@ std::string message::reply_to_group_id() const {
     return s ? std::string(s) : std::string();
 }
 
-void message::body(const data& v) { body() = v; }
+void message::body(const value& v) { body() = v; }
 
 const data& message::body() const {
     return *data::cast(pn_message_body(message_));

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/01e136cc/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 6b4c402..7059d69 100644
--- a/proton-c/bindings/cpp/src/value.cpp
+++ b/proton-c/bindings/cpp/src/value.cpp
@@ -40,18 +40,16 @@ void value::clear() { data_->clear(); }
 
 bool value::empty() const { return data_->empty(); }
 
-class encoder& value::encoder() { return data_->encoder(); }
+class encoder& value::encoder() { clear(); return data_->encoder(); }
 
-class decoder& value::decoder() { return data_->decoder(); }
+class decoder& value::decoder() const { data_->decoder().rewind(); return 
data_->decoder(); }
 
-type_id value::type() const { return rewind().type(); }
+type_id value::type() const { return decoder().type(); }
 
 bool value::operator==(const value& x) const { return *data_ == *x.data_; }
 
 bool value::operator<(const value& x) const { return *data_ < *x.data_; }
 
-
-
 std::ostream& operator<<(std::ostream& o, const value& v) {
     // pn_inspect prints strings with quotes which is not normal in C++.
     switch (v.type()) {
@@ -63,12 +61,4 @@ std::ostream& operator<<(std::ostream& o, const value& v) {
     }
 }
 
-class encoder& operator<<(class encoder& e, const value& v) {
-    return e << *v.data_;
-}
-
-class decoder& operator>>(class decoder& d, value& v) {
-    return d >> *v.data_;
-}
-
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to