Ticket #1396: value_of_transform_view_defect.cpp

File value_of_transform_view_defect.cpp, 8.5 KB (added by Shunsuke Sogame <pstade.mb@…>, 15 years ago)

A defect report of this problem

Line 
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
21namespace 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.
42template <typename FusionVectorOfStlIterators>
43struct 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.
59struct 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
95T_mybegin const mybegin = {};
96
97template <typename SequenceOfContainers, typename Begin>
98fusion_zip_iterator<
99 typename fusion::result_of::as_vector<
100 fusion::transform_view<SequenceOfContainers, Begin>
101 >::type
102>
103make_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.
119void 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.
141struct 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
177T_mybegin_cheat const mybegin_cheat = {};
178
179void 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
206struct 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
223void 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
249struct use_default; // may be any name.
250
251template <typename Seq, typename Fun, typename ValueOf = use_default>
252struct 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:
284struct 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:
293struct 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
308int 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}