| 1 | // Copyright Shunsuke Sogame 2007.
|
|---|
| 2 | // Distributed under the Boost Software License, Version 1.0.
|
|---|
| 3 | // (See accompanying file LICENSE_1_0.txt or copy at
|
|---|
| 4 | // http://www.boost.org/LICENSE_1_0.txt)
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 | // A Defect Report:
|
|---|
| 9 | // value_of applied to transform_view doesn't always work.
|
|---|
| 10 | //
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 | #include <boost/detail/lightweight_test.hpp>
|
|---|
| 14 | #include <boost/fusion/include/at.hpp>
|
|---|
| 15 | #include <boost/fusion/include/transform.hpp>
|
|---|
| 16 | #include <boost/fusion/include/list.hpp>
|
|---|
| 17 | #include <boost/fusion/include/as_vector.hpp>
|
|---|
| 18 | #include <boost/utility/result_of.hpp>
|
|---|
| 19 | #include <string>
|
|---|
| 20 |
|
|---|
| 21 | namespace fusion = boost::fusion;
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 | // 1-1. Background
|
|---|
| 26 | //
|
|---|
| 27 |
|
|---|
| 28 | // Let's a fusion_zip_iterator which is a Fusion version of boost::zip_iterator.
|
|---|
| 29 | // This iterator is internally used by make_fusion_zip_range.
|
|---|
| 30 | // For example,
|
|---|
| 31 | //
|
|---|
| 32 | // std::cout <<
|
|---|
| 33 | // make_fusion_zip_range( fusion::make_list(std::string("123"), std::string("abc")) );
|
|---|
| 34 | //
|
|---|
| 35 | // outputs: {(1 a),(2 b),(3 c)}
|
|---|
| 36 |
|
|---|
| 37 | // For simplicity, assume you've already written a right fusion_zip_iterator.
|
|---|
| 38 | // Note that, in general, it is a bad idea for iterator adaptor to hold
|
|---|
| 39 | // a reference to a Container/Range, because input Range may be a temporary proxy
|
|---|
| 40 | // something like boost::iterator_range.
|
|---|
| 41 | // So, this iterator must hold not a View but a Vector. Ditto boost::zip_iterator.
|
|---|
| 42 | template <typename FusionVectorOfStlIterators>
|
|---|
| 43 | struct fusion_zip_iterator
|
|---|
| 44 | {
|
|---|
| 45 | explicit fusion_zip_iterator(FusionVectorOfStlIterators const& iters)
|
|---|
| 46 | : m_stl_iters(iters)
|
|---|
| 47 | {}
|
|---|
| 48 |
|
|---|
| 49 | FusionVectorOfStlIterators m_stl_iters;
|
|---|
| 50 |
|
|---|
| 51 | // iterator implementations
|
|---|
| 52 | // ...
|
|---|
| 53 | };
|
|---|
| 54 |
|
|---|
| 55 | // Now, you have to make a fusion_zip_iterator object holding begin iterators.
|
|---|
| 56 | // For simplicity, Boost.Range isn't used in this example.
|
|---|
| 57 |
|
|---|
| 58 | // result_of-conforming FunctionObject to get begin iterator.
|
|---|
| 59 | struct T_mybegin
|
|---|
| 60 | {
|
|---|
| 61 | template <typename Signature>
|
|---|
| 62 | struct result;
|
|---|
| 63 |
|
|---|
| 64 | template <typename This, typename Container>
|
|---|
| 65 | struct result<This(Container&)> // mutable lvalue
|
|---|
| 66 | {
|
|---|
| 67 | typedef typename Container::iterator type;
|
|---|
| 68 | };
|
|---|
| 69 |
|
|---|
| 70 | template <typename This, typename Container>
|
|---|
| 71 | struct result<This(Container const&)> // const lvalue
|
|---|
| 72 | {
|
|---|
| 73 | typedef typename Container::const_iterator type;
|
|---|
| 74 | };
|
|---|
| 75 |
|
|---|
| 76 | template <typename This, typename Container>
|
|---|
| 77 | struct result<This(Container)> // rvalue
|
|---|
| 78 | {
|
|---|
| 79 | typedef typename Container::const_iterator type;
|
|---|
| 80 | };
|
|---|
| 81 |
|
|---|
| 82 | template <typename Container>
|
|---|
| 83 | typename Container::iterator operator()(Container& c) const
|
|---|
| 84 | {
|
|---|
| 85 | return c.begin();
|
|---|
| 86 | }
|
|---|
| 87 |
|
|---|
| 88 | template <typename Container>
|
|---|
| 89 | typename Container::const_iterator operator()(Container const& c) const
|
|---|
| 90 | {
|
|---|
| 91 | return c.begin();
|
|---|
| 92 | }
|
|---|
| 93 | };
|
|---|
| 94 |
|
|---|
| 95 | T_mybegin const mybegin = {};
|
|---|
| 96 |
|
|---|
| 97 | template <typename SequenceOfContainers, typename Begin>
|
|---|
| 98 | fusion_zip_iterator<
|
|---|
| 99 | typename fusion::result_of::as_vector<
|
|---|
| 100 | fusion::transform_view<SequenceOfContainers, Begin>
|
|---|
| 101 | >::type
|
|---|
| 102 | >
|
|---|
| 103 | make_fusion_zip_iterator(SequenceOfContainers& seq, Begin beg)
|
|---|
| 104 | {
|
|---|
| 105 | return
|
|---|
| 106 | fusion_zip_iterator<
|
|---|
| 107 | typename fusion::result_of::as_vector<
|
|---|
| 108 | fusion::transform_view<SequenceOfContainers, Begin>
|
|---|
| 109 | >::type
|
|---|
| 110 | >(
|
|---|
| 111 | fusion::as_vector(
|
|---|
| 112 | fusion::transform_view<SequenceOfContainers, Begin>(seq, beg)
|
|---|
| 113 | )
|
|---|
| 114 | );
|
|---|
| 115 | }
|
|---|
| 116 |
|
|---|
| 117 |
|
|---|
| 118 | // Surprisingly, this doesn't compile for now.
|
|---|
| 119 | void test_mutability_of_stl_iters()
|
|---|
| 120 | {
|
|---|
| 121 | fusion::list<std::string, std::string> seq("abc", "efg");
|
|---|
| 122 |
|
|---|
| 123 | #if 0
|
|---|
| 124 | // compile error: you cannot assign to a variable that is const.
|
|---|
| 125 | *fusion::at_c<0>(make_fusion_zip_iterator(seq, mybegin).m_stl_iters) = 'x';
|
|---|
| 126 | BOOST_TEST(*fusion::at_c<0>(make_fusion_zip_iterator(seq, mybegin).m_stl_iters) == 'x');
|
|---|
| 127 | #endif
|
|---|
| 128 | }
|
|---|
| 129 |
|
|---|
| 130 | // This is because fusion::as_vector calls boost::result_of(through fusion::value_of)
|
|---|
| 131 | // in non-conforming manner.
|
|---|
| 132 | // In this case, `result_of<T_mybegin(std::string)>` is called, which means its arument is rvalue.
|
|---|
| 133 | // my_begin can't get a mutable STL iterator from rvalue Container.
|
|---|
| 134 |
|
|---|
| 135 |
|
|---|
| 136 |
|
|---|
| 137 | // 1-2. A current workaround
|
|---|
| 138 | //
|
|---|
| 139 |
|
|---|
| 140 | // A "cheating" mybegin is needed for as_vector.
|
|---|
| 141 | struct T_mybegin_cheat
|
|---|
| 142 | {
|
|---|
| 143 | template <typename Signature>
|
|---|
| 144 | struct result;
|
|---|
| 145 |
|
|---|
| 146 | template <typename This, typename Container>
|
|---|
| 147 | struct result<This(Container&)> // mutable lvalue
|
|---|
| 148 | {
|
|---|
| 149 | typedef typename Container::iterator type;
|
|---|
| 150 | };
|
|---|
| 151 |
|
|---|
| 152 | template <typename This, typename Container>
|
|---|
| 153 | struct result<This(Container const&)> // const lvalue
|
|---|
| 154 | {
|
|---|
| 155 | typedef typename Container::const_iterator type;
|
|---|
| 156 | };
|
|---|
| 157 |
|
|---|
| 158 | template <typename This, typename Container>
|
|---|
| 159 | struct result<This(Container)> // rvalue
|
|---|
| 160 | {
|
|---|
| 161 | typedef typename Container::iterator type; // *cheating!*
|
|---|
| 162 | };
|
|---|
| 163 |
|
|---|
| 164 | template <typename Container>
|
|---|
| 165 | typename Container::iterator operator()(Container& c) const
|
|---|
| 166 | {
|
|---|
| 167 | return c.begin();
|
|---|
| 168 | }
|
|---|
| 169 |
|
|---|
| 170 | template <typename Container>
|
|---|
| 171 | typename Container::const_iterator operator()(Container const& c) const
|
|---|
| 172 | {
|
|---|
| 173 | return c.begin();
|
|---|
| 174 | }
|
|---|
| 175 | };
|
|---|
| 176 |
|
|---|
| 177 | T_mybegin_cheat const mybegin_cheat = {};
|
|---|
| 178 |
|
|---|
| 179 | void test_mutability_of_stl_iters_cheat()
|
|---|
| 180 | {
|
|---|
| 181 | fusion::list<std::string, std::string> seq("abc", "efg");
|
|---|
| 182 |
|
|---|
| 183 | // ok!
|
|---|
| 184 | *fusion::at_c<0>(make_fusion_zip_iterator(seq, mybegin_cheat).m_stl_iters) = 'x';
|
|---|
| 185 | BOOST_TEST(*fusion::at_c<0>(make_fusion_zip_iterator(seq, mybegin_cheat).m_stl_iters) == 'x');
|
|---|
| 186 | }
|
|---|
| 187 |
|
|---|
| 188 | // This workaround is clearly a bad resolution.
|
|---|
| 189 | // You have to write a cheating FunctionObject which is not result_of-conforming.
|
|---|
| 190 | // (According to lawyers, such a cheating FunctionObject is not well-formed under TR1.)
|
|---|
| 191 | // Also, transform_view and as_vector is tightly-bonded.
|
|---|
| 192 | // (Notice transform_view with mybegin_cheat is broken.)
|
|---|
| 193 |
|
|---|
| 194 |
|
|---|
| 195 |
|
|---|
| 196 | // 2-1. Defect Summary
|
|---|
| 197 | //
|
|---|
| 198 |
|
|---|
| 199 | // A FunctionObject with transform_view can't determine its `value_of` behavior.
|
|---|
| 200 |
|
|---|
| 201 |
|
|---|
| 202 |
|
|---|
| 203 | // 2-2. A Simple Example
|
|---|
| 204 | //
|
|---|
| 205 |
|
|---|
| 206 | struct identity
|
|---|
| 207 | {
|
|---|
| 208 | template<class FunCall>
|
|---|
| 209 | struct result;
|
|---|
| 210 |
|
|---|
| 211 | template<class Fun>
|
|---|
| 212 | struct result<Fun(int&)>
|
|---|
| 213 | {
|
|---|
| 214 | typedef int& type;
|
|---|
| 215 | };
|
|---|
| 216 |
|
|---|
| 217 | int& operator()(int& i) const
|
|---|
| 218 | {
|
|---|
| 219 | return i;
|
|---|
| 220 | }
|
|---|
| 221 | };
|
|---|
| 222 |
|
|---|
| 223 | void test_defect_summary()
|
|---|
| 224 | {
|
|---|
| 225 | typedef fusion::vector<int, int> from_t;
|
|---|
| 226 | from_t from(10, 20);
|
|---|
| 227 | fusion::transform_view<from_t, ::identity> v(from, ::identity());
|
|---|
| 228 |
|
|---|
| 229 | // transform_view's deref is implemented using result_of in the standard manner.
|
|---|
| 230 | *fusion::begin(v) = 999;
|
|---|
| 231 | BOOST_TEST(boost::fusion::at_c<0>(from) == 999);
|
|---|
| 232 |
|
|---|
| 233 | // transform_view's value_of is implemented using result_of in a Fusion-defined manner.
|
|---|
| 234 | #if 0
|
|---|
| 235 | boost::fusion::as_vector(v); // `identity::result` is unexpectedly passed a rvalue.
|
|---|
| 236 | #endif
|
|---|
| 237 | }
|
|---|
| 238 |
|
|---|
| 239 |
|
|---|
| 240 |
|
|---|
| 241 | // 3-1. Proposed Resolution.
|
|---|
| 242 | //
|
|---|
| 243 |
|
|---|
| 244 | // The probolem is that the current behavior of transform_view's value_of
|
|---|
| 245 | // is unsatisfactory for some FunctionObjects.
|
|---|
| 246 | // So, transform_view should take an optional MetafunctionClass `ValueOf` for value_of implementation.
|
|---|
| 247 |
|
|---|
| 248 |
|
|---|
| 249 | struct use_default; // may be any name.
|
|---|
| 250 |
|
|---|
| 251 | template <typename Seq, typename Fun, typename ValueOf = use_default>
|
|---|
| 252 | struct transform_view;
|
|---|
| 253 |
|
|---|
| 254 |
|
|---|
| 255 |
|
|---|
| 256 | // 3-2. Proposed value_of implementation of transform_view
|
|---|
| 257 | //
|
|---|
| 258 |
|
|---|
| 259 | // Notation:
|
|---|
| 260 | // * Assume `I` is an iterator of `Seq`.
|
|---|
| 261 | // * Assume `J` is a `fusion::transform_view<Seq, Fun, ValueOf>` iterator whose underlying iterator is `I`.
|
|---|
| 262 | // * Assume `V` is a metafunction which represents the current(Boost1.35) implementation behavior.
|
|---|
| 263 | // * Assume `W` is `boost::mpl::apply2<ValueOf, fusion::result_of::deref<I>::type, fusion::result_of::value_of<I>::type>`.
|
|---|
| 264 |
|
|---|
| 265 | // Valid Expression:
|
|---|
| 266 | // * `fusion::result_of::value_of<J>::type`
|
|---|
| 267 |
|
|---|
| 268 | // Valid Expression's semantics:
|
|---|
| 269 | // * `boost::mpl::eval_if<boost::is_same<ValueOf, use_default>, V, W>::type`
|
|---|
| 270 |
|
|---|
| 271 | // Precondition:
|
|---|
| 272 | // * Value Expression's semantics is a valid expression.
|
|---|
| 273 |
|
|---|
| 274 | // Notice:
|
|---|
| 275 | // * `W` uses both `deref` and `value_of`. That's fusion.
|
|---|
| 276 | // * `boost::mpl::eval_if` must be used instead of `boost::mpl::if_`, because `V::type` may be ill-formed.
|
|---|
| 277 |
|
|---|
| 278 |
|
|---|
| 279 |
|
|---|
| 280 | // 3-3. Example of ValueOf template argument
|
|---|
| 281 | //
|
|---|
| 282 |
|
|---|
| 283 | // ValueOf of mybegin will be something like this:
|
|---|
| 284 | struct mybegin_value_of
|
|---|
| 285 | {
|
|---|
| 286 | template <typename Ref, typename Value>
|
|---|
| 287 | struct apply :
|
|---|
| 288 | boost::result_of<T_mybegin(Ref)>
|
|---|
| 289 | {};
|
|---|
| 290 | };
|
|---|
| 291 |
|
|---|
| 292 | // ValueOf of identity will be something like this:
|
|---|
| 293 | struct identity_value_of
|
|---|
| 294 | {
|
|---|
| 295 | template <typename Ref, typename Value>
|
|---|
| 296 | struct apply
|
|---|
| 297 | {
|
|---|
| 298 | typedef Value type;
|
|---|
| 299 | };
|
|---|
| 300 | };
|
|---|
| 301 |
|
|---|
| 302 |
|
|---|
| 303 |
|
|---|
| 304 | //
|
|---|
| 305 | // END.
|
|---|
| 306 |
|
|---|
| 307 |
|
|---|
| 308 | int main()
|
|---|
| 309 | {
|
|---|
| 310 | test_mutability_of_stl_iters();
|
|---|
| 311 | test_mutability_of_stl_iters_cheat();
|
|---|
| 312 | test_defect_summary();
|
|---|
| 313 | return boost::report_errors();
|
|---|
| 314 | }
|
|---|