$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
Subject: [Boost-commit] svn:boost r70211 - in trunk: boost/spirit/home/qi/detail boost/spirit/home/qi/operator boost/spirit/home/support boost/spirit/home/support/utree libs/spirit/test/qi
From: hartmut.kaiser_at_[hidden]
Date: 2011-03-19 19:42:11
Author: hkaiser
Date: 2011-03-19 19:42:09 EDT (Sat, 19 Mar 2011)
New Revision: 70211
URL: http://svn.boost.org/trac/boost/changeset/70211
Log:
Spirit: more work on Qi container attributes
Text files modified: 
   trunk/boost/spirit/home/qi/detail/pass_container.hpp   |   148 ++++++++++++++++++++++++++++----------- 
   trunk/boost/spirit/home/qi/operator/sequence_base.hpp  |     2                                         
   trunk/boost/spirit/home/support/attributes_fwd.hpp     |     4 +                                       
   trunk/boost/spirit/home/support/utree/utree_traits.hpp |    49 ++++++++++--                            
   trunk/libs/spirit/test/qi/pass_container2.cpp          |    94 ++++++++++++++++++++++++-               
   trunk/libs/spirit/test/qi/utree1.cpp                   |     2                                         
   6 files changed, 238 insertions(+), 61 deletions(-)
Modified: trunk/boost/spirit/home/qi/detail/pass_container.hpp
==============================================================================
--- trunk/boost/spirit/home/qi/detail/pass_container.hpp	(original)
+++ trunk/boost/spirit/home/qi/detail/pass_container.hpp	2011-03-19 19:42:09 EDT (Sat, 19 Mar 2011)
@@ -25,6 +25,15 @@
 
 namespace boost { namespace spirit { namespace qi { namespace detail
 {
+    // Helper meta-function allowing to evaluate weak substitutability and
+    // negate the result if the predicate is not true
+    template <typename Sequence, typename Attribute, typename ValueType>
+    struct negate_weak_substitute_if_not
+      : mpl::if_<
+            Sequence, traits::is_weak_substitute<Attribute, ValueType>
+          , mpl::not_<traits::is_weak_substitute<Attribute, ValueType> > >
+    {};
+
     // pass_through_container: utility to check decide whether a provided 
     // container attribute needs to be passed through to the current component 
     // or of we need to split the container by passing along instances of its
@@ -34,9 +43,9 @@
     // sequence nor a container, we will pass through the provided container 
     // only if its value type is not compatible with the component
     template <typename Container, typename ValueType, typename Attribute
-      , typename Enable = void>
+      , typename Sequence, typename Enable = void>
     struct pass_through_container_base
-      : mpl::not_<traits::is_weak_substitute<Attribute, ValueType> >
+      : negate_weak_substitute_if_not<Sequence, Attribute, ValueType>
     {};
 
     // Specialization for fusion sequences, in this case we check whether all
@@ -45,17 +54,19 @@
     // We return false if the rhs attribute itself is a fusion sequence, which
     // is compatible with the LHS sequence (we want to pass through this 
     // attribute without it being split apart).
-    template <typename Container, typename ValueType, typename Attribute>
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence = mpl::true_>
     struct not_compatible_element
       : mpl::and_<
-            mpl::not_<traits::is_weak_substitute<Attribute, Container> >
-          , mpl::not_<traits::is_weak_substitute<Attribute, ValueType> > >
+            negate_weak_substitute_if_not<Sequence, Attribute, Container>
+          , negate_weak_substitute_if_not<Sequence, Attribute, ValueType> >
     {};
 
     // If the value type of the container is not a Fusion sequence, we pass
     // through the container if each of the elements of the Attribute 
     // sequence is compatible with either the container or its value type.
     template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence
       , bool IsSequence = fusion::traits::is_sequence<ValueType>::value>
     struct pass_through_container_fusion_sequence
     {
@@ -70,16 +81,27 @@
     // If both, the Attribute and the value type of the provided container
     // are Fusion sequences, we pass the container only if the two 
     // sequences are not compatible.
-    template <typename Container, typename ValueType, typename Attribute>
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence>
     struct pass_through_container_fusion_sequence<
-            Container, ValueType, Attribute, true>
-      : mpl::not_<traits::is_weak_substitute<Attribute, ValueType> >
-    {};
+            Container, ValueType, Attribute, Sequence, true>
+    {
+        typedef typename mpl::find_if<
+            Attribute
+          , not_compatible_element<Container, ValueType, mpl::_1, Sequence>
+        >::type iter;
+        typedef typename mpl::end<Attribute>::type end;
+
+        typedef typename is_same<iter, end>::type type;
+    };
 
-    template <typename Container, typename ValueType, typename Attribute>
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence>
     struct pass_through_container_base<Container, ValueType, Attribute
+          , Sequence
           , typename enable_if<fusion::traits::is_sequence<Attribute> >::type>
-      : pass_through_container_fusion_sequence<Container, ValueType, Attribute>
+      : pass_through_container_fusion_sequence<
+            Container, ValueType, Attribute, Sequence>
     {};
 
     // Specialization for containers
@@ -88,7 +110,7 @@
     // a Fusion sequence, we have to pass through the provided container if 
     // both are compatible.
     template <typename Container, typename ValueType, typename Attribute
-      , typename AttributeValueType
+      , typename Sequence, typename AttributeValueType
       , bool IsSequence = fusion::traits::is_sequence<AttributeValueType>::value>
     struct pass_through_container_container
       : mpl::or_<
@@ -98,26 +120,22 @@
 
     // If the value type of the exposed container attribute is a Fusion
     // sequence, we use the already existing logic for those.
-    template <typename Container, typename ValueType
-      , typename Attribute, typename AttributeValueType>
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence, typename AttributeValueType>
     struct pass_through_container_container<
-            Container, ValueType, Attribute, AttributeValueType, true>
+            Container, ValueType, Attribute, Sequence, AttributeValueType, true>
       : pass_through_container_fusion_sequence<
-            Container, ValueType, AttributeValueType>
+            Container, ValueType, AttributeValueType, Sequence>
     {};
 
-    template <typename Container, typename ValueType, typename Attribute>
-    struct pass_through_container_base<Container, ValueType, Attribute
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence>
+    struct pass_through_container_base<
+            Container, ValueType, Attribute, Sequence
           , typename enable_if<traits::is_container<Attribute> >::type>
       : detail::pass_through_container_container<
-          Container, ValueType
-        , Attribute, typename traits::container_value<Attribute>::type>
-    {};
-
-    ///////////////////////////////////////////////////////////////////////////
-    template <typename Container, typename ValueType, typename Attribute>
-    struct pass_through_container
-      : pass_through_container_base<Container, ValueType, Attribute> 
+          Container, ValueType, Attribute, Sequence
+        , typename traits::container_value<Attribute>::type>
     {};
 
     // Specialization for exposed optional attributes
@@ -127,6 +145,7 @@
     // either to the optionals embedded type or to the containers value 
     // type.
     template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence
       , bool IsSequence = fusion::traits::is_sequence<Attribute>::value>
     struct pass_through_container_optional
       : mpl::or_<
@@ -136,25 +155,38 @@
 
     // If the embedded type of the exposed optional attribute is a Fusion
     // sequence, we use the already existing logic for those.
-    template <typename Container, typename ValueType, typename Attribute>
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence>
     struct pass_through_container_optional<
-            Container, ValueType, Attribute, true>
+                Container, ValueType, Attribute, Sequence, true>
       : pass_through_container_fusion_sequence<
-            Container, ValueType, Attribute>
+            Container, ValueType, Attribute, Sequence>
     {};
 
-    template <typename Container, typename ValueType, typename Attribute>
-    struct pass_through_container<Container, ValueType, boost::optional<Attribute> >
-      : pass_through_container_optional<Container, ValueType, Attribute> 
+    ///////////////////////////////////////////////////////////////////////////
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence>
+    struct pass_through_container
+      : pass_through_container_base<Container, ValueType, Attribute, Sequence> 
+    {};
+
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence>
+    struct pass_through_container<
+                Container, ValueType, boost::optional<Attribute>, Sequence>
+      : pass_through_container_optional<
+            Container, ValueType, Attribute, Sequence> 
     {};
 
     // If both, the containers value type and the exposed attribute type are
     // optionals we are allowed to pass through the the container only if the
     // embedded types of those optionals are not compatible.
-    template <typename Container, typename ValueType, typename Attribute>
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence>
     struct pass_through_container<
-            Container, boost::optional<ValueType>, boost::optional<Attribute> >
-      : mpl::not_<traits::is_weak_substitute<Attribute, ValueType> >
+            Container, boost::optional<ValueType>, boost::optional<Attribute>
+          , Sequence>
+      : mpl::not_<traits::is_weak_substitute<Attribute, ValueType> > 
     {};
 
     // Specialization for exposed variant attributes
@@ -164,24 +196,41 @@
 
 #define BOOST_SPIRIT_PASS_THROUGH_CONTAINER(z, N, _)                          \
     pass_through_container<Container, ValueType,                              \
-        BOOST_PP_CAT(T, N)>::type::value ||                                   \
+        BOOST_PP_CAT(T, N), Sequence>::type::value ||                         \
     /***/
 
-    template <typename Container, typename ValueType
+    template <typename Container, typename ValueType, typename Sequence
       , BOOST_VARIANT_ENUM_PARAMS(typename T)>
     struct pass_through_container<Container, ValueType
-          , boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
+          , boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>, Sequence>
       : mpl::bool_<BOOST_PP_REPEAT(BOOST_VARIANT_LIMIT_TYPES
           , BOOST_SPIRIT_PASS_THROUGH_CONTAINER, _) false>
     {};
 
 #undef BOOST_SPIRIT_PASS_THROUGH_CONTAINER
+}}}}
 
+///////////////////////////////////////////////////////////////////////////////
+namespace boost { namespace spirit { namespace traits
+{
+    ///////////////////////////////////////////////////////////////////////////
+    // forwarding customization point for domain qi::domain
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence>
+    struct pass_through_container<
+            Container, ValueType, Attribute, Sequence, qi::domain>
+      : qi::detail::pass_through_container<
+            Container, ValueType, Attribute, Sequence>
+    {};
+}}}
+
+namespace boost { namespace spirit { namespace qi { namespace detail
+{
     ///////////////////////////////////////////////////////////////////////////
     // This function handles the case where the attribute (Attr) given
     // the sequence is an STL container. This is a wrapper around F.
     // The function F does the actual parsing.
-    template <typename F, typename Attr>
+    template <typename F, typename Attr, typename Sequence>
     struct pass_container
     {
         typedef typename F::context_type context_type;
@@ -242,7 +291,8 @@
             // element is a substitute for the value type of the container
             // attribute 
             typedef mpl::and_<
-                pass_through_container<Attr, value_type, rhs_attribute>
+                traits::pass_through_container<
+                    Attr, value_type, rhs_attribute, Sequence, qi::domain>
               , traits::handles_container<
                     Component, Attr, context_type, iterator_type> 
             > predicate;
@@ -278,12 +328,22 @@
         pass_container& operator= (pass_container const&);
     };
 
-    // Utility function to make a pass_container
+    ///////////////////////////////////////////////////////////////////////////
+    // Utility function to make a pass_container for container components 
+    // (kleene, list, plus, repeat)
+    template <typename F, typename Attr>
+    inline pass_container<F, Attr, mpl::false_>
+    make_pass_container(F const& f, Attr& attr)
+    {
+        return pass_container<F, Attr, mpl::false_>(f, attr);
+    }
+
+    // Utility function to make a pass_container for sequences
     template <typename F, typename Attr>
-    pass_container<F, Attr>
-    inline make_pass_container(F const& f, Attr& attr)
+    inline pass_container<F, Attr, mpl::true_>
+    make_sequence_pass_container(F const& f, Attr& attr)
     {
-        return pass_container<F, Attr>(f, attr);
+        return pass_container<F, Attr, mpl::true_>(f, attr);
     }
 }}}}
 
Modified: trunk/boost/spirit/home/qi/operator/sequence_base.hpp
==============================================================================
--- trunk/boost/spirit/home/qi/operator/sequence_base.hpp	(original)
+++ trunk/boost/spirit/home/qi/operator/sequence_base.hpp	2011-03-19 19:42:09 EDT (Sat, 19 Mar 2011)
@@ -104,7 +104,7 @@
             Iterator iter = first;
             // return false if *any* of the parsers fail
             if (fusion::any(elements
-              , detail::make_pass_container(
+              , detail::make_sequence_pass_container(
                     Derived::fail_function(iter, last, context, skipper), attr_))
                 )
                 return false;
Modified: trunk/boost/spirit/home/support/attributes_fwd.hpp
==============================================================================
--- trunk/boost/spirit/home/support/attributes_fwd.hpp	(original)
+++ trunk/boost/spirit/home/support/attributes_fwd.hpp	2011-03-19 19:42:09 EDT (Sat, 19 Mar 2011)
@@ -215,6 +215,10 @@
             , typename Iterator = unused_type, typename Enable = void>
     struct handles_container;
 
+    template <typename Container, typename ValueType, typename Attribute
+      , typename Sequence, typename Domain, typename Enable = void>
+    struct pass_through_container;
+
     ///////////////////////////////////////////////////////////////////////////
     // Qi only
     template <typename Container, typename T, typename Enable = void>
Modified: trunk/boost/spirit/home/support/utree/utree_traits.hpp
==============================================================================
--- trunk/boost/spirit/home/support/utree/utree_traits.hpp	(original)
+++ trunk/boost/spirit/home/support/utree/utree_traits.hpp	2011-03-19 19:42:09 EDT (Sat, 19 Mar 2011)
@@ -586,21 +586,35 @@
     // or a grammar exposes an utree as it's attribute
     namespace detail
     {
-        // checks if the attr is the explicit utree list type, utree::list_type
-        template <typename Attribute>
-        struct attribute_is_not_utree_list
+        // Checks whether the exposed Attribute allows to handle utree or 
+        // utree::list_type directly. Returning mpl::false_ from this meta 
+        // function will force a new utree instance to be created for each
+        // invocation of the embedded parser.
+        template <typename Attribute, typename Enable = void>
+        struct handles_utree_list_container 
           : mpl::and_<
                 mpl::not_<is_same<utree::list_type, Attribute> >,
                 traits::is_container<Attribute> >
         {};
+
+        template <typename Attribute>
+        struct handles_utree_list_container<Attribute
+              , typename enable_if<fusion::traits::is_sequence<Attribute> >::type>
+          : mpl::true_
+        {};
+
+        template <typename Attribute>
+        struct handles_utree_list_container<optional<Attribute> >
+          : mpl::true_
+        {};
     }
 
     template <
         typename IteratorA, typename IteratorB, typename Context
       , typename T1, typename T2, typename T3, typename T4>
     struct handles_container<qi::rule<IteratorA, T1, T2, T3, T4>
-      , utree, Context, IteratorB>
-      : detail::attribute_is_not_utree_list<typename attribute_of<
+          , utree, Context, IteratorB>
+      : detail::handles_utree_list_container<typename attribute_of<
             qi::rule<IteratorA, T1, T2, T3, T4>, Context, IteratorB
         >::type>
     {};
@@ -609,8 +623,8 @@
         typename IteratorA, typename IteratorB, typename Context
       , typename T1, typename T2, typename T3, typename T4>
     struct handles_container<qi::grammar<IteratorA, T1, T2, T3, T4>
-      , utree, Context, IteratorB>
-      : detail::attribute_is_not_utree_list<typename attribute_of<
+          , utree, Context, IteratorB>
+      : detail::handles_utree_list_container<typename attribute_of<
             qi::grammar<IteratorA, T1, T2, T3, T4>, Context, IteratorB
         >::type>
     {};
@@ -619,8 +633,8 @@
         typename IteratorA, typename IteratorB, typename Context
       , typename T1, typename T2, typename T3, typename T4>
     struct handles_container<qi::rule<IteratorA, T1, T2, T3, T4>
-      , utree::list_type, Context, IteratorB>
-      : detail::attribute_is_not_utree_list<typename attribute_of<
+          , utree::list_type, Context, IteratorB>
+      : detail::handles_utree_list_container<typename attribute_of<
             qi::rule<IteratorA, T1, T2, T3, T4>, Context, IteratorB
         >::type>
     {};
@@ -629,13 +643,26 @@
         typename IteratorA, typename IteratorB, typename Context
       , typename T1, typename T2, typename T3, typename T4>
     struct handles_container<qi::grammar<IteratorA, T1, T2, T3, T4>
-      , utree::list_type, Context, IteratorB>
-      : detail::attribute_is_not_utree_list<typename attribute_of<
+          , utree::list_type, Context, IteratorB>
+      : detail::handles_utree_list_container<typename attribute_of<
             qi::grammar<IteratorA, T1, T2, T3, T4>, Context, IteratorB
         >::type>
     {};
 
     ///////////////////////////////////////////////////////////////////////////
+    template <typename Attribute, typename Sequence>
+    struct pass_through_container<
+            utree, utree, Attribute, Sequence, qi::domain>
+      : detail::handles_utree_list_container<Attribute>
+    {};
+
+    template <typename Attribute, typename Sequence>
+    struct pass_through_container<
+            utree::list_type, utree, Attribute, Sequence, qi::domain>
+      : detail::handles_utree_list_container<Attribute>
+    {};
+
+    ///////////////////////////////////////////////////////////////////////////
     namespace detail
     {
         // checks if the attr is utree
Modified: trunk/libs/spirit/test/qi/pass_container2.cpp
==============================================================================
--- trunk/libs/spirit/test/qi/pass_container2.cpp	(original)
+++ trunk/libs/spirit/test/qi/pass_container2.cpp	2011-03-19 19:42:09 EDT (Sat, 19 Mar 2011)
@@ -19,12 +19,15 @@
 #include <boost/spirit/include/qi_directive.hpp>
 #include <boost/spirit/include/qi_action.hpp>
 #include <boost/spirit/include/qi_auxiliary.hpp>
+#include <boost/spirit/include/qi_nonterminal.hpp>
 #include <boost/spirit/include/support_argument.hpp>
 #include <boost/spirit/include/phoenix_core.hpp>
 #include <boost/spirit/include/phoenix_operator.hpp>
 #include <boost/spirit/include/phoenix_object.hpp>
 #include <boost/spirit/include/phoenix_stl.hpp>
+#include <boost/fusion/include/adapt_struct.hpp>
 #include <boost/fusion/include/std_pair.hpp>
+#include <boost/fusion/include/vector.hpp>
 
 #include "test.hpp"
 
@@ -35,6 +38,18 @@
     return v.size() == s.size() && std::equal(v.begin(), v.end(), s.begin());
 }
 
+struct A
+{
+    int i1;
+    double d2;
+};
+
+BOOST_FUSION_ADAPT_STRUCT(
+    A,
+    (int, i1)
+    (double, d2)
+)
+
 int main()
 {
     using boost::spirit::qi::char_;
@@ -157,8 +172,82 @@
         s1.clear();
         BOOST_TEST(test_attr("ab1cd2", *(alpha >> alpha | digit), s1) &&
             s1 == "ab1cd2");
+    }
+
+    {
+        using boost::spirit::qi::rule;
+        using boost::spirit::qi::space;
+        using boost::spirit::qi::space_type;
+        using boost::spirit::qi::int_;
+        using boost::spirit::qi::double_;
+
+        std::vector<A> v;
+        BOOST_TEST(test_attr("A 1 2.0", 'A' >> *(int_ >> double_), v, space) &&
+            v.size() == 1 && v[0].i1 == 1 && v[0].d2 == 2.0);
+
+        v.clear();
+        BOOST_TEST(test_attr("1 2.0", *(int_ >> double_), v, space) &&
+            v.size() == 1 && v[0].i1 == 1 && v[0].d2 == 2.0);
+
+        v.clear();
+        rule<char const*, std::vector<A>()> r = *(int_ >> ',' >> double_);
+        BOOST_TEST(test_attr("1,2.0", r, v) &&
+            v.size() == 1 && v[0].i1 == 1 && v[0].d2 == 2.0);
+    }
+
+    { 
+        using boost::spirit::qi::rule;
+        using boost::spirit::qi::int_;
+        using boost::spirit::qi::double_;
+
+        rule<char const*, A()> r = int_ >> ',' >> double_;
+        rule<char const*, std::vector<A>()> r2 = 'A' >> *(r >> ',' >> r);
+
+        std::vector<A> v;
+        BOOST_TEST(test_attr("A1,2.0,3,4.0", r2, v) &&
+            v.size() == 2 && v[0].i1 == 1.0 && v[0].d2 == 2.0 &&
+                             v[1].i1 == 3.0 && v[1].d2 == 4.0);
+
+        v.clear();
+        BOOST_TEST(test_attr("A1,2.0,3,4.0", 'A' >> *(r >> ',' >> r), v) &&
+            v.size() == 2 && v[0].i1 == 1.0 && v[0].d2 == 2.0 &&
+                             v[1].i1 == 3.0 && v[1].d2 == 4.0);
+
+        v.clear();
+        BOOST_TEST(test_attr("1,2.0,3,4.0", *(r >> ',' >> r), v) &&
+            v.size() == 2 && v[0].i1 == 1.0 && v[0].d2 == 2.0 &&
+                             v[1].i1 == 3.0 && v[1].d2 == 4.0);
+    }
+
+    { 
+        using boost::spirit::qi::rule;
+        using boost::spirit::qi::int_;
+        using boost::spirit::qi::double_;
+        using boost::fusion::at_c;
+
+        typedef boost::fusion::vector<int, double> data_type;
+
+        rule<char const*, data_type()> r = int_ >> ',' >> double_;
+        rule<char const*, std::vector<data_type>()> r2 = 'A' >> *(r >> ',' >> r);
+
+        std::vector<data_type> v;
+        BOOST_TEST(test_attr("A1,2.0,3,4.0", r2, v) &&
+            v.size() == 2 && at_c<0>(v[0]) == 1 && at_c<1>(v[0]) == 2.0 &&
+                             at_c<0>(v[1]) == 3 && at_c<1>(v[1]) == 4.0);
+
+        v.clear();
+        BOOST_TEST(test_attr("A1,2.0,3,4.0", 'A' >> *(r >> ',' >> r), v) &&
+            v.size() == 2 && at_c<0>(v[0]) == 1 && at_c<1>(v[0]) == 2.0 &&
+                             at_c<0>(v[1]) == 3 && at_c<1>(v[1]) == 4.0);
+
+        v.clear();
+        BOOST_TEST(test_attr("1,2.0,3,4.0", *(r >> ',' >> r), v) &&
+            v.size() == 2 && at_c<0>(v[0]) == 1 && at_c<1>(v[0]) == 2.0 &&
+                             at_c<0>(v[1]) == 3 && at_c<1>(v[1]) == 4.0);
+    }
 
 // doesn't work yet
+//     {
 //         std::vector<std::vector<char> > v2;
 //         BOOST_TEST(test_attr("ab1cd123", *(alpha >> alpha | +digit), v2) &&
 //             v2.size() == 4 &&
@@ -174,10 +263,7 @@
 //             v3[1] == "1" &&
 //             v3[2] == "cd" &&
 //             v3[3] == "123");
-    }
-
-    {
-    }
+//     }
 
     return boost::report_errors();
 }
Modified: trunk/libs/spirit/test/qi/utree1.cpp
==============================================================================
--- trunk/libs/spirit/test/qi/utree1.cpp	(original)
+++ trunk/libs/spirit/test/qi/utree1.cpp	2011-03-19 19:42:09 EDT (Sat, 19 Mar 2011)
@@ -8,9 +8,9 @@
 #include <boost/config/warning_disable.hpp>
 #include <boost/detail/lightweight_test.hpp>
 
+#include <boost/mpl/print.hpp>
 #include <boost/spirit/include/qi.hpp>
 #include <boost/spirit/include/support_utree.hpp>
-#include <boost/mpl/print.hpp>
 
 #include <sstream>