Assign V2 - first impression

Hi all, I've been using assign v1 for a long time, and I find it sometimes convenient to have at hand but I don't consider it a crucial tool. To have a v2 of the same library is to me..surprising.:) Very well, I compared the introduction of v1 and v2 to get a feeling for the difference V1:
The purpose of this library is to make it easy to fill containers with data by overloading operator,() and operator()(). These two operators make it possible to construct lists of values that are then copied into a container:
A parenthesis-separated list:
map<string,int> m; insert( m )( "Bar", 1 )( "Foo", 2 );
V2:
This second version of Boost.Assign is a new design, with optional support for C++0x. Its core utility is a compact interface for executing the repetitive tasks of assigning or inserting elements in a container, such as follows boost::for_each( cal | do_csv_put<2>( "jan", 31, "feb", 28, "mar", 31 ), std::cout << lambda::bind( &p_::first, lambda::_1 ) << ':' << lambda::bind( &p_::second, lambda:: _1 ) << ' ' ); // prints jan:31 feb:28 mar:31
To my taste, I prefer V1 insert( m )( "Bar", 1 )( "Foo", 2 ); over V2 m | do_csv_put<2>( "Bar", 1, "Foo", 2) Even if I would find V2's syntax to be more tasty to the eye, i don't see that motivating enough for a new library at this point. Very well, continuing to understand the V2 library.. This example captures the basic features of this library: It maps arguments to a suitable data-element, p_( k, x ), and invokes a
container's modifier, insert. These semantics vary by container category.
Operator | returns the modified container, thereby facilitating integration
with range algorithms. There is a function, csv_put, that achieves the same as above, but without returning the container.
Why is operator | used at all, if there are two separate functions? It doesn't look compelling to me and I'm perfectly fine with separating my statements, one for the assign part and one for the for_each part. In fact, the first example from the documentation would benefit from doing that, since it would be much more readable. cal | do_csv_put<2>( "jan", 31, "feb", 28, "mar", 31 ); boost::for_each(cal, long-lambda-expression..) Now it looks even more odd with the operator |. Additional features include:
Options for overriding default semantics, and macros to create custom options
What are the default semantics?
A functor analogue of csv_put, put, for constructing a sequence of elements from variadic argument lists Two functions, csv_deque<> and deque<>, which are the analogues of those just described, but which generate a container.
You lost me here. I needed to read those sentences a few time before I think I understand what you mean. Functionality that is adjunct to or independent of what precedes is itemized
below:
Surely, this can be written smoother, no? =)
Chaining ranges, with special consideration for those created using the ref functionalty (below)
I don't know what the docs are talking about at this point. Chaining ranges in the assign library? That sounds to the uneducated mind like a job for boost.range
Conversion from ranges to containers
This sentence doesn't make sence to me. std::container(range.begin(), range.end()) is supported by all containers.
A framework, whose identifiers are in namespace ref, for generating an array of reference wrappers, for short, a reference-array.
I do not understand this part. What kind of container are we attempting to populate here?
Functionality is defined in namespace boost::assign::v2, thereby avoiding any clash with the prior version [1].
Ok. Maybe V2 could show a compelling use-case that V1 lacks? I get the feeling V2 shows features I don't understand how to use, or didn't knew that I needed. Cheers, - christian

Why is operator | used at all, if there are two separate functions? It doesn't look compelling to me and I'm perfectly fine with separating my statements, one for the assign part and one for the for_each part. In fact, the first example from the documentation would benefit from doing that, since it would be much more readable.
cal | do_csv_put<2>( "jan", 31, "feb", 28, "mar", 31 ); boost::for_each(cal, long-lambda-expression..)
v2 doesn't stop you from separating your statements like this: csv_put( cal, "jan", 31, "feb", 28, "mar", 31 ); boost::for_each(cal, long-lambda-expression..); You may ignore operator| altogether, but since it achieves something comparable (not identical) to boost adaptors, which also use the operator| syntax, it enhances coherence.
Additional features include:
Options for overriding default semantics, and macros to create custom options
What are the default semantics?
Please do a search for semantics in the tutorial: https://svn.boost.org/svn/boost/sandbox/assign_v2/libs/assign/v2/doc/html/bo...
A functor analogue of csv_put, put, for constructing a sequence of elements from variadic argument lists Two functions, csv_deque<> and deque<>, which are the analogues of those just described, but which generate a container.
You lost me here. I needed to read those sentences a few time before I think I understand what you mean.
Indeed, the intro merly skims the surface...
Chaining ranges, with special consideration for those created using the ref functionalty (below)
I don't know what the docs are talking about at this point. Chaining ranges in the assign library? That sounds to the uneducated mind like a job for boost.range
It probably straddles both. As stated above, it adresses a particular need that arises from using csv_array. Please see the tutorial under Reference Array and Chaining.
Conversion from ranges to containers
This sentence doesn't make sence to me. std::container(range.begin(), range.end()) is supported by all containers.
Please see Conversion in the tutorial. The first sentence gives it away: This section deals with conversion from a Range to collections of elements that need not (but may) support iterators.
A framework, whose identifiers are in namespace ref, for generating an array of reference wrappers, for short, a reference-array.
I do not understand this part. What kind of container are we attempting to populate here?
Please see the tutorial under Reference Array.

Additional features include:
Options for overriding default semantics, and macros to create custom options
What are the default semantics?
Please do a search for semantics in the tutorial:
I continued with the tutorial, but it didn't help me much in the understanding how V2 would make my life easier. V2's first example:
typedef std::string word_; const char x[] = "foo"; const char y[4] = { 'b', 'a', 'r', '\0' }; word_ z = "***baz"; std::map<int, word_> map; put( map )( 1, x, 3 )( 2, y )( 3, z, 3, 3 )( 4, "qux" );
If I was given the above code and had to figure out the contents of map, I would fail to do so. What's the benefit of the library to construct the container's value_type, instead of explicitly by the user? This reads easier to me map[1] = std::string(x, 3); map[2] = y; map[3] = std::string(z, 3, 3); map[4] = "qux"; I would not sacrifice clarity for compactness. The following next example is also IMO convoluted, I've read it multiple times but I don't understand what it attempts to do. std::vector<int> numeric( 10 ); iota( numeric, 0 );
typedef std::string str_; typedef variant< int, str_ > data_; array<data_, 17> keypad; csv( put( keypad ), "+", "-", "*", "/", "=", ".", "c" ).for_each( numeric );
Can you provide both the standard way of populating the keypad structure, so it can be more easily compared to the way V2 suggests it should be done. Reading the assert macros, I guess this is equivalent? array<variant<int, std::string>, 17> keypad = { "+", "-", "*", "/", "=", ".", "c", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - Christian

I continued with the tutorial, but it didn't help me much in the understanding how V2 would make my life easier.
V2's first example:
typedef std::string word_; const char x[] = "foo"; const char y[4] = { 'b', 'a', 'r', '\0' }; word_ z = "***baz"; std::map<int, word_> map; put( map )( 1, x, 3 )( 2, y )( 3, z, 3, 3 )( 4, "qux" );
If I was given the above code and had to figure out the contents of map, I would fail to do so.
It's impossible to have a syntax that is 100% self-explanatory. The tutorial states: For a map, as above, the first argument is treated as a key, and the rest is forwarded to the mapped-type's constructor, word_( x, 3 ), in the first call. Key and data are then combined into a pair, which is inserted using modifier insert.
What's the benefit of the library to construct the container's value_type, instead of explicitly by the user?
Yet this is what you do (construct the container's value_type) in your previous example: "To my taste, I prefer V1 insert( m )( "Bar", 1 )( "Foo", 2 );" While you only state a preference over V2, it's hard to see that you are unhappy with it. "Bar" and 1 are combined into a pair, which is the value_type of the map containers. My example just uses varying number of arguments to create the mapped value.

V2's first example:
typedef std::string word_; const char x[] = "foo"; const char y[4] = { 'b', 'a', 'r', '\0' }; word_ z = "***baz"; std::map<int, word_> map; put( map )( 1, x, 3 )( 2, y )( 3, z, 3, 3 )( 4, "qux" );
If I was given the above code and had to figure out the contents of map, I would fail to do so.
It's impossible to have a syntax that is 100% self-explanatory. The tutorial states:
For a map, as above, the first argument is treated as a key, and the rest is forwarded to the mapped-type's constructor, word_( x, 3 ), in the first call. Key and data are then combined into a pair, which is inserted using modifier insert.
It is not at all impossible to have self-explanatory code that doesn't not
do anything more complicated than populating a container.
What's the benefit of the library to construct the container's value_type,
instead of explicitly by the user?
Yet this is what you do (construct the container's value_type) in your previous example:
You didn't answer the question, why would I need a library that does this for me? It obfuscates the code for no clear advantage. You argue that std::container.insert() isn't enough, and that we need a global insert(Container& c, ...args) (although you call it put, which is also unclear to me) I disagree to put such things into a library, if user needs that it can be easily expressed in c+0x.
"To my taste, I prefer V1 insert( m )( "Bar", 1 )( "Foo", 2 );"
While you only state a preference over V2, it's hard to see that you are unhappy with it.
I think your library needs to do better than coming up with a slightly different syntax than V1, otherwise I don't see the point of having it. To be clear, I'm not happy with V2 and I think V1 is much clearer.
"Bar" and 1 are combined into a pair, which is the value_type of the map containers. My example just uses varying number of arguments to create the mapped value.
You never commented on the keypad example. Is my code correct? If so, don't you find it a bit easier to read than yours? - Christian

While you only state a preference over V2, it's hard to see that you are unhappy with it.
I think your library needs to do better than coming up with a slightly different syntax than V1, otherwise I don't see the point of having it.
Indeed, but I claim more than a slightly different syntax. The fixed-arity stuff is quite a different syntax. The other stated goals of the design, in the change-log, are, amongst other things, to bring these improvements: 1- Code reuse because deque and put share the same crtp 2- Code decoupling (modularity) 3- Open for extension (albeit not enough, see my response to) Do I have to argue that these objectives worthwhile?! No, so I please ask whether they are were met, and if not, what is lacking. PS: maybe I did not state 3-. Let's assume I get paid for writing a TPS report, every day, involving 100 unit-tests such as the one below. STL approach: std::deque<int> cont; const int n = 2; int x = log10( 1 ); for(int i = 0; i < n; i++) { cont.push_front( x ); } x = log10( 10 ); for(int i = 0; i < n; i++) { cont.push_front( x ); } x = log10( 100 ); for(int i = 0; i < n; i++) { cont.push_front( x ); } x = log10( 1000 ); for(int i = 0; i < n; i++) { cont.push_front( x ); } assert( boost::range::equal( cont, csv_deque( 3, 3, 2, 2, 1, 1, 0, 0 ) ) ); PS: No need to argue that a nested loop would made the job easier. Your boss can make it as arcane as he likes and he might have as well re-arranged 1, 10, 100, 1000 in a different order. Assign 2.0: BOOST_AUTO( _r, ( _repeat = 2 ) ); typedef function<int(int)> f_; BOOST_AUTO( _d, ( _data = f_( log10 ) ) ); assert( boost::range::equal( csv( deque<int, push_front_>( _nil) % _d % _r, 1, 10, 100, 1000 ), csv_deque( 3, 3, 2, 2, 1, 1, 0, 0 ) ) ); Does Assign (1.0-2.0) begins to add positively to your well being? Assume, further, that you have to do the same thing all over again, but you have to drop the function altogether, and replace push_front by push_back. Is it better to write a function or use Assign? As for 2.0, specifically, do we agree about *decoupling* of features (_r,_d and push_front_)? The same code above requires very minor modification if used with put() rather than deque(), are we on the same page about *code reuse*? Finally, let's say you want something other than repeat, that does not already exits. You can write write a small class, that conforms to a concept (ConceptModifier), and invoke two mere macros, and the job is done. Do you agree about *open for extension*? https://svn.boost.org/svn/boost/sandbox/assign_v2/boost/assign/v2/option/mod... Please have a bit mercy and give me your updated impressions based on these clarifications.

On 23 June 2011 12:06, er <er.ci.2020@gmail.com> wrote:
While you only state a preference over V2, it's hard to see that you are
unhappy with it.
I think your library needs to do better than coming up with a slightly different syntax than V1, otherwise I don't see the point of having it.
Indeed, but I claim more than a slightly different syntax. The fixed-arity stuff is quite a different syntax. The other stated goals of the design, in the change-log, are, amongst other things, to bring these improvements:
1- Code reuse because deque and put share the same crtp
Are we talking about code reuse within the library? A library isn't useful on its merits to not have unnecessary code duplication in its implementation. Or did I misunderstand this point?
2- Code decoupling (modularity)
How does this benefit me as a user of the library?
3- Open for extension (albeit not enough, see my response to)
Dito.
Do I have to argue that these objectives worthwhile?! No, so I please ask
whether they are were met, and if not, what is lacking. PS: maybe I did not state 3-.
Yes, please argue explicitly how these improvements over V1 benefits the user of the library.
Let's assume I get paid for writing a TPS report, every day, involving 100 unit-tests such as the one below.
STL approach:
std::deque<int> cont; const int n = 2; int x = log10( 1 ); for(int i = 0; i < n; i++) { cont.push_front( x ); } x = log10( 10 ); for(int i = 0; i < n; i++) { cont.push_front( x ); } x = log10( 100 ); for(int i = 0; i < n; i++) { cont.push_front( x ); } x = log10( 1000 ); for(int i = 0; i < n; i++) { cont.push_front( x ); }
assert( boost::range::equal( cont, csv_deque( 3, 3, 2, 2, 1, 1, 0, 0 ) ) );
PS: No need to argue that a nested loop would made the job easier. Your boss can make it as arcane as he likes and he might have as well re-arranged 1, 10, 100, 1000 in a different order.
array<int, 4> log_constants = {1, 10, 100, 1000}; vector<int> cont; int n = 2; foreach(i, log_constants) int x = log10(i); for(int j = 0; j < n;++j) { cont.push_back(x); } I doesn't matter, really, how the values are arranged or what kind of boss you have. I don't know if I got the above code correctly, but it doesn't matter. The point is that it's readable.
Assign 2.0:
BOOST_AUTO( _r, ( _repeat = 2 ) ); typedef function<int(int)> f_; BOOST_AUTO( _d, ( _data = f_( log10 ) ) );
assert( boost::range::equal( csv( deque<int, push_front_>( _nil) % _d % _r, 1, 10, 100, 1000 ), csv_deque( 3, 3, 2, 2, 1, 1, 0, 0 ) ) );
Does Assign (1.0-2.0) begins to add positively to your well being?
I'm sorry, not to me. The STL code I can understand, and I could compact that myself quite a bit with some useful snippets if my daily work involved this kind of code repeatedly. If you and I were at the same company, I could take over the STL example for future maintenance, but the assign V2 code I personally cannot parse.
Assume, further, that you have to do the same thing all over again, but you have to drop the function altogether, and replace push_front by push_back. Is it better to write a function or use Assign?
You lost me here. Guessing wildly, I would probably respond "write a function" and you would say "use Assign".
As for 2.0, specifically, do we agree about *decoupling* of features (_r,_d and push_front_)?
I don't know. What is _r, _d and push_front_?
The same code above requires very minor modification if used with put() rather than deque(), are we on the same page about *code reuse*?
?
Finally, let's say you want something other than repeat, that does not already exits. You can write write a small class, that conforms to a concept (ConceptModifier), and invoke two mere macros, and the job is done. Do you agree about *open for extension*?
https://svn.boost.org/svn/**boost/sandbox/assign_v2/boost/** assign/v2/option/modifier/**repeat.hpp<https://svn.boost.org/svn/boost/sandbox/assign_v2/boost/assign/v2/option/modifier/repeat.hpp>
Please have a bit mercy and give me your updated impressions based on these clarifications.
Let's not get overly dramatic, shall we =) I get the impression that Assign V2 mixes too many features under one hood, and on top of that uses an unorthodox ways of expressing itself. I feel that assignment and algorithms should not be interleaved in this way. Skimming trough the docs and examples leaves me very puzzled. I've now spent a few hours on this library, but I've not been able yet to figure out the first code line from the docs cal | do_csv_put<2>( "jan", 31, "feb", 28, "mar", 31 ), The difference between do_csv, put, _put, csv_put in all forms are subtle to me. I think they all return a proxy object containing a conversion operator and a magic for_each member function, but I could be wrong. I would still like to a see a clear, concise and easy to read example that shows: * The example can't be expressed neatly with V1. * The example can't be expressed neatly with standard tools, such as Boost.Range + Boost.Lamba/Phoenix, The example would need to guide me trough this process, and it needs to make a -best effort- in using standard tools, and then consistently showing how they fail. - C

Does Assign (1.0-2.0) begins to add positively to your well being? Assume, further, that you have to do the same thing all over again, but you have to drop the function altogether, and replace push_front by push_back. Is it better to write a function or use Assign?
You lost me here. Guessing wildly, I would probably respond "write a function" and you would say "use Assign".
Then there is no conflict between your view and mine, because Assign provides you the function to do just that.
As for 2.0, specifically, do we agree about *decoupling* of features (_r,_d and push_front_)?
I don't know. What is _r, _d and push_front_?
I usually leave the benefit of doubt when I don't know. As for _r, _d and push_front_, they were in the snippet I gave. Do you mean that you don't understand their purpose?
The same code above requires very minor modification if used with put() rather than deque(), are we on the same page about *code reuse*?
?
csv( put( cont ) % _push_front % _d % _r, 1, 10, 100, 1000 ); The syntax is very similar to that of the previous example, which used deque. Each of put and deque rely on the same class template. Hence the claim of code reuse.
Finally, let's say you want something other than repeat, that does not already exits. You can write write a small class, that conforms to a concept (ConceptModifier), and invoke two mere macros, and the job is done. Do you agree about *open for extension*?
Absent a rebuttal, I take it you agree it is open for extension. Let me ask you: why would that not be an improvement (that benefits the user)?
I get the impression that Assign V2 mixes too many features under one hood,
Two, under one hood: 1- Argument forwarding (to a produce a data-element) 2- A modifier, to put the data-element in the container Each is specified with a template parameter. This is behind two out of the 3 claimed 'improvements'. The third one, code reuse, is that it is the same class template behind put() and deque().
and on top of that uses an unorthodox ways of expressing itself. I feel that
Fair. Suggestions have been made to improve on that, and I see that one person endorsed them.
assignment and algorithms should not be interleaved in this way.
Do you mean operator| appearing inside an algorithm, by interleaved? I already answered this.
Skimming trough the docs and examples leaves me very puzzled. I've now spent a few hours on this library, but I've not been able yet to figure out the first code line from the docs
cal | do_csv_put<2>( "jan", 31, "feb", 28, "mar", 31 ),
The difference between do_csv, put, _put, csv_put in all forms are subtle to me.
If we can leave the advanced features aside for a moment we have something that is manageable: put( cont )(args1...)...(argsn...); csv_put<I>( cont, a1,...aI,...,z1,...,zI); My familiarity has blinded me. I now know to make the distinction clear between the basic features and the advanced ones, and motivate each better.
I think they all return a proxy object containing a conversion operator and a magic for_each member function, but I could be wrong.
English sentence: Put each element of range in cont. Assign v2 sentence: put( cont ).for_each( range ); I'll try to think more about your concerns. Thanks for helping to look at this library with a new pair of eyes.

Let's assume I get paid for writing a TPS report, every day, involving 100 unit-tests such as the one below.
array<int, 4> log_constants = {1, 10, 100, 1000}; vector<int> cont; int n = 2; foreach(i, log_constants) int x = log10(i); for(int j = 0; j< n;++j) { cont.push_back(x); }
I doesn't matter, really, how the values are arranged or what kind of boss you have. I don't know if I got the above code correctly, but it doesn't matter. The point is that it's readable.
Indeed, it's readable. But I talked about 100 unit tests that vary a bit. Let's settle for 5. The STL way: array<int, 1> log_constants = { a1 }; vector<int> cont; int n = n1; foreach(i, log_constants) int x = f1(i); for(int j = 0; j < n;++j) { cont.push_front(x); } } array<int, 2> log_constants = { a2, b2 }; vector<int> cont; int n = n2; foreach(i, log_constants) int x = f2(i); for(int j = 0; j < n;++j) { cont.push_front(x); } } array<int, 3> log_constants = { a3, b3, c3 }; vector<int> cont; int n = n3; foreach(i, log_constants) int x = f3(i); for(int j = 0; j < n;++j) { cont.push_front(x); } } array<int, 4> log_constants = { a4, b4, c4, d4 }; vector<int> cont; int n = n4; foreach(i, log_constants) int x = f4(i); for(int j = 0; j < n;++j) { cont.push_front(x); } } array<int, 5> log_constants = { a5, b5, c5, d5, e5 }; vector<int> cont; int n = n5; foreach(i, log_constants) int x = f5(i); for(int j = 0; j < n;++j) { cont.push_front(x); } } Assign v2 way: csv( // 1 deque<int, push_front_>( _nil ) % ( _data = f1 ) % ( _repeat = n1 ), a1 ); csv( // 2 deque<int, push_front_>( _nil ) % ( _data = f2 ) % ( _repeat = n2 ), a2, b2 ); csv( // 3 deque<int, push_front_>( _nil ) % ( _data = f3 ) % ( _repeat = n3 ), a3, b3, c3 ); csv( // 4 deque<int, push_front_>( _nil ) % ( _data = f4 ) % ( _repeat = n4 ), a4, b4, c4, d4 ); csv( // 5 deque<int, push_front_>( _nil ) % ( _data = f5 ) % ( _repeat = n5 ), a5, b5, c5, d5, e5 ); Isn't this more clear, and less error prone? It is not that much, and not "crucial", but the claim of the library is just as modest: "Its core utility is a compact interface for executing the repetitive tasks of assigning or inserting elements in a container". As for v1 vs v2, I have already stated 3 improvements that you have not refuted. What's left to prove, in your eyes, is that the changes benefits the user. For instance, with list_of, you have to contend with push_back, if I recall correctly. Second, you can invoke fun() in place of % ( _data = f ) repeat() in place of and repeat() in place of % ( _repeat = n ) and fun_repeat() in place of % ( _data = f ) % ( _repeat = n ); That's about it, whereas you are not limited in the number of options in v2. See here, however: https://svn.boost.org/svn/boost/sandbox/assign_v2/libs/assign/v2/doc/html/bo... Furthermore, it's open for extension i.e. you can write your own _option, should the available options be insufficient to meet your needs.
could compact that myself quite a bit with some useful snippets if my daily work involved this kind of code repeatedly.
Not with STL, if the structure of your program changes often : you would have to rewrite a new snippet often as well.
I would still like to a see a clear, concise and easy to read example that shows: * The example can't be expressed neatly with V1.
I think I answered that.
* The example can't be expressed neatly with standard tools, such as Boost.Range + Boost.Lamba/Phoenix,
Why don't you show me how to do it with Range + Lambda, and if necessary I'll move to more complicated examples?

Indeed, it's readable. But I talked about 100 unit tests that vary a bit. Let's settle for 5. The STL way:
array<int, 1> log_constants = { a1 }; vector<int> cont; int n = n1; foreach(i, log_constants) int x = f1(i);
for(int j = 0; j < n;++j) { cont.push_front(x); } } array<int, 2> log_constants = { a2, b2 }; vector<int> cont; int n = n2; foreach(i, log_constants) int x = f2(i);
for(int j = 0; j < n;++j) { cont.push_front(x); } } array<int, 3> log_constants = { a3, b3, c3 }; vector<int> cont; int n = n3; foreach(i, log_constants) int x = f3(i);
for(int j = 0; j < n;++j) { cont.push_front(x); } } array<int, 4> log_constants = { a4, b4, c4, d4 }; vector<int> cont; int n = n4; foreach(i, log_constants) int x = f4(i);
for(int j = 0; j < n;++j) { cont.push_front(x); } } array<int, 5> log_constants = { a5, b5, c5, d5, e5 }; vector<int> cont; int n = n5; foreach(i, log_constants) int x = f5(i);
for(int j = 0; j < n;++j) { cont.push_front(x); } }
Assign v2 way:
csv( // 1 deque<int, push_front_>( _nil ) % ( _data = f1 ) % ( _repeat = n1 ), a1 ); csv( // 2 deque<int, push_front_>( _nil ) % ( _data = f2 ) % ( _repeat = n2 ), a2, b2 ); csv( // 3 deque<int, push_front_>( _nil ) % ( _data = f3 ) % ( _repeat = n3 ), a3, b3, c3 ); csv( // 4 deque<int, push_front_>( _nil ) % ( _data = f4 ) % ( _repeat = n4 ), a4, b4, c4, d4 ); csv( // 5 deque<int, push_front_>( _nil ) % ( _data = f5 ) % ( _repeat = n5 ), a5, b5, c5, d5, e5 );
Isn't this more clear, and less error prone? It is not that much, and not "crucial", but the claim of the library is just as modest:
Quick answer, I'll try to me give more feedback tomorrow: I don't work a lot with copy'n'paste. There's always a structure to be find somewhere, and this structure can be identified and formalized into a shorter program. Anyway, my main problem is that I cannot understand your code. If you were using standard facilities, like std::generate/transform/copy, I wouldn't need to start from scratch. I would need to study your documentation quite a lot to figure out what values your code generates. Thinking out loud, this would be easier to me (made up example): vector<int> v; std::generate_n(std::back_inserter(v), 10, assign::v2::fancy_generator(......)); or std::copy_n(assign::v2::fancy_iterator(...), 10, std::back_inseter(v)); It seems like your library can build complex generators/converters of some kind, but it's integration with the containers appears ad-hoc. The above would allow me to only focus on how to configure the generators, and use my old-time knowledge on how to use these with std::containers. - Christian

Thinking out loud, this would be easier to me (made up example):
Before we switch topics, I'd like to summarize the last one, while not closing the door to its extension, given that you promised me "more feedback". Your initial concern, that I recognize as legitimate, can be summarized as: Do the established design improvements ( code reuse, orthogonal features, open for ext ) that I claim provide benefits the user, within the quite narrow mandate of this library? To this end I compared a hypothetical sequence of unit tests using STL, to another using V2. Pending your feedback, I have not seen a refutation, only that "I don't know work a lot with copy 'n' paste", which does not rule out that sometimes you come across something similar, or that someone else might have to do such repetitive work. Mind you, V2 does not purport to be a swiss army knife. Let me rephrase the narrow mandate: "Its core utility is a compact interface for executing the repetitive tasks of assigning or inserting elements in a container" Even as I'm the one under scrutiny, today, I'm going to say that having to study the documentation is not a receivable criticism. Having said this, there is quite some leeway to rethink the interface to be more friendly. At this stage, I would rather hear suggestions about "how", and thank Paul Bristow for doing so. You do acknowledge that the "library can build complex generators/converters of some kind", which has some good in it, I suppose, but go on to criticize that "it's integration with the containers appears ad-hoc.". First, please clarify "integration" since it has come up a few times. Second, your reasoning rests on hypotheticals (would): "The above would allow me to only focus on how to configure the generators, and use my old-time knowledge on how to use these with std::containers.". The proposed library *is* in existence...

On 24 June 2011 03:45, er <er.ci.2020@gmail.com> wrote:
Thinking out loud, this would be easier to me (made up example):
Before we switch topics, I'd like to summarize the last one, while not closing the door to its extension, given that you promised me "more feedback". Your initial concern, that I recognize as legitimate, can be summarized as:
Do the established design improvements ( code reuse, orthogonal features, open for ext ) that I claim provide benefits the user, within the quite narrow mandate of this library?
You return to these claims all the time.Code resuse and extensions can both be part of a healthy design, but a library cannot stand on that ground alone.
To this end I compared a hypothetical sequence of unit tests using STL, to another using V2. Pending your feedback, I have not seen a refutation, only that "I don't know work a lot with copy 'n' paste", which does not rule out that sometimes you come across something similar, or that someone else might have to do such repetitive work. Mind you, V2 does not purport to be a swiss army knife. Let me rephrase the narrow mandate:
Then I think you have not read my response, or I fail to communicate with what I've said from beginning. -> I cannot understand the code you are producing with the help of your library <-. Even if I read the documentation it's still a mystery.
"Its core utility is a compact interface for executing the repetitive tasks of assigning or inserting elements in a container"
There is already a library that does this, Boost.Assign. You are proposing a library that, I think, does assign + transform + generate as one expression.
Even as I'm the one under scrutiny, today, I'm going to say that having to study the documentation is not a receivable criticism. Having said this, there is quite some leeway to rethink the interface to be more friendly. At this stage, I would rather hear suggestions about "how", and thank Paul Bristow for doing so.
I suggested how: Allow the user to use standard stl algorithms together with your library. It's a no win with this: std::vector<int> v = magic_v2_expression; over std::vector<int> v; std::generate_n(back_inserter(v), 10, generator_v2());
You do acknowledge that the "library can build complex generators/converters of some kind", which has some good in it, I suppose, but go on to criticize that "it's integration with the containers appears ad-hoc.". First, please clarify "integration" since it has come up a few times.
With integration I mean the library should play nicely with standard stl concepts and algorithms.
Second, your reasoning rests on hypotheticals (would): "The above would allow me to only focus on how to configure the generators, and use my old-time knowledge on how to use these with std::containers.". The proposed library *is* in existence...
I take that as you are not interested in criticism then.
- Christian

On 6/24/11 12:04 PM, Christian Holmquist wrote:
On 24 June 2011 03:45, er<er.ci.2020@gmail.com> wrote:
Thinking out loud, this would be easier to me (made up example):
Before we switch topics, I'd like to summarize the last one, while not closing the door to its extension, given that you promised me "more feedback". Your initial concern, that I recognize as legitimate, can be summarized as:
Do the established design improvements ( code reuse, orthogonal features, open for ext ) that I claim provide benefits the user, within the quite narrow mandate of this library?
You return to these claims all the time.Code resuse and extensions can both be part of a healthy design, but a library cannot stand on that ground alone.
About "return to these claims all the time". You have to understand that I don't really have other claims to make. The Formal Review Process invites, amongst others, these 2 recommendations : design & usefulness. I don't think I'm off topic.
To this end I compared a hypothetical sequence of unit tests using STL, to another using V2. Pending your feedback, I have not seen a refutation, only that "I don't know work a lot with copy 'n' paste", which does not rule out that sometimes you come across something similar, or that someone else might have to do such repetitive work. Mind you, V2 does not purport to be a swiss army knife. Let me rephrase the narrow mandate:
"Its core utility is a compact interface for executing the repetitive tasks of assigning or inserting elements in a container"
There is already a library that does this, Boost.Assign. You are proposing a library that, I think, does assign + transform + generate as one expression.
What do you mean by transform? Are you referring to ( _data = f )? In this case, you are mistaken : Boost.Assign's list_of and list-inserters have a mf fun to that effect. As for 'generate', deque() in v2 and list_of() in v1 are container generators. Or did you mean ( _data = f ) as generator? Based on this, v2 is no the upheaval you claim. Mind you, I studied v1 very closely to come up with v2.
Even as I'm the one under scrutiny, today, I'm going to say that having to study the documentation is not a receivable criticism. Having said this, there is quite some leeway to rethink the interface to be more friendly. At this stage, I would rather hear suggestions about "how", and thank Paul Bristow for doing so.
I suggested how: Allow the user to use standard stl algorithms together with your library. It's a no win with this: std::vector<int> v = magic_v2_expression; over std::vector<int> v; std::generate_n(back_inserter(v), 10, generator_v2());
If I understand correctly, you preference is for the second statement, right? V1 and V2 deal with putting elements, either a) initialization or b) filling. I don't see elements, says x, y, and z, so it's hard for me to bounce back. Please clarify.
You do acknowledge that the "library can build complex generators/converters of some kind", which has some good in it, I suppose, but go on to criticize that "it's integration with the containers appears ad-hoc.". First, please clarify "integration" since it has come up a few times.
With integration I mean the library should play nicely with standard stl concepts and algorithms.
I thought I started to do that by providing in place modification of a container with operator|, but you told me that you prefer separate statements : first fill a container, then pass it through an algorithm.
Second, your reasoning rests on hypotheticals (would): "The above would allow me to only focus on how to configure the generators, and use my old-time knowledge on how to use these with std::containers.". The proposed library *is* in existence...
I take that as you are not interested in criticism then.
You misunderstood my point. I think I tried hard to respond to your criticism. First, there are gaps to fill in your suggested alternative, above, but please feel free to do so. Second, I don't think it's unreasonable to expect that the proposal be reviewed against things that already exist, such as V1. I have yet to see a Boost.Range + Lambda way to do the 5 unit tests that would challenge V2. Having said this, I really thank you for initiating a challenge to give me a chance to prove that the design carries benefits for the user and appreciate that you are willing to try to move past first impressions. Whether or not you agree the code is more manageable, clear and safer, is a call that you can make without understanding the syntax. I should hope that if the answer is yes, it will trigger your interest for learning the syntax, which hopefully can be be improved.
- Christian _______________________________________________ Unsubscribe& other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 23 Jun 2011, at 18:06, er wrote:
BOOST_AUTO( _r, ( _repeat = 2 ) ); typedef function<int(int)> f_; BOOST_AUTO( _d, ( _data = f_( log10 ) ) );
assert( boost::range::equal( csv( deque<int, push_front_>( _nil) % _d % _r, 1, 10, 100, 1000 ), csv_deque( 3, 3, 2, 2, 1, 1, 0, 0 ) ) );
I am afraid to say I think you have been using your own library for too long. To me, that may as well be perl for all the sense it makes. I am sure that it is a powerful library, but I really have no idea at all what is going on. I can't even parse the first line as valid C++ in any way. You need better examples, and also is that really the best way you could find to express repeating?

I am sure that it is a powerful library, but I really have no idea at all what is going on. I can't even parse the first line as valid C++ in any way. You need better examples, and also is that really the best way you could find to express repeating?
If I try to imagine what the interface would look like after taking into account some of the suggestions that were made, this sentence: "Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front." translates to ( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 ); Questions : - is it more readable, now? - What if each example was preceded by its English equivalent. Is this a bit of stretch or a reasonable approach? Thanks.

er wrote:
I am sure that it is a powerful library, but I really have no idea at all what is going on. I can't even parse the first line as valid C++ in any way. You need better examples, and also is that really the best way you could find to express repeating?
If I try to imagine what the interface would look like after taking into account some of the suggestions that were made, this sentence:
"Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front."
If I interpret that correctly, it would be clearer as: "Create elements in cont, using push_front(), by calling f() 1, 10, 100, and 1000 times."
translates to
( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
Why the leading underscores on "_data" and "_repeat?"
Questions : - is it more readable, now? - What if each example was preceded by its English equivalent.
Preceding each by a clear English equivalent is necessary. Could that code be spelled like the following instead? push_front<1>(cont).from(f).repeated(1, 10, 100, 1000); That is, instead of operator overloading, member functions would make things clearer and more succinct. Is there more to the operator overloading than I know making the member function approach too restrictive? What happens to your operator overloading approach when a using directive is not in force? Does "_data" become "boost::assign::_data," for example? _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 6/24/11 9:08 AM, Stewart, Robert wrote:
er wrote:
I am sure that it is a powerful library, but I really have no idea at all what is going on. I can't even parse the first line as valid C++ in any way. You need better examples, and also is that really the best way you could find to express repeating?
If I try to imagine what the interface would look like after taking into account some of the suggestions that were made, this sentence:
"Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front."
If I interpret that correctly, it would be clearer as:
"Create elements in cont, using push_front(), by calling f() 1, 10, 100, and 1000 times."
Noted, thanks.
translates to
( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
Why the leading underscores on "_data" and "_repeat?"
It's part of a set of conventions that are pervasive throughout the library: trailing underscore for types, leading one for const objects: const name_ _name = {}; Names without underscores are reserved for functions.
Questions : - is it more readable, now? - What if each example was preceded by its English equivalent.
Preceding each by a clear English equivalent is necessary.
Noted.
Could that code be spelled like the following instead?
push_front<1>(cont).from(f).repeated(1, 10, 100, 1000);
That is, instead of operator overloading, member functions would make things clearer and more succinct. Is there more to the operator
overloading than I know making the member function approach too restrictive? What happens to your operator overloading approach when a To use a proverbial expression, the member function approach is "carved in stone", if you like. Not using operator%. I'm going to assume that 'object' is returned by put( cont ) or deque<T>( _nil ). It doesn't matter which, since they rely on the same implementation. Consider ( object % option1 % ... % optionn )(1, 10, 100, 1000); object has two (orthogonal) components : the data-generator, which maps arguments to a data-element, and the modifier which takes care of inserting that data-element. Each option modifies either of these two components. For example ( object % ( _data = f ) )( 1, 10, 100, 1000 ); changes the data-generator to f. For example, ( object % ( _repeat = n ) )( 1, 10, 100, 1000 ); wraps a new modifier around the one that already resides in object (say push_back), such as to invoke it n times. While this is perhaps not as intuitive as desired, it's in my view an irreducible price to pay to have modularity and open for extension : one can customize an option. I anticipated that this would be a problem and already provided some remedy (I won't delve on it), but did not push the reasoning far enough. The next step, I think, is to try to get back to functions, as you suggest, but free functions, not member functions. I already proposed a new syntax. Let me repeat it: push_front<1>( cont )( 1, 10, 100, 1000 ); would be equivalent to the less eye candy ( put<1>( cont ) % _push_front )( 1, 10, 100, 1000 ); Consequently, using the first form, the use of operator% would appear, only for additional options: ( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 ); One way to see that being able to encapsulate an option as an object is a good thing, however, is to imagine to have to write two unit tests: void unit_test1() { std::deque<T> v; v.push_back( T( args1... ) ); ...; v.push_back( T( argsn...) ) // whatever } void unit_test2() { std::deque<int> v; v.push_front( T( args1... ) ); ...; v.push_front( T( argsn...) ) // whatever } With V2, you can do the same like this: template<typename Option> void unit_test(Option option) { std::deque<int> vec; ( put( vec ) % option )( args1... )...( argsn... ); // whatever } unit_test( _push_back ); unit_test( _push_front ); Some people might find this contrived. Fair enough, but it's meant to make a point about modularity, which should have some benefit in less contrived situations. HTH, and thanks for your interest.

I anticipated that this would be a problem and already provided some remedy (I won't delve on it), but did not push the reasoning far enough. The next step, I think, is to try to get back to functions, as you suggest, but free functions, not member functions. I already proposed a
Consequently, using the first form, the use of operator% would appear, only for additional options:
( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
I still hadn't pushed the reasoning far enough but another participant completed it : push_front<1>( cont, _data = f, _repeat = n )( 1, 10, 100, 1000 ); Although it appears in another message, I wanted to make sure it wasn't missed.

On 6/24/11 9:08 AM, Stewart, Robert wrote:
er wrote:
I am sure that it is a powerful library, but I really have no idea at all what is going on. I can't even parse the first line as valid C++ in any way. You need better examples, and also is that really the best way you could find to express repeating?
If I try to imagine what the interface would look like after taking into account some of the suggestions that were made, this sentence:
"Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front."
If I interpret that correctly, it would be clearer as:
"Create elements in cont, using push_front(), by calling f() 1, 10, 100, and 1000 times."
Apologies, I misread this. It should be : Map 1, 10, 100, and 1000 by function f, which yields f( 1 ), f( 10 ), f( 100 ), f( 1000 ) and insert each, n times, using push_front, in cont. T x; x = f( 1 ); for(int i = 0; i < n; i++){ cont.push_front( x ); } x = f( 10 ); for(int i = 0; i < n; i++){ cont.push_front( x ); } etc. The meaning of what is said next, I think, is not altered.
translates to
( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
Why the leading underscores on "_data" and "_repeat?"
Questions : - is it more readable, now? - What if each example was preceded by its English equivalent.
Preceding each by a clear English equivalent is necessary.
Could that code be spelled like the following instead?
push_front<1>(cont).from(f).repeated(1, 10, 100, 1000);
That is, instead of operator overloading, member functions would make things clearer and more succinct. Is there more to the operator overloading than I know making the member function approach too restrictive? What happens to your operator overloading approach when a using directive is not in force? Does "_data" become "boost::assign::_data," for example?
_____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools& Components Susquehanna International Group, LLP http://www.sig.com
IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of vi ruses. _______________________________________________ Unsubscribe& other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

er wrote:
On 6/24/11 9:08 AM, Stewart, Robert wrote:
er wrote:
"Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front."
If I interpret that correctly, it would be clearer as:
"Create elements in cont, using push_front(), by calling f() 1, 10, 100, and 1000 times."
Apologies, I misread this. It should be :
Map 1, 10, 100, and 1000 by function f, which yields f( 1 ), f( 10 ), f( 100 ), f( 1000 ) and insert each, n times, using push_front, in cont.
I see now. The "map" terminology obscures things somewhat. I prefer to see mention of "insert," "cont," and "push_back" near the beginning as the purpose of the operation is to insert elements into the container using push_back(). IOW, something more like the following would suit me better: Insert elements in cont, using push_front(), where the values are generated by calling f() n times with 1, 10, 100, and 1000, in turn. It might even be useful to expand that with the following: That is, call cont.push_front(f(1)) n times, then cont.push_front(f(10)) n times, and so on. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 6/24/11 2:17 PM, Stewart, Robert wrote:
er wrote:
On 6/24/11 9:08 AM, Stewart, Robert wrote:
er wrote:
"Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front."
If I interpret that correctly, it would be clearer as:
"Create elements in cont, using push_front(), by calling f() 1, 10, 100, and 1000 times."
Apologies, I misread this. It should be :
Map 1, 10, 100, and 1000 by function f, which yields f( 1 ), f( 10 ), f( 100 ), f( 1000 ) and insert each, n times, using push_front, in cont.
I see now. The "map" terminology obscures things somewhat. I prefer to see mention of "insert," "cont," and "push_back" near the beginning as the purpose of the operation is to insert elements into the container using push_back().
Yes, thanks, my use of the word map is because in the tutorial in expression ( _data = f ), f is mathematical function. 'Calling' f is indeed context free.
IOW, something more like the following would suit me better:
Insert elements in cont, using push_front(), where the values are generated by calling f() n times with 1, 10, 100, and 1000, in turn.
Thanks for trying to get to the bottom of it. I agree that the wording in English might call for Insert in the beginning. But the actual sequence of events is like this: Each of x = 1, ..., 1000 is forwarded to a data-generator, f. The result of f( x ), which I call y, is forwarded to a modifier which, here, executes: repeat n times { cont.push_front( y ); } The sequence matters, because f( x ) is computed only once, which is the most efficient way. I use the words data-generator & modifier to be consistent with the naming conventions of the library. To be clear, the data-generator's job is to map arguments to a suitable data-element. The modifier's job is to insert that element (n times, in this case).

I use the words data-generator & modifier to be consistent with the naming conventions of the library. To be clear, the data-generator's job is to map arguments to a suitable data-element. The modifier's job is to
Ouch, the data-generator's job is to construct (instead of 'map') a data-element from the arguments.

er wrote:
On 6/24/11 2:17 PM, Stewart, Robert wrote:
er wrote:
Map 1, 10, 100, and 1000 by function f, which yields f( 1 ), f( 10 ), f( 100 ), f( 1000 ) and insert each, n times, using push_front, in cont. [snip] IOW, something more like the following would suit me better:
Insert elements in cont, using push_front(), where the values are generated by calling f() n times with 1, 10, 100, and 1000, in turn.
Thanks for trying to get to the bottom of it. I agree that the wording in English might call for Insert in the beginning. But the actual sequence of events is like this:
The English description, as I understood what you were proposing, is an introduction to the example code. It should be about the desired outcome, not the sequence of actions or its efficiency. It is something of a motivating statement.
Each of x = 1, ..., 1000 is forwarded to a data-generator, f. The result of f( x ), which I call y, is forwarded to a modifier which, here, executes: repeat n times { cont.push_front( y ); }
That's reasonable to describe what the example code actually does, but it shouldn't be the example's introduction.
The sequence matters, because f( x ) is computed only once, which is the most efficient way.
That's good to point out, but not in the example's introduction.
I use the words data-generator & modifier to be consistent with the naming conventions of the library. To be clear, the data- generator's job is to map arguments to a suitable data-element. The modifier's job is to insert that element (n times, in this case).
That's good, but only to describe the example code, not to introduce it. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 6/24/11 2:55 PM, Stewart, Robert wrote:
er wrote:
On 6/24/11 2:17 PM, Stewart, Robert wrote:
er wrote:
Map 1, 10, 100, and 1000 by function f, which yields f( 1 ), f( 10 ), f( 100 ), f( 1000 ) and insert each, n times, using push_front, in cont. [snip] IOW, something more like the following would suit me better:
Insert elements in cont, using push_front(), where the values are generated by calling f() n times with 1, 10, 100, and 1000, in turn.
Thanks for trying to get to the bottom of it. I agree that the wording in English might call for Insert in the beginning. But the actual sequence of events is like this:
The English description, as I understood what you were proposing, is an introduction to the example code. It should be about the desired outcome, not the sequence of actions or its efficiency. It is something of a motivating statement.
Yes, you are right that the English description should focus on the outcome. But I remain uneasy about "calling f() n times", because if f depends on a state i, such that f() internally triggers i++, the outcome is not the same. It turns to be harder than I anticipated to phrase it in English without recourse to pseudo code...
Each of x = 1, ..., 1000 is forwarded to a data-generator, f. The result of f( x ), which I call y, is forwarded to a modifier which, here, executes: repeat n times { cont.push_front( y ); }
That's reasonable to describe what the example code actually does, but it shouldn't be the example's introduction.
Indeed.
The sequence matters, because f( x ) is computed only once, which is the most efficient way.
That's good to point out, but not in the example's introduction.
Right.

On 24 Jun 2011, at 10:24, er wrote:
I am sure that it is a powerful library, but I really have no idea at all what is going on. I can't even parse the first line as valid C++ in any way. You need better examples, and also is that really the best way you could find to express repeating?
If I try to imagine what the interface would look like after taking into account some of the suggestions that were made, this sentence:
"Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front."
translates to
( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
Questions : - is it more readable, now?
That is more readable.
- What if each example was preceded by its English equivalent. Is this a bit of stretch or a reasonable approach? I think this is a good idea. In general I think you can't over-explain basic examples.
The style the library is using is still.. interesting. I'm still not sure why the '_repeat = n' comes in, and things seem attached strangely. I might perfer something that looked more like boost range's make_range(1,10,100,1000) | f | push_front(cont) But, that might well not design as well as I think it would, or not do what you are suggesting. Chris

On 6/24/11 12:06 PM, Christopher Jefferson wrote:
On 24 Jun 2011, at 10:24, er wrote:
I am sure that it is a powerful library, but I really have no idea at all what is going on. I can't even parse the first line as valid C++ in any way. You need better examples, and also is that really the best way you could find to express repeating?
If I try to imagine what the interface would look like after taking into account some of the suggestions that were made, this sentence:
"Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front."
translates to
( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
Questions : - is it more readable, now?
That is more readable.
Thanks.
- What if each example was preceded by its English equivalent. Is this a bit of stretch or a reasonable approach? I think this is a good idea. In general I think you can't over-explain basic examples.
OK, Thanks.
The style the library is using is still.. interesting. I'm still not sure why the '_repeat = n' comes in, and things seem attached strangely. I might perfer something that looked more like boost range's
make_range(1,10,100,1000) | f | push_front(cont)
I will answer this, but please note that I caused some confusion in answering Steward R. I just posted a correcting message, which perhaps will clarify where option ( _repeat = n ) was meant to achieve, in that case.
But, that might well not design as well as I think it would, or not do what you are suggesting.
Chris _______________________________________________ Unsubscribe& other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

The style the library is using is still.. interesting.
Features that already existed in V1 were made modular, and open for extension, through the use of options. As I argued with Stewart, it's possible to transition towards free functions, so that the use of operator% is only reserved for the less common options, since it seems to be causing some confusion. Also, at the risk of stating the obvious, support for C++0x is an improvement. For example, in push_front<1>( cont )( args1... )...( argsn... ); argi... is variadic. This was done without letting C++03 down : both are supported, such that the syntax is the same. In C++03, it is possible to pass combinations of non-const and const arguments up to macro constant. The total number of arguments is controlled by a macro constant. This degree of control was not present in V1.
I'm still not sure why the '_repeat = n' comes in, and things seem attached strangely. I might perfer something that looked more like boost range's
make_range(1,10,100,1000) | f | push_front(cont)
Let's forget about _repeat = n, for now, and continue with your example. This copy( cvs_deque<int, 1>( 1, 10, 100, 1000 ) | adaptor::transform( f ) std::front_inserter( cont ) ); and this ( put_front<1>( cont ) % ( _data = f ) )( 1, 10, 100, 1000 ); are quite close, I think. The first is more heavy at runtime since it has to allocate a vector, while the second is perhaps more heavy at compile time. Now if you want to repeat the operation of inserting the element f( x ), I'm not sure how you do that with Range, but with V2, after learning the syntax, it's relatively straightforward: ( put_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 ); This is not the only option, of course. For example, say you want to fill only the second half of cont with f( 1 ),..., f( 1000 ): int index = cont.size() / 2; ( put_front<1>( cont ) % ( _data = f ) % ( _iterate = lambda::var( index )++ ) )( 1, 10, 100, 1000 ); is same as cont[ index++ ] = f( 1 ); cont[ index++ ] = f( 10 ); cont[ index++ ] = f( 100 ); cont[ index++ ] = f( 1000 ); Assign is a container tool. Here, iterating is only possible because if cont is a RandomAccessContainer.

Now if you want to repeat the operation of inserting the element f( x ), I'm not sure how you do that with Range, but with V2, after learning the syntax, it's relatively straightforward:
( put_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
This was suggested, which is probably the best: push_front<1>( cont , _data = f, _repeat = n )( 1, 10, 100, 1000 ); More generally, // Fixed arity: push_front<I>( cont , options... )( a1,...,aI,...,z1,...,zI ); // Variadic push_front( cont , options... )( args1 )...( argsn ); where in each case (fixed and variadic), options... modify the semantics of the subsequent calls to operator().

er wrote:
Now if you want to repeat the operation of inserting the element f( x ), I'm not sure how you do that with Range, but with V2, after learning the syntax, it's relatively straightforward:
( put_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
This was suggested, which is probably the best:
push_front<1>( cont , _data = f, _repeat = n )( 1, 10, 100, 1000 );
More generally,
// Fixed arity: push_front<I>( cont , options... )( a1,...,aI,...,z1,...,zI );
// Variadic push_front( cont , options... )( args1 )...( argsn );
where in each case (fixed and variadic), options... modify the semantics of the subsequent calls to operator().
Can we change the name of _data to something like _filter? Also, I don't like the pre and post _ names in the interface. If you want to use a convention related to pre and post _ in the implementation details that's fine, but I don't think it is appropriate in the interface. If you use boost.parameter I think you would get a post _ charater, I believe, so having both pre and post _ for different reasons seems confusing. Also, it would be nice to have iterator pair and range be acceptable alternatives to the fixed arity and variadic options shown above. I realize that would require some SFINAE, but I think being able to consistently initialize containers and then mix the forms would be a big plus. char* cstr[] = "Hello world!"; std::vector<char> chrs; push_back( chrs , filter = to_upper )( cstr, cstr+5 )( '\0' ); chrs contains a null terminated string "HELLO". There is then a whole area of treating std::string like a container by specializing some of your library interfaces that would really be handy. Regards, Luke

On 6/24/11 4:33 PM, Simonson, Lucanus J wrote:
er wrote:
Now if you want to repeat the operation of inserting the element f( x ), I'm not sure how you do that with Range, but with V2, after learning the syntax, it's relatively straightforward:
( put_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
This was suggested, which is probably the best:
push_front<1>( cont , _data = f, _repeat = n )( 1, 10, 100, 1000 );
More generally,
// Fixed arity: push_front<I>( cont , options... )( a1,...,aI,...,z1,...,zI );
// Variadic push_front( cont , options... )( args1 )...( argsn );
where in each case (fixed and variadic), options... modify the semantics of the subsequent calls to operator().
Can we change the name of _data to something like _filter? Also, I don't like the pre and post _ names in the interface. If you want to use a convention related to pre and post _ in the implementation details that's fine, but I don't think it is appropriate in the interface. If you use boost.parameter I think you would get a post _ charater, I believe, so having both pre and post _ for different reasons seems confusing.
Indeed, I should follow the Boost.Parameter convention and use postfix _ for a const objects. _filter (or rather filter_) is fine.
Also, it would be nice to have iterator pair and range be acceptable alternatives to the fixed arity and variadic options shown above. I realize that would require some SFINAE, but I think being able to consistently initialize containers and then mix the forms would be a big plus.
Take push_back( cont )( range1 )( range2 )( range3 ); I guess your SFINAE approach would check whether range1 models ConceptRange and if so, push back each of its elements at the back of cont. Unfortunately, the actual intent might be to treat range1 as an element of cont. So I actually prefer the current approach: push_back( cont ).for_each( range1 ).for_each( range2 ).for_each( range3 ); This puts in cont a total of size( range1 ) + size( range2 ) + size( range3 ) elements. Whereas this push_back( cont )( range1 )( range2 )( range3 ); puts 3 elements in cont : range1, range2, and range3. What do you think?
char* cstr[] = "Hello world!"; std::vector<char> chrs; push_back( chrs , filter = to_upper )( cstr, cstr+5 )( '\0' );
push_back( chrs, filter_ = to_upper ).for_each( make_iterator_range( cstr, cstr + 5) )( '\0' );
chrs contains a null terminated string "HELLO".
There is then a whole area of treating std::string like a container by specializing some of your library interfaces that would really be handy.
Sure, I welcome more examples/problem specifications such as that above. Please do send them. Thanks.
Regards, Luke
_______________________________________________ Unsubscribe& other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

er wrote:
Also, it would be nice to have iterator pair and range be acceptable alternatives to the fixed arity and variadic options shown above. I realize that would require some SFINAE, but I think being able to consistently initialize containers and then mix the forms would be a big plus.
Take
push_back( cont )( range1 )( range2 )( range3 );
I guess your SFINAE approach would check whether range1 models ConceptRange and if so, push back each of its elements at the back of cont. Unfortunately, the actual intent might be to treat range1 as an element of cont. So I actually prefer the current approach:
push_back( cont ).for_each( range1 ).for_each( range2 ).for_each( range3 );
This puts in cont a total of size( range1 ) + size( range2 ) + size( range3 ) elements. Whereas this
push_back( cont )( range1 )( range2 )( range3 );
puts 3 elements in cont : range1, range2, and range3.
What do you think?
I was thinking that we could apply SFINAE on the value type of the container equal to the value type of the iterators/range to know at compile time whether they should be treated as elements or a range/iterator pair. A vector<vector<int>::iterator> container could then be initialized with a pair of vector<vector<int>::iterator>::iterator representing a range or a pair of vector<int>::iterator representing two elements by identifying at compile time which case we have. I don't believe it would ever be ambiguous. SFINAE would also improve the value of your error messages when the interface is used with the wrong type. The error would be "no function operator() accepts type xyz". I would agree, however, that the for_each syntax is at least clear and easier to achieve from the library implementation side. It isn't obvious from a user perspective why they have to use for_each with a range or iterator pair but not for the fixed arity case. I would say that if the user knows the type of the container and the type of the range he knows what to expect without the use of for_each.
char* cstr[] = "Hello world!"; std::vector<char> chrs; push_back( chrs , filter = to_upper )( cstr, cstr+5 )( '\0' );
push_back( chrs, filter_ = to_upper ).for_each( make_iterator_range( cstr, cstr + 5) )( '\0' );
Why do I have to make_iterator_range? Why can't it be overloaded for both iterators and ranges?
chrs contains a null terminated string "HELLO".
There is then a whole area of treating std::string like a container by specializing some of your library interfaces that would really be handy.
Sure, I welcome more examples/problem specifications such as that above. Please do send them. Thanks.
I won't ask you to fix everything that is wrong with std string, stream operators and worst of all wide characters. On the other hand, making std::string behavior more consistent with that of the std containers would be a nice touch and extend the library in a new and useful direction. Regards, Luke

I was thinking that we could apply SFINAE on the value type of the container equal to the value type of the iterators/range to know at compile time whether they should be treated as elements or a range/iterator pair. A vector<vector<int>::iterator> container could then be initialized with a pair of vector<vector<int>::iterator>::iterator representing a range or a pair of vector<int>::iterator representing two elements by identifying at compile time which case we have. I don't believe it would ever be ambiguous. SFINAE would also improve the value of your error messages when the interface is used with the wrong type. The error would be "no function operator() accepts type xyz". I would agree, however, that the for_each syntax is at least clear and easier to achieve from the library imp
Remember that elements are passed through what you call a 'filter' (data-generator), even when it is not specified explicitly. Filter's result_type is the container's value-type, by default: // Push at the front of cont, filter( args1… ),..., filter( argsn… ) push_front( cont )( args1... )...( args... ); Assume filter( *begin( range ) ) and filter( range ) are each valid expressions. In this case, you have an ambiguity that can only be resolved by letting the user specify his intent explicitly, For example: push_front( cont )( args1 ... ).for_each( range )( argsn... );
char* cstr[] = "Hello world!"; std::vector<char> chrs; push_back( chrs , filter = to_upper )( cstr, cstr+5 )( '\0' );
FYI I made sure that using the existing syntax, the above compiles and executes, albeit with char cstr[], not char* cstr[].
push_back( chrs, filter_ = to_upper ).for_each( make_iterator_range( cstr, cstr + 5) )( '\0' );
Why do I have to make_iterator_range? Why can't it be overloaded for both iterators and ranges?
It can be accommodated.
Sure, I welcome more examples/problem specifications such as that above. Please do send them. Thanks.
I won't ask you to fix everything that is wrong with std string, stream operators and worst of all wide characters. On the other hand, making std::string behavior more consistent with that of the std containers would be a nice touch and extend the library in a new and useful direction.
Thanks, but I would need a more detailed request.
Regards, Luke _______________________________________________ Unsubscribe& other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

er wrote:
If I try to imagine what the interface would look like after taking into account some of the suggestions that were made, this sentence:
"Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front."
translates to
( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
Questions : - is it more readable, now? - What if each example was preceded by its English equivalent. Is this a bit of stretch or a reasonable approach?
No, the % operator is ascii art. I guess % means "modifier" in this context, that we are parameterizing the push front. I don't see how what you wrote above is any more readable than this: push_front<1>( cont ) ( _data = f ) ( _repeat = n ) ( 1, 10, 100, 1000 ); But why not pull the modifiers into push_front as optional arguments using boost param push_front<1>( cont, _data = f, _repeat = n ) ( 1, 10, 100, 1000 ); Now it is more clear. I'd still probably need to read the documentation to understand the meaning of _repeat (is it repeat each element or repeat the sequence?) but only the first time I saw it. The concern about needing to read the documentation to read the code is that if the documentation is a magic decoder ring without which the code is unintelligable. People should be able to guess what the code is doing and their guess should be correct, otherwise the interfaces are not intuitive. Regards, Luke

On 6/24/11 3:00 PM, Simonson, Lucanus J wrote:
er wrote:
If I try to imagine what the interface would look like after taking into account some of the suggestions that were made, this sentence:
"Create data elements by mapping 1, 10, 100, 1000 by function f, and insert each result in cont by invoking modifier push_front."
translates to
( push_front<1>( cont ) % ( _data = f ) % ( _repeat = n ) )( 1, 10, 100, 1000 );
Questions : - is it more readable, now? - What if each example was preceded by its English equivalent. Is this a bit of stretch or a reasonable approach?
No, the % operator is ascii art.
I guess % means "modifier" in this context, that we are parameterizing the push front.
Almost, so bear with me. operator% is used for passing an option, either - a data-generator, with syntax _data = f - a modifier, such as _push_front, ( _repeat = n ), whatever there is or want to create.
I don't see how what you wrote above is any more readable than this:
push_front<1>( cont ) ( _data = f ) ( _repeat = n ) ( 1, 10, 100, 1000 );
This invokes operator() over options and arguments. There is a SFINAE way to do it, but it thought it would be confusing. Currently, V2 uses - operator% for options (changes the semantics) - operator() for arguments - operator| piping.
But why not pull the modifiers into push_front as optional arguments using boost param
push_front<1>( cont, _data = f, _repeat = n ) ( 1, 10, 100, 1000 );
Now it is more clear. I'd still probably need to read the documentation to understand the meaning of _repeat (is it repeat each element or repeat the sequence?) but only the first time I saw it.
Indeed, it's very clear. And it's quite easy to do, in fact, and not very costly. Thanks.
The concern about needing to read the documentation to read the code is that if the documentation is a magic decoder ring without which the code is unintelligable. People should be able to guess what the code is doing and their guess should be correct, otherwise the interfaces are not intuitive.
Stated like this I agree.
Regards, Luke _______________________________________________ Unsubscribe& other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

The following next example is also IMO convoluted, I've read it multiple times but I don't understand what it attempts to do.
std::vector<int> numeric( 10 ); iota( numeric, 0 );
typedef std::string str_; typedef variant< int, str_> data_; array<data_, 17> keypad; csv( put( keypad ), "+", "-", "*", "/", "=", ".", "c" ).for_each( numeric );
Call me nitpicky but this example was not meant to illustrate initialization, which does not come until Conversion in the tutorial. Therefore, it is not equivalent to your example below:
Reading the assert macros, I guess this is equivalent?
array<variant<int, std::string>, 17> keypad = { "+", "-", "*", "/", "=", ".", "c", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
The statement that would be equivalent to your statement is : array<data_, 17> keypad = converter( csv_deque<data_, 1>( "+", "-", "*", "/", "=", ".", "c" ).for_each( numeric ) ); or (since you stated a preference for the variadic form): array<data_, 17> keypad = converter( deque<data_> ( "+" )( "-" )( "*" )( "/" )( "=" )( "." )( "c" ) .for_each( numeric ) ); Neither of which is as neat as C++0x's initialization list approach, as already acknowledged in a previous thread. But it's not always supported and does not help for filling a container either.

Hello all! I think Assign 2.0 is not simple library. It's not as technical view as just first impression. What is Boost.Assign? It's library for simplifying of programmer's life. Moreover, it can do this simplifying quickly. But I compare this: vector<int> v; v += 1,2,3,4,5,6,7,8,9; map<string,int> m; insert( m )( "Bar", 1 )( "Foo", 2 ); and this: typedef std::map<std::string, int> map_; map_ cal; typedef map_::value_type p_; boost::for_each( cal | do_csv_put<2>( "jan", 31, "feb", 28, "mar", 31 ), std::cout << lambda::bind( &p_::first, lambda::_1 ) << ':' << lambda::bind( &p_::second, lambda:: _1 ) << ' ' ); // prints jan:31 feb:28 mar:31 Hmmm.... Think about it: this is the first and the simplest example, i.e. "Hello world". The man who does not know what is lambda::bind and not familiar with the concept of adapters (with overloaded operator|) see this and think: "Is this is a simplest example? And this is a library to simplify working with containers??" I know there are libraries which is difficult to start using immediately. Spirit, Proto and some other are really require a deep training. However, these libraries are serious tools. If I want create my own DSEL - I must study Proto deeply. But libraries such as Assign 1.0 can be used immediately, and high qualifications is not required for this. Novices don't starts with Proto, they starts with Assign, etc. And just imagine how this novice read this annotation: "Filling containers with constant or generated data has never been easier." What?? "Never been easier"? Are you kidding? IMO, library that designed only for simplify the filling containers *MUST* be simple. - Denis

On 6/23/11 3:21 AM, Denis Shevchenko wrote:
Hello all!
I think Assign 2.0 is not simple library. It's not as technical view as just first impression.
IMO, library that designed only for simplify the filling containers *MUST* be simple.
That's a very candid assessment which I hear loud and clear, and does a very good job of summarizing the overwhelming impression. But I do think that this proposal fits with the mandate of Boost to seek code reuse, modularity, and convergence towards the C++0x standard. I have given specific arguments to that effect in previous comments. The question is, I think, whether there is sufficient ground for bringing this design and its documentation to meet the demand for greater simplicity? Thanks. PS: The example was not meant as the analogue of hello world. On the contrary, but it seems that is has baffled reviewers more than it has impressed them ;-)
- Denis _______________________________________________ Unsubscribe& other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 22/06/11 21:31, er wrote: <snip>
Conversion from ranges to containers
This sentence doesn't make sence to me. std::container(range.begin(), range.end()) is supported by all containers.
Please see Conversion in the tutorial. The first sentence gives it away:
This section deals with conversion from a Range to collections of elements that need not (but may) support iterators.
As I see it, the primary advantage of this conversion feature is that you can use it when th range only exists as a temporary. e.g. if a function returns a range, you can use it without first assigning that range to a variable. Perhaps this aspect could be made more clear? John Bytheway

This section deals with conversion from a Range to collections of elements that need not (but may) support iterators.
As I see it, the primary advantage of this conversion feature is that you can use it when th range only exists as a temporary. e.g. if a function returns a range, you can use it without first assigning that range to a variable. Perhaps this aspect could be made more clear?
I thought it was made clear in the tutorial, but I am proven wrong. In this example, a container of type C, in the left hand side of <, is constructed in place and filled with values 1, 1, 3 in it. ( csv_deque( 1, 1, 3 ) | convert<C>() ) < fifo;
John Bytheway
_______________________________________________ Unsubscribe& other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (7)
-
Christian Holmquist
-
Christopher Jefferson
-
Denis Shevchenko
-
er
-
John Bytheway
-
Simonson, Lucanus J
-
Stewart, Robert