If boost::fusion is the hammer, then what's the nail?

I think this is the right place to ask this question. As cool as boost::fusion is, there's still one question that bothers me, that is, if boost::fusion is the hammer, then what's the nail? I sure know that it's generic( more so than boost::tuple) and it's complete( with a bundle of algorithms and cool utilities), and it seems that it "solves" some problem quite well( when I read the documents). However, I found it bothering that the documents didn't even mention one real-world application( boost libs aside). Based on my experience, the most frequent scenarios where boost::tuple/fusion is useful are those where one needs a generated-on-the-fly struct for holding return values bundle, or where one needs to return multiple values transparently( well, nearly so), just like those lua does. Other than that, I can't really imagine a scenario where this kinda stuff kicks butts. The biggest disadvantage of boost::fusion, I think, is that the size of it is known and fixed at compile time, which renders it not so qualified as a truly heterogeneous container. And by the bye, that it can be used to build a handy pseudo-struct that enables full introspection really is a good and fancy feature. However, there's something fundamentally irremediable of it, that is, it can't be used to build a full-fledged class( rather than a POD-struct), which, I think, will too reduce its usefulness w.r.t. this kinda situations. I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments?

Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments?
I can give you one use I have for tuples: I have a piece of boilerplate code that accepts a tuple (of any size) and prints out either a csv file or a boost::array C++ code conaining the data passed. It allows me to output data for graphing, or matrixes of test data very quickly just by creating a short function that returns a tuple, and then passing that function to my boilerplate. If I want more columns of data I just increase the size of tuple by 1. I suppose I could have used a vector instead, but it's less elegant somehow. I've also used tuples in place of struct's whenever I have an API that needs to return more than 2 items. For example I have some root finding algorithms that accept a unary functor whose root is to be found: the functor returns a tuple containing the function evaluation, plus the first two derivatives. Using a tuple here simplified both implementation and documentation. Had I felt the need, I could have performed compile time dispatch to different algorithms based on how many derivatives were available (the tuples size). HTH, John.

John Maddock wrote:
I have a piece of boilerplate code that accepts a tuple (of any size) and prints out either a csv file or a boost::array C++ code conaining the data passed.
It allows me to output data for graphing, or matrixes of test data very quickly just by creating a short function that returns a tuple, and then passing that function to my boilerplate. If I want more columns of data I just increase the size of tuple by 1.
I suppose I could have used a vector instead, but it's less elegant somehow.
I've also used tuples in place of struct's whenever I have an API that needs to return more than 2 items. For example I have some root finding algorithms that accept a unary functor whose root is to be found: the functor returns a tuple containing the function evaluation, plus the first two derivatives. Using a tuple here simplified both implementation and documentation. Had I felt the need, I could have performed compile time dispatch to different algorithms based on how many derivatives were available (the tuples size).
Thanks John, that's very informative. Although a little bit of code would be excellent :-) I do have a question about the second application, I mean, don't you have to wait until runtime to know how many derivatives a function actually have? Or is it just that it can be known in the very scenario you mentioned?

Weapon Liu wrote:
Thanks John, that's very informative. Although a little bit of code would be excellent :-)
See http://freespace.virgin.net/boost.regex/toolkit/html/math_toolkit/toolkit/ro...
I do have a question about the second application, I mean, don't you have to wait until runtime to know how many derivatives a function actually have? Or is it just that it can be known in the very scenario you mentioned?
The solvers are not intended to be "hands off", which is to say they're used when: I want to compute the inverse of a function numerically. I know how many derivatives I can compute (if any). I know where to start looking for the root, and which method performs the best. It might be possible to use these root finders as the underlying engine for a higher level API for use in situations where you know next to nothing about the problem domain. But you can get yourself into so much trouble if you start looking for the root in the wrong place, or just pick the wrong algorithm for the job, that I would rather users of that code are forced to think at least a little about the problem domain. John.

John Maddock wrote:
Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments?
I can give you one use I have for tuples:
I've also used tuples in place of struct's whenever I have an API that needs to return more than 2 items. For example I have some root finding algorithms that accept a unary functor whose root is to be found: the functor returns a tuple containing the function evaluation, plus the first two derivatives. Using a tuple here simplified both implementation and documentation.
I have used tuples in a similar manner, but I kinda think its not the best to do; a named tuple would be more appropriate, or maybe just a struct. What bothers me is the self-explanatory properties are lost; I can't figure out what tuple element that means what by just looking at the code. Is it tie(eval,dev1,dev2) = find_root(...) or tie(dev1,dev2,eval) = find_root(...) ? Instead a struct would be somewhat unambiguous: template< class Float > struct root_result { Float eval, dev1, dev2; }; root_result<float> r = find_root( ... ); -Thorsten

On 12/14/2006 08:52 AM, Thorsten Ottosen wrote: [snip]
I have used tuples in a similar manner, but I kinda think its not the best to do; a named tuple would be more appropriate, or maybe just a struct.
What bothers me is the self-explanatory properties are lost; I can't figure out what tuple element that means what by just looking at the code. Is it
tie(eval,dev1,dev2) = find_root(...)
or
tie(dev1,dev2,eval) = find_root(...)
?
This "named tuple" is essentially the composite_product whose test code is here: http://boost.cvs.sourceforge.net/boost-sandbox/boost-sandbox/libs/indexed_ty... Instead of field names, numerators in an enumeration are used. For example, in the above test, the numerators, {a,b,c} of the enumeration, indices, correspond to the field names in the following struct: struct r_struct { field_ndx<a> a_field; field_ndx<b> b_field; field_ndx<c> c_field; };

Larry Evans wrote:
On 12/14/2006 08:52 AM, Thorsten Ottosen wrote: [snip]
I have used tuples in a similar manner, but I kinda think its not the best to do; a named tuple would be more appropriate, or maybe just a struct.
What bothers me is the self-explanatory properties are lost; I can't figure out what tuple element that means what by just looking at the code. Is it
tie(eval,dev1,dev2) = find_root(...)
or
tie(dev1,dev2,eval) = find_root(...)
?
This "named tuple" is essentially the composite_product whose test code is here:
http://boost.cvs.sourceforge.net/boost-sandbox/boost-sandbox/libs/indexed_ty...
Instead of field names, numerators in an enumeration are used. For example, in the above test, the numerators, {a,b,c} of the enumeration, indices, correspond to the field names in the following struct:
struct r_struct { field_ndx<a> a_field; field_ndx<b> b_field; field_ndx<c> c_field; };
Looks very similar to the fusion map except for the use of enum tags. Enums are not good as tags because you have to close them; they are not open to extension. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Thorsten Ottosen wrote:
John Maddock wrote:
Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments?
I can give you one use I have for tuples:
I've also used tuples in place of struct's whenever I have an API that needs to return more than 2 items. For example I have some root finding algorithms that accept a unary functor whose root is to be found: the functor returns a tuple containing the function evaluation, plus the first two derivatives. Using a tuple here simplified both implementation and documentation.
I have used tuples in a similar manner, but I kinda think its not the best to do; a named tuple would be more appropriate, or maybe just a struct.
What bothers me is the self-explanatory properties are lost; I can't figure out what tuple element that means what by just looking at the code. Is it
tie(eval,dev1,dev2) = find_root(...)
or
tie(dev1,dev2,eval) = find_root(...)
?
Instead a struct would be somewhat unambiguous:
template< class Float > struct root_result { Float eval, dev1, dev2; };
root_result<float> r = find_root( ... );
You can probably use a fusion map if you want the elements named. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Thorsten Ottosen <thorsten.ottosen <at> dezide.com> writes: [...]
What bothers me is the self-explanatory properties are lost; I can't figure out what tuple element that means what by just looking at the code. Is it
tie(eval,dev1,dev2) = find_root(...)
or
tie(dev1,dev2,eval) = find_root(...)
?
Instead a struct would be somewhat unambiguous:
template< class Float > struct root_result { Float eval, dev1, dev2; };
root_result<float> r = find_root( ... );
One could always do something like template< class Float > struct root_result : public tuple<Float &, Float &, Float &> { root_result() : tuple<Float &, Float &, Float &>(eval, dev1, dev2) {} Float eval, dev1, dev2; }; And get the best of both worlds. I sort of wish the compiler would create it by magic, though. Cheers, Nicola Musatti

"John Maddock" <john@johnmaddock.co.uk> wrote in message news:006401c71f85$12d1a8b0$20570252@fuji...
Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments?
I can give you one use I have for tuples:
I have a piece of boilerplate code that accepts a tuple (of any size) and prints out either a csv file or a boost::array C++ code conaining the data passed.
It allows me to output data for graphing, or matrixes of test data very quickly just by creating a short function that returns a tuple, and then passing that function to my boilerplate. If I want more columns of data I just increase the size of tuple by 1.
I suppose I could have used a vector instead, but it's less elegant somehow.
How the std::vector<boost::variant> is less elegant? 1. It's could be implemented in cpp file. Your tuple based solution is in header right? 2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around The only drawback is that you need to know set of types ahead of time. But this is minor IMO and it's quite easy to add an extra type whether a need arise. Gennadiy

Hi Gennadiy, sorry for jumping in here. But I believe this post might need a bugfix. Gennadiy Rozental wrote:
"John Maddock" <john@johnmaddock.co.uk> wrote in message news:006401c71f85$12d1a8b0$20570252@fuji...
Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments?
I can give you one use I have for tuples:
I have a piece of boilerplate code that accepts a tuple (of any size) and prints out either a csv file or a boost::array C++ code conaining the data passed.
It allows me to output data for graphing, or matrixes of test data very quickly just by creating a short function that returns a tuple, and then passing that function to my boilerplate. If I want more columns of data I just increase the size of tuple by 1.
I suppose I could have used a vector instead, but it's less elegant somehow.
How the std::vector<boost::variant> is less elegant?
1. It's could be implemented in cpp file. Your tuple based solution is in header right?
Right. It can be both an advantage or an disadvantage depending on what you're up to (modularity vs. genericity). (Sidenote: If you know how the tuples will look like you can instantiate them explicitly and toast them into a cpp file, but I'd probably go for vector<variant> in case I need a translation boundary for the sake of convenience).
2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around
AFAIK Boost.Variant calculates the storage size based on the largest object (just like a union does). It also has good reason to do so since heap allocation is an expensive operation. IOW if we have vector< variant<some_huge_class,int,int> > and most of the variants in the vector are ints we're wasting memory.
The only drawback is that you need to know set of types ahead of time. But this is minor IMO and it's quite easy to add an extra type whether a need arise.
Here's another drawback: Say we want to visit every entry in the sequence and use an appropriate function for each type of thing we find. With vector< variant<...> > we need a) a loop and b) to choose the function at runtime. Both can be eliminated with Fusion and it even takes less code to emphasize it. Regards, Tobias

"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:els60p$kdv$1@sea.gmane.org...
Hi Gennadiy,
sorry for jumping in here. But I believe this post might need a bugfix.
Gennadiy Rozental wrote:
"John Maddock" <john@johnmaddock.co.uk> wrote in message news:006401c71f85$12d1a8b0$20570252@fuji...
Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments?
I can give you one use I have for tuples:
I have a piece of boilerplate code that accepts a tuple (of any size) and prints out either a csv file or a boost::array C++ code conaining the data passed.
It allows me to output data for graphing, or matrixes of test data very quickly just by creating a short function that returns a tuple, and then passing that function to my boilerplate. If I want more columns of data I just increase the size of tuple by 1.
I suppose I could have used a vector instead, but it's less elegant somehow.
How the std::vector<boost::variant> is less elegant?
1. It's could be implemented in cpp file. Your tuple based solution is in header right?
Right. It can be both an advantage or an disadvantage depending on what you're up to (modularity vs. genericity).
Here is my logic: we should prefer to minimize dependency, unless performance in visibly hurt in performance critical area => we should prefer to place implementation in cpp files under the same circomstances => we were talking anout output operation, so performace is a) not that critical b) mostly lost in syste i/o => i/o operation we should prefer to implement offline In other words: for the problem at hand inline implementation is disadvantage.
(Sidenote: If you know how the tuples will look like you can instantiate them explicitly and toast them into a cpp file, but I'd probably go for vector<variant> in case I need a translation boundary for the sake of convenience).
So would I
2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around
AFAIK Boost.Variant calculates the storage size based on the largest object (just like a union does). It also has good reason to do so since heap allocation is an expensive operation. IOW if we have vector< variant<some_huge_class,int,int> > and most of the variants in the vector are ints we're wasting memory.
In case like this we should be using snart_ptr<some_huge_class> instead of some_huge_class as a member of tuple. But for the task at hand I don't see this as a real problem anyway.
The only drawback is that you need to know set of types ahead of time. But this is minor IMO and it's quite easy to add an extra type whether a need arise.
Here's another drawback: Say we want to visit every entry in the sequence and use an appropriate function for each type of thing we find. With vector< variant<...> > we need a) a loop and b) to choose the function at runtime. Both can be eliminated with Fusion and it even takes less code to emphasize it.
Again. It's probably applicable for the performance critical area. For the output operation I would stick with BOOST_FOREACH or stl algorithm. In my experience performance critical tasks are comparatively rare and we should not blur the clarity and simplicity for no particular reason. Gennadiy P.S. please do not take these posts as a critique of the fusion lib, I just do not want us to be carried away by it's power.

Gennadiy Rozental wrote:
"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:els60p$kdv$1@sea.gmane.org...
Hi Gennadiy,
sorry for jumping in here. But I believe this post might need a bugfix.
Gennadiy Rozental wrote:
"John Maddock" <john@johnmaddock.co.uk> wrote in message news:006401c71f85$12d1a8b0$20570252@fuji...
Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments?
I can give you one use I have for tuples:
I have a piece of boilerplate code that accepts a tuple (of any size) and prints out either a csv file or a boost::array C++ code conaining the data passed.
It allows me to output data for graphing, or matrixes of test data very quickly just by creating a short function that returns a tuple, and then passing that function to my boilerplate. If I want more columns of data I just increase the size of tuple by 1.
I suppose I could have used a vector instead, but it's less elegant somehow.
How the std::vector<boost::variant> is less elegant?
1. It's could be implemented in cpp file. Your tuple based solution is in header right?
Right. It can be both an advantage or an disadvantage depending on what you're up to (modularity vs. genericity).
Here is my logic:
we should prefer to minimize dependency,
Where are we doing that? A template written towards concepts has no dependencies while a separate-translation-unit-solution depends at least on vector, variant and whatever types that variant is specialized with.
unless performance in visibly hurt in performance critical area => we should prefer to place implementation in cpp files under the same circomstances => we were talking anout output operation, so performace is a) not that critical b) mostly lost in syste i/o
=> i/o operation we should prefer to implement offline
In other words: for the problem at hand inline implementation is disadvantage.
The bad extensibility of your proposed solution is much worse (especially for IO): Extending the variant requires you to touch the header (and can cause a major rebuild). It also requires you to add a switch case for the new type to the code in the cpp file. If you have a lot of "ostreamable" types (which is not too uncommon of a case), you end up with many (totally unecessary) dependencies that will cause your cpp file to be rebuilt a lot. For the Fusion case things just work. No dependencies, no code has to be written to have things work with another type. (Given the ostream operators exists in both cases).
(Sidenote: If you know how the tuples will look like you can instantiate them explicitly and toast them into a cpp file, but I'd probably go for vector<variant> in case I need a translation boundary for the sake of convenience).
(-; Not that it's always possible, due to combinational explosion).
So would I
As shown above, a translation boundary doesn't always make things better, though. In fact, removing generericity can introduce much more dependencies than it eliminates.
2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around
AFAIK Boost.Variant calculates the storage size based on the largest object (just like a union does). It also has good reason to do so since heap allocation is an expensive operation. IOW if we have vector< variant<some_huge_class,int,int> > and most of the variants in the vector are ints we're wasting memory.
In case like this we should be using snart_ptr<some_huge_class> instead of some_huge_class as a member of tuple. But for the task at hand I don't see this as a real problem anyway.
Well, the statements from your OP read pretty general to me (and, excuse my bluntness, these two in particular are just plainly wrong in any context).
The only drawback is that you need to know set of types ahead of time. But this is minor IMO and it's quite easy to add an extra type whether a need arise.
Here's another drawback: Say we want to visit every entry in the sequence and use an appropriate function for each type of thing we find. With vector< variant<...> > we need a) a loop and b) to choose the function at runtime. Both can be eliminated with Fusion and it even takes less code to emphasize it.
Again. It's probably applicable for the performance critical area. For the output operation I would stick with BOOST_FOREACH or stl algorithm.
In my experience performance critical tasks are comparatively rare and we should not blur the clarity and simplicity for no particular reason.
Yes, we shouldn't blur the clarity. The Fusion solution is simpler and cleaner (just implement both ways and you'll see). Further you are proposing to violate the golden law of modularization: "Find modules with strong coherence and provide loose coupling among them" (so the vector<variant> solution is bad design even if Fusion didn't exist).
Gennadiy
P.S. please do not take these posts as a critique of the fusion lib, I just do not want us to be carried away by it's power.
No offense taken (why should I? I'm not even its author). In fact, I think that critique is a good thing - and so is critique to critique. I also believe that it's important to explore the power of new libraries, rather than to run away from it. Regards, Tobias

"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:elu6fo$ib2$1@sea.gmane.org...
Gennadiy Rozental wrote:
Here is my logic:
we should prefer to minimize dependency,
Where are we doing that? A template written towards concepts has no dependencies while a separate-translation-unit-solution depends at least on vector, variant
In part yes. But usually primary source of dependencies is not an interface, but implementation. So in this regard usually it's more preferrable to hide implementation to coding in generic concepts. Also STL components are not so much an extra dependancy anyway (they are present in many/most non-trivial translation units anyway)
and whatever types that variant is specialized with.
tuple will depend on the same types as well.
unless performance in visibly hurt in performance critical area => we should prefer to place implementation in cpp files under the same circomstances => we were talking anout output operation, so performace is a) not that critical b) mostly lost in syste i/o
=> i/o operation we should prefer to implement offline
In other words: for the problem at hand inline implementation is disadvantage.
The bad extensibility of your proposed solution is much worse (especially for IO):
I don't argue that tuple based solution is much more easily extandible.
Extending the variant requires you to touch the header (and can cause a major rebuild). It also requires you to add a switch case for the new type to the code in the cpp file. If you have a lot of "ostreamable" types (which is not too uncommon of a case), you end up with many (totally unecessary) dependencies that will cause your cpp file to be rebuilt a lot.
In practive unless you are writing a generic component to be used by wide audience, set of used types may not change that frequently In case if I do have a lof of different types or this set is not fixed, I would prefer header based solution either
2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around
AFAIK Boost.Variant calculates the storage size based on the largest object (just like a union does). It also has good reason to do so since heap allocation is an expensive operation. IOW if we have vector< variant<some_huge_class,int,int> > and most of the variants in the vector are ints we're wasting memory.
In case like this we should be using snart_ptr<some_huge_class> instead of some_huge_class as a member of tuple. But for the task at hand I don't see this as a real problem anyway.
Well, the statements from your OP read pretty general to me (and, excuse my bluntness, these two in particular are just plainly wrong in any context).
How are they wrong? 2. ... Let's say you have record with 10 optional elements. tuple based solution will end up compiling in anything from 1 to 10! different versions of the same function, while offline version will be only one. Also in case if you always want output in the same 10 column format I dont see easy way to implement it using tuples. 3. ... If you take tuples by value you will end up copying huge structures. If not - the only difference I could see in an extra jump command per value, in practice completely negligeble. Also keep in mind that tupble based solution will create tuples in place while variant collections could be used throughout the application as a generic record storage.
Again. It's probably applicable for the performance critical area. For the output operation I would stick with BOOST_FOREACH or stl algorithm.
In my experience performance critical tasks are comparatively rare and we should not blur the clarity and simplicity for no particular reason.
Yes, we shouldn't blur the clarity. The Fusion solution is simpler and cleaner (just implement both ways and you'll see).
Unfortunately I am not well versed in tuple/fusion interfaces. Lets say you have comparatively expensive overloaded output operation foo. BOOST_FOREACH solutions would look something like this (ignoring header line) printer p( foo ) // don't remember exact syntax for the visitor definition BOOST_FOREACH( my_v const& v, record ) { apply_visitor( p, v ); } After compiler optimizations it should be equivalent to BOOST_FOREACH( my_v const& v, record ) { switch( v.type){ case 0: foo( *(type0*)v.data ); ... case 10: foo( *(type10*)v.data ); } } So that' why I think variant solution should be as fact (almost). If you avoid the looping it will be even closer. Gennadiy

Gennadiy Rozental wrote:
"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:elu6fo$ib2$1@sea.gmane.org...
Gennadiy Rozental wrote:
Here is my logic:
we should prefer to minimize dependency,
Where are we doing that? A template written towards concepts has no dependencies while a separate-translation-unit-solution depends at least on vector, variant
In part yes. But usually primary source of dependencies is not an interface, but implementation.
What?! Dependencies in the interface are the worst there are, because they transitively apply to any other component that uses the interface. Would you elaborate, please?
So in this regard usually it's more preferrable to hide implementation to coding in generic concepts.
Not at the price of compromising extensibility and adding unecessary dependencies to the interface. It only makes sense to use a translation unit if there is some benefit. There is none here - not even the fixed costs of compilation will armortize (dependency scanning of the build system, compiler startup, etc.) and I guess it won't.
Also STL components are not so much an extra dependancy anyway (they are present in many/most non-trivial translation units anyway)
Maybe (although I usually try to stick with a stricter policy regarding header dependencies) ...
and whatever types that variant is specialized with.
... BUT this ^^^ is the real problem.
tuple will depend on the same types as well.
template<class Tuple> void func(Tuple const &); does not depend on any type that'll be in the tuple (and that's the kind of interface that would usually accept a Fusion tuple). <snip>
Well, the statements from your OP read pretty general to me (and, excuse my bluntness, these two in particular are just plainly wrong in any context).
How are they wrong?
2. ... Let's say you have record with 10 optional elements. tuple based solution will end up compiling in anything from 1 to 10! different versions of the same function, while offline version will be only one. Also in case if you always want output in the same 10 column format I dont see easy way to implement it using tuples.
We can take an arbitrary Fusion tuple and iterate it. Why should we be limited to a fixed number of elements?! Also, only the templates that really get instantiated end up in the binary and ultimately there are no functions at all and things will be inlined. Please note that Fusion tuples do not store any dummy elements up to some maximum, or so (in case that's what you believe).
3. ... If you take tuples by value you will end up copying huge structures.
It's much worse to copy std::vector, because we'll need dynamic allocation on top of the copying, which will never armortize for just few elements. Also we'll have to copy more bytes, N*sizeof(largest) instead of sum[N,i](sizeof(T_i)). <snip>
Yes, we shouldn't blur the clarity. The Fusion solution is simpler and cleaner (just implement both ways and you'll see).
Unfortunately I am not well versed in tuple/fusion interfaces. Lets say you have comparatively expensive overloaded output operation foo. BOOST_FOREACH solutions would look something like this (ignoring header line)
<code> In Fusion it's: fusion::for_each( seq, v ); Regards, Tobias

"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:eluqo4$d97$1@sea.gmane.org...
In part yes. But usually primary source of dependencies is not an interface, but implementation.
What?! Dependencies in the interface are the worst there are, because they transitively apply to any other component that uses the interface. Would you elaborate, please?
I am not sure what is unclear. If you put implementation into header you not only bring dependencies caused by types used in interface, but also all the staff that is used by the implementation.
So in this regard usually it's more preferrable to hide implementation to coding in generic concepts.
Not at the price of compromising extensibility and adding unecessary dependencies to the interface.
It only makes sense to use a translation unit if there is some benefit. There is none here - not even the fixed costs of compilation will armortize (dependency scanning of the build system, compiler startup, etc.) and I guess it won't.
Also STL components are not so much an extra dependancy anyway (they are present in many/most non-trivial translation units anyway)
Maybe (although I usually try to stick with a stricter policy regarding header dependencies) ...
and whatever types that variant is specialized with.
... BUT this ^^^ is the real problem.
Looks like our primary misunderstanding is that we are talking about different initial points/different problem domain. If we are talking about comparatively simple generic component for let's say csv format output, that could be used by completely different development teams I would easily agree that tuple or similar is the best approach. If I am writing component to be used in my project for the the same output generation I probably prefer variant instead.
Let's say you have record with 10 optional elements. tuple based solution will end up compiling in anything from 1 to 10! different versions of the same function, while offline version will be only one. Also in case if you always want output in the same 10 column format I dont see easy way to implement it using tuples.
We can take an arbitrary Fusion tuple and iterate it. Why should we be limited to a fixed number of elements?!
Also, only the templates that really get instantiated end up in the binary
So if I have 100 different calls to the output function I will endup with 100 different instantiations instantiation, right? And even in case of inlining my code size would be 100 times more?
and ultimately there are no functions at all and things will be inlined.
Non nessesarily, It all depends on complexity of the output function. One of the compiler I was using rejects doing inlining under any optimization options if there is a throw statement. Another one does do inlining, but the resulting version works visibly slower than the one that does not.
In Fusion it's:
fusion::for_each( seq, v );
Um, where is an actor here? Meaning where is the call to an output method Gennadiy

On 12/15/06, Gennadiy Rozental <gennadiy.rozental@thomson.com> wrote:
[snipped]
Let's say you have record with 10 optional elements. tuple based solution will end up compiling in anything from 1 to 10! different versions of the same function, while offline version will be only one. Also in case if you always want output in the same 10 column format I dont see easy way to implement it using tuples.
We can take an arbitrary Fusion tuple and iterate it. Why should we be limited to a fixed number of elements?!
Also, only the templates that really get instantiated end up in the binary
So if I have 100 different calls to the output function I will endup with 100 different instantiations instantiation, right? And even in case of inlining my code size would be 100 times more?
I believe that most tuple functions when inlined should occuppy almost the same as a function call, so I dont belive your estimative would be correct. With register allocation playing right, there would be less, or none, need to save registers and the results being used directly too, the whole using function could be even smaller when using the inlined one.
[snipped]
Gennadiy
-- Felipe Magno de Almeida

"Felipe Magno de Almeida" <felipe.m.almeida@gmail.com> wrote in message news:a2b17b60612151137o20340d10i5c2a2f18e293ca5c@mail.gmail.com...
On 12/15/06, Gennadiy Rozental <gennadiy.rozental@thomson.com> wrote:
[snipped]
Let's say you have record with 10 optional elements. tuple based solution will end up compiling in anything from 1 to 10! different versions of the same function, while offline version will be only one. Also in case if you always want output in the same 10 column format I dont see easy way to implement it using tuples.
We can take an arbitrary Fusion tuple and iterate it. Why should we be limited to a fixed number of elements?!
Also, only the templates that really get instantiated end up in the binary
So if I have 100 different calls to the output function I will endup with 100 different instantiations instantiation, right? And even in case of inlining my code size would be 100 times more?
I believe that most tuple functions when inlined should occuppy almost the same as a function call, so I dont belive your estimative would be correct. With register allocation playing right, there would be less, or none, need to save registers and the results being used directly too, the whole using function could be even smaller when using the inlined one.
It's possible. Still there code size will be significantly bigger in comparison with offline implementation, right? Gennadiy

Gennadiy Rozental wrote:
"Felipe Magno de Almeida" <felipe.m.almeida@gmail.com> wrote in message news:a2b17b60612151137o20340d10i5c2a2f18e293ca5c@mail.gmail.com...
On 12/15/06, Gennadiy Rozental <gennadiy.rozental@thomson.com> wrote:
[snipped]
Let's say you have record with 10 optional elements. tuple based solution will end up compiling in anything from 1 to 10! different versions of the same function, while offline version will be only one. Also in case if you always want output in the same 10 column format I dont see easy way to implement it using tuples. We can take an arbitrary Fusion tuple and iterate it. Why should we be limited to a fixed number of elements?!
Also, only the templates that really get instantiated end up in the binary So if I have 100 different calls to the output function I will endup with 100 different instantiations instantiation, right? And even in case of inlining my code size would be 100 times more? I believe that most tuple functions when inlined should occuppy almost the same as a function call, so I dont belive your estimative would be correct. With register allocation playing right, there would be less, or none, need to save registers and the results being used directly too, the whole using function could be even smaller when using the inlined one.
It's possible. Still there code size will be significantly bigger in comparison with offline implementation, right?
No, there is zero overhead. See my other post. Fusion vector, for example is as fast as a struct. Why? because it *IS* as struct. So, accesses to its members, for example is as fast as: s.member Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
Gennadiy Rozental wrote:
It's possible. Still there code size will be significantly bigger in comparison with offline implementation, right?
Fusion vector, for example is as fast as a struct. But Gennadiy is talking about code size, not execution speed. The very fact that makes Fusion so fast, its ability to compile down to code that looks like it was specifically written for the case at hand, means that every usage will have almost exactly the same code, but slightly different - as if it was written specifically for each case by hand.
Sebastian Redl

Sebastian Redl wrote:
Joel de Guzman wrote:
Gennadiy Rozental wrote:
It's possible. Still there code size will be significantly bigger in comparison with offline implementation, right?
Fusion vector, for example is as fast as a struct. But Gennadiy is talking about code size, not execution speed. The very fact that makes Fusion so fast, its ability to compile down to code that looks like it was specifically written for the case at hand, means that every usage will have almost exactly the same code, but slightly different - as if it was written specifically for each case by hand.
Ok, let's see... for_each(x, f) generates: f(x.m1) f(x.m2) f(x.m3) f(x.m4) ... then: for_each(y, f) which gives: f(y.m1) f(y.m2) f(y.m3) f(y.m4) ... so where is the overhead? It's as minimal as it can be. It can't be any better than that. Duplication of code? Of course! There's no way around that given the polymorphic nature of 'f'. Even variant would generate these code. Now... std::for_each(x, f) // x is a variant std::for_each(y, f) // y is a variant in addition to what fusion does (zero overhead), the variant code will also have to emit some switch code to dispatch to the correct 'f' (visitor) overload. .......... Really, this is all very wasteful of time. When in doubt, profile, test! See the assembler. If you think variant can be as optimal as fusion, be my guest, profile the code and examine the assembler results (in terms of code size *and* speed). Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
Now...
std::for_each(x, f) // x is a variant std::for_each(y, f) // y is a variant
Darned. I meant a vector of variants of course ;-)
in addition to what fusion does (zero overhead), the variant code will also have to emit some switch code to dispatch to the correct 'f' (visitor) overload.
Anyway, having said all that, vector of variants do have their place. Its most important property is the ability to dynamically resize the container. Fusion sequences will always have fixed sizes. So, bottom line, use the right tool for the job. Both have their place and comparing one to the other is like comparing std::vector with boost::array. There's reason for the existence of both. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

On 12/15/06, Gennadiy Rozental <gennadiy.rozental@thomson.com> wrote:
[snipped]
I believe that most tuple functions when inlined should occuppy almost the same as a function call, so I dont belive your estimative would be correct. With register allocation playing right, there would be less, or none, need to save registers and the results being used directly too, the whole using function could be even smaller when using the inlined one.
It's possible. Still there code size will be significantly bigger in comparison with offline implementation, right?
Probably the other way around. The compiler would have to emit code to make the function call (pass arguments, make the call machine code, reallocate the return value in another register). And *that* code would be bigger than using the inlined version of tuples, which wouldnt need to call anything, just access the value directly. And if possibilities to optimize away other instructions through the possible optimizations that inlining offers.
Gennadiy
Best regards, -- Felipe Magno de Almeida

Felipe Magno de Almeida wrote:
On 12/15/06, Gennadiy Rozental <gennadiy.rozental@thomson.com> wrote:
[snipped]
Let's say you have record with 10 optional elements. tuple based solution will end up compiling in anything from 1 to 10! different versions of the same function, while offline version will be only one. Also in case if you always want output in the same 10 column format I dont see easy way to implement it using tuples. We can take an arbitrary Fusion tuple and iterate it. Why should we be limited to a fixed number of elements?!
Also, only the templates that really get instantiated end up in the binary So if I have 100 different calls to the output function I will endup with 100 different instantiations instantiation, right? And even in case of inlining my code size would be 100 times more?
I believe that most tuple functions when inlined should occuppy almost the same as a function call, so I dont belive your estimative would be correct. With register allocation playing right, there would be less, or none, need to save registers and the results being used directly too, the whole using function could be even smaller when using the inlined one.
Fusion has some perf tests for that. They are contained in libs/fusion/example/performance. With good compilers like VC++ and Intel, the abstraction penalty is zero. In other words, there is zero overhead. You can see that by looking at the assembler code. Go check it out! Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Gennadiy Rozental wrote:
"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:eluqo4$d97$1@sea.gmane.org...
In part yes. But usually primary source of dependencies is not an interface, but implementation.
What?! Dependencies in the interface are the worst there are, because they transitively apply to any other component that uses the interface. Would you elaborate, please?
I am not sure what is unclear.
In the context of translation units: interface = header implementation = source file In the context of classes, functions or templates thereof: interface = declaration implementation = definition
If you put implementation into header you not only bring dependencies caused by types used in interface, but also all the staff that is used by the implementation.
OK, I see. But it doesn't really apply here, because a non-templated declaration generates much more and heavier dependencies than the defintion.
Looks like our primary misunderstanding is that we are talking about different initial points/different problem domain. If we are talking about comparatively simple generic component for let's say csv format output, that could be used by completely different development teams I would easily agree that tuple or similar is the best approach.
If I am writing component to be used in my project for the the same output generation I probably prefer variant instead.
...and all translation units that use that IO routine need a rebuild once another printable type is introduced. I'd never call a design like that "elegant", regardless of the scale. Keep in mind that "good practice", "common practice" and "practicability" can refer to different things, though ;-).
Let's say you have record with 10 optional elements. tuple based solution will end up compiling in anything from 1 to 10! different versions of the same function, while offline version will be only one. Also in case if you always want output in the same 10 column format I dont see easy way to implement it using tuples.
We can take an arbitrary Fusion tuple and iterate it. Why should we be limited to a fixed number of elements?!
Also, only the templates that really get instantiated end up in the binary
So if I have 100 different calls to the output function I will endup with 100 different instantiations instantiation, right? And even in case of inlining my code size would be 100 times more?
It depends. Surprisingly, most compilers factor out similarities among template instantiations pretty well. And of course you can write non- (explicit or implicit) inline functions.
and ultimately there are no functions at all and things will be inlined.
Non nessesarily, It all depends on complexity of the output function. One of the compiler I was using rejects doing inlining under any optimization options if there is a throw statement. Another one does do inlining, but the resulting version works visibly slower than the one that does not.
In Fusion it's:
fusion::for_each( seq, v );
Um, where is an actor here? Meaning where is the call to an output method
The v stands for visitor (I just noticed, you used v for variant, sorry for the inconsistency) - basically the same kind of thing we'd use with Boost.Variant, so I omitted it just like you did. Regards, Tobias

Tobias Schwinger wrote: on it's:
fusion::for_each( seq, v );
Um, where is an actor here? Meaning where is the call to an output method
The v stands for visitor (I just noticed, you used v for variant, sorry for the inconsistency) - basically the same kind of thing we'd use with Boost.Variant, so I omitted it just like you did.
It would be (for completeness sake): for_each(seq, cout << _1); (using lambda/phoenix, of course) Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Gennadiy Rozental wrote:
"John Maddock" <john@johnmaddock.co.uk> wrote in message news:006401c71f85$12d1a8b0$20570252@fuji...
Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments? I can give you one use I have for tuples:
I have a piece of boilerplate code that accepts a tuple (of any size) and prints out either a csv file or a boost::array C++ code conaining the data passed.
It allows me to output data for graphing, or matrixes of test data very quickly just by creating a short function that returns a tuple, and then passing that function to my boilerplate. If I want more columns of data I just increase the size of tuple by 1.
I suppose I could have used a vector instead, but it's less elegant somehow.
How the std::vector<boost::variant> is less elegant?
1. It's could be implemented in cpp file. Your tuple based solution is in header right? 2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around
The only drawback is that you need to know set of types ahead of time. But this is minor IMO and it's quite easy to add an extra type whether a need arise.
Another drawback of std::vector<boost::variant> is speed, of course. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:elskhd$b32$2@sea.gmane.org...
Gennadiy Rozental wrote:
"John Maddock" <john@johnmaddock.co.uk> wrote in message news:006401c71f85$12d1a8b0$20570252@fuji...
Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments? I can give you one use I have for tuples:
I have a piece of boilerplate code that accepts a tuple (of any size) and prints out either a csv file or a boost::array C++ code conaining the data passed.
It allows me to output data for graphing, or matrixes of test data very quickly just by creating a short function that returns a tuple, and then passing that function to my boilerplate. If I want more columns of data I just increase the size of tuple by 1.
I suppose I could have used a vector instead, but it's less elegant somehow.
How the std::vector<boost::variant> is less elegant?
1. It's could be implemented in cpp file. Your tuple based solution is in header right? 2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around
The only drawback is that you need to know set of types ahead of time. But this is minor IMO and it's quite easy to add an extra type whether a need arise.
Another drawback of std::vector<boost::variant> is speed, of course.
This is not clear cut. I do not see in theory why any boost::variant based algorithm couldn't be optimized to almost the same code (module type switching). On the other hand excessive usage of tuples will cause appropriate code bloat, eventually leading to code slow down. Gennadiy

"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
This is not clear cut. I do not see in theory why any boost::variant based algorithm couldn't be optimized to almost the same code (module type switching). On the other hand excessive usage of tuples will cause appropriate code bloat, eventually leading to code slow down.
Not clear cut at all. When operating on a sequence where all the types are different, the version with variant will be both slower and larger. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:87lkl93hlx.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
This is not clear cut. I do not see in theory why any boost::variant based algorithm couldn't be optimized to almost the same code (module type switching). On the other hand excessive usage of tuples will cause appropriate code bloat, eventually leading to code slow down.
Not clear cut at all. When operating on a sequence where all the types are different, the version with variant will be both slower and larger.
I love these performance "estimations" Do you have any numbers to sustain this?

"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:87lkl93hlx.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
This is not clear cut. I do not see in theory why any boost::variant based algorithm couldn't be optimized to almost the same code (module type switching). On the other hand excessive usage of tuples will cause appropriate code bloat, eventually leading to code slow down.
Not clear cut at all. When operating on a sequence where all the types are different, the version with variant will be both slower and larger.
I love these performance "estimations"
Do you have any numbers to sustain this?
No, sorry. It's simply obvious. Feel free to prove me wrong if it isn't obvious to you. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:87psal1e9x.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:87lkl93hlx.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
This is not clear cut. I do not see in theory why any boost::variant based algorithm couldn't be optimized to almost the same code (module type switching). On the other hand excessive usage of tuples will cause appropriate code bloat, eventually leading to code slow down.
Not clear cut at all. When operating on a sequence where all the types are different, the version with variant will be both slower and larger.
I love these performance "estimations"
Do you have any numbers to sustain this?
No, sorry. It's simply obvious.
Feel free to prove me wrong if it isn't obvious to you.
Nah. I don't really care. Just that after many years of optimizing performance critical code I gave up doing any "estimations". I was proven wrong too many times, so that even test program is not real prove. There are way too many factors that affect performance of real-life applications. Sometimes the same code compiled with different compilers of in different hardware will exhibit completely different performance pattern. We could talk about trends. And in this case I believe trends could be different depending on circumstances. Gennadiy

"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:87psal1e9x.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
Just that after many years of optimizing performance critical code I gave up doing any "estimations". I was proven wrong too many times, so that even test program is not real prove. There are way too many factors that affect performance of real-life applications. Sometimes the same code compiled with different compilers of in different hardware will exhibit completely different performance pattern. We could talk about trends. And in this case I believe trends could be different depending on circumstances.
That's true in most cases, but there's no need to muddy this case with uncertainties. If you think about it for just a few moments, you'll see there's absolutely no way the tuple version could be worse, and that it would take an optimizer of unheralded brilliance to make the variant version as good. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:87irgdxafy.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:87psal1e9x.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
Just that after many years of optimizing performance critical code I gave up doing any "estimations". I was proven wrong too many times, so that even test program is not real prove. There are way too many factors that affect performance of real-life applications. Sometimes the same code compiled with different compilers of in different hardware will exhibit completely different performance pattern. We could talk about trends. And in this case I believe trends could be different depending on circumstances.
That's true in most cases, but there's no need to muddy this case with uncertainties. If you think about it for just a few moments, you'll see there's absolutely no way the tuple version could be worse, and that it would take an optimizer of unheralded brilliance to make the variant version as good.
Let's just say that we'll have agree to disagree ;) Gennadiy

Gennadiy Rozental wrote:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:87irgdxafy.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes: Just that after many years of optimizing
"David Abrahams" <dave@boost-consulting.com> wrote in message news:87psal1e9x.fsf@pereiro.luannocracy.com... performance critical code I gave up doing any "estimations". I was proven wrong too many times, so that even test program is not real prove. There are way too many factors that affect performance of real-life applications. Sometimes the same code compiled with different compilers of in different hardware will exhibit completely different performance pattern. We could talk about trends. And in this case I believe trends could be different depending on circumstances. That's true in most cases, but there's no need to muddy this case with uncertainties. If you think about it for just a few moments, you'll see there's absolutely no way the tuple version could be worse, and that it would take an optimizer of unheralded brilliance to make the variant version as good.
Let's just say that we'll have agree to disagree ;)
Please see my other post. You must have missed this reply to your post when fusion was being reviewed. I've done lots of tests for variant. It's not as optimal as you think, alas :( Mind you, I hoped variant would be as fast as it can too because I rely on it in spirit2. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Gennadiy Rozental wrote:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:87lkl93hlx.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
This is not clear cut. I do not see in theory why any boost::variant based algorithm couldn't be optimized to almost the same code (module type switching). On the other hand excessive usage of tuples will cause appropriate code bloat, eventually leading to code slow down. Not clear cut at all. When operating on a sequence where all the types are different, the version with variant will be both slower and larger.
I love these performance "estimations"
Do you have any numbers to sustain this?
Yes. Dan posted some in my answer to your post in the fusion review sometime ago. You must've missed it. Here it is again (see attached). Here's the link to Dan's post: http://tinyurl.com/yawqs3 You must've missed it. We see speed differences as much as 20X. In addition to speed, notice the variant size too. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net /*============================================================================= Copyright (c) 2006 Eric Niebler Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ==============================================================================*/ #define FUSION_MAX_VECTOR_SIZE 25 #ifdef _MSC_VER // inline aggressively # pragma inline_recursion(on) // turn on inline recursion # pragma inline_depth(255) // max inline depth #endif #include <iostream> #include <algorithm> #include <vector> #include <boost/timer.hpp> #include <boost/fusion/sequence.hpp> #include <boost/fusion/algorithm.hpp> #include <boost/variant.hpp> namespace test { struct accumulator : boost::static_visitor<> { accumulator(int &i_) : i(i_) {} template<typename T> void operator()(T const &t) const { this->i += t; } int &i; }; struct variant_accumulator { variant_accumulator(int &i_) : i(i_) {} template <typename Variant> void operator()(Variant const& v) const { boost::apply_visitor( accumulator(i), v ); } int &i; }; int const REPEAT_COUNT = 10; template<typename T> double time_for_each_variant(T const &t, int &j) { boost::timer tim; int i = 0; long long iter = 65536; long long counter, repeats; double result = 0; double run; do { tim.restart(); for(counter = 0; counter < iter; ++counter) { i = 0; std::for_each(t.begin(), t.end(), variant_accumulator(i)); static_cast<void>(i); } result = tim.elapsed(); iter *= 2; } while(result < 0.5); iter /= 2; // repeat test and report least value for consistency: for(repeats = 0; repeats < REPEAT_COUNT; ++repeats) { tim.restart(); for(counter = 0; counter < iter; ++counter) { i = 0; std::for_each(t.begin(), t.end(), variant_accumulator(i)); j += i; } run = tim.elapsed(); result = (std::min)(run, result); } std::cout << i << std::endl; return result / iter; } template<typename T> double time_for_each_fusion(T const &t, int &j) { boost::timer tim; int i = 0; long long iter = 65536; long long counter, repeats; double result = 0; double run; do { tim.restart(); for(counter = 0; counter < iter; ++counter) { i = 0; boost::fusion::for_each(t, accumulator(i)); static_cast<void>(i); } result = tim.elapsed(); iter *= 2; } while(result < 0.5); iter /= 2; // repeat test and report least value for consistency: for(repeats = 0; repeats < REPEAT_COUNT; ++repeats) { tim.restart(); for(counter = 0; counter < iter; ++counter) { i = 0; boost::fusion::for_each(t, accumulator(i)); j += i; } run = tim.elapsed(); result = (std::min)(run, result); } std::cout << i << std::endl; return result / iter; } int test_short() { int j = 0; typedef int T0; typedef char T1; typedef short T2; typedef long T3; typedef boost::fusion::vector<T0,T1,T2,T3> fusion_vector_type; fusion_vector_type const l(3,42,6,29); double fusion_time = time_for_each_fusion( l, j ); typedef boost::variant<T0,T1,T2,T3> variant_type; std::vector<variant_type> v; v.push_back(3); v.push_back(42); v.push_back(6); v.push_back(29); double variant_time = time_for_each_variant( v, j ); std::cout << "Fusion vector size : " << sizeof(fusion_vector_type) << std::endl; std::cout << "Variant vector size (includes vector size + data) : " << (sizeof(variant_type)*4)+sizeof(v) << std::endl; std::cout << "Fusion vector time : " << fusion_time << std::endl; std::cout << "Variant time : " << variant_time << std::endl; return j; } int test_long() { int j = 0; typedef int T0; typedef char T1; typedef short T2; typedef long T3; boost::fusion::vector<T0,T1,T2,T3,T0,T1,T2,T3,T0,T1,T2,T3,T0,T1,T2,T3,T0,T1,T2,T3,T0,T1,T2,T3> const l( 3,42,6,29 ,3,42,6,29 ,3,42,6,29 ,3,42,6,29 ,3,42,6,29 ,3,42,6,29); double fusion_time = time_for_each_fusion( l, j ); typedef boost::variant<T0,T1,T2,T3> variant_type; std::vector<variant_type> v; for (int i = 0; i < 6; ++i) { v.push_back(3); v.push_back(42); v.push_back(6); v.push_back(29); } double variant_time = time_for_each_variant( v, j ); std::cout << "Fusion vector time : " << fusion_time << std::endl; std::cout << "Variant time : " << variant_time << std::endl; return j; } } int main() { std::cout << "Short test ... \n"; int i = test::test_short(); std::cout << "Long test ... \n"; int j = test::test_long(); return i + j; }

"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:elv64c$qao$1@sea.gmane.org...
Gennadiy Rozental wrote:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:87lkl93hlx.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
This is not clear cut. I do not see in theory why any boost::variant based algorithm couldn't be optimized to almost the same code (module type switching). On the other hand excessive usage of tuples will cause appropriate code bloat, eventually leading to code slow down. Not clear cut at all. When operating on a sequence where all the types are different, the version with variant will be both slower and larger.
I love these performance "estimations"
Do you have any numbers to sustain this?
Yes. Dan posted some in my answer to your post in the fusion review sometime ago. You must've missed it. Here it is again (see attached). Here's the link to Dan's post:
You must've missed it.
I did not have resources at the time to analize your test.
We see speed differences as much as 20X. In addition to speed, notice the variant size too.
It does indeed looks like the test in hand indicate that for the more or less trivial actors most (not all BTW) are able to optimize fusion version much better. Plus all the type switching and loop iteration is starting to play the role. But let's get to the problem at hand: output operation. I changed accumulator implementation in your test to still very trivial one like this: struct accumulator { accumulator( std::ostream& os ) : m_os( os ) {} template<typename T> void operator()(T const &t) const { this->m_os << t << ','; } std::ostream& m_os; }; (and the rest of the test appropriately) See attached files. When I used onullstream as ostream in test the fusion based version shows stable results of only 20% better performance advantage. onullstream does nothing whatsoever, just some virtual calls. When I used std::cerr instead, both fusion and boost::variant version show exactly the same performance (maybe 1-2% difference, but it depends on how long you run the test). boost::variant based version used offline implementation of test routine and test executable size doesn't grow that much when number of calls increases fusion based used inline implementation of test routine and test executable size grows faster. So my concluding is that I may use either version for the task at hand. Actual decision will depend on other factors but performance. If I keep the values in vectors of variant anyway I obviously prefer variant based version. If I need to create these collection on a fly I may opt to stick with the fusion. There maybe some other reasoning. Gennadiy P.S. I am using my own acc_timer in the attached files. You could use any one of your own. begin 666 b.cpp M(V1E9FEN92!&55-)3TY?34%87U9%0U1/4E]325I%(#$V"@HC:6YC;'5D92 \ M:6]S=')E86T^"B-I;F-L=61E(#QA;&=O<FET:&T^"B-I;F-L=61E(#QV96-T M;W(^"@HC:6YC;'5D92 \9FYD+V%C8U]T:6UE<BYH<' ^"@HC:6YC;'5D92 \ M8F]O<W0O9G5S:6]N+W-E<75E;F-E+FAP<#X*(VEN8VQU9&4@/&)O;W-T+V9U M<VEO;B]A;&=O<FET:&TN:'!P/@H*;F%M97-P86-E('1E<W0@>PH*='EP961E M9B!I;G0@("!4,#L*='EP961E9B!C:&%R("!4,3L*='EP961E9B!S:&]R="!4 M,CL*='EP961E9B!L;VYG("!4,SL*"FEN="!C," @/2 P.PII;G0@8S$@(#T@ M,3L*:6YT(&,R(" ](#(["FEN="!C,R @/2 S.PII;G0@8S0@(#T@-#L*:6YT M(&,U(" ](#4["FEN="!C-B @/2 V.PII;G0@8S<@(#T@-SL*:6YT(&,X(" ] M(#@["FEN="!C.2 @/2 Y.PII;G0@8S$P(#T@,3 ["FEN="!C,3$@/2 Q,3L* M:6YT(&,Q,B ](#$R.PII;G0@8S$S(#T@,3,["FEN="!C,30@/2 Q-#L*:6YT M(&,Q-2 ](#$U.PH*+R\M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TO+PH*<W1R=6-T(&%C8W5M=6QA=&]R('L*(" @ M(&%C8W5M=6QA=&]R*"!S=&0Z.F]S=')E86TF(&]S("D*(" @(#H@;5]O<R@@ M;W,@*2![?0H*(" @('1E;7!L871E/'1Y<&5N86UE(%0^"B @("!V;VED(&]P M97)A=&]R*"DH5"!C;VYS=" F="D@8V]N<W0*(" @('L*(" @(" @("!T:&ES M+3YM7V]S(#P\('0@/#P@)RPG.PH@(" @?0H*(" @('-T9#HZ;W-T<F5A;28@ M;5]O<SL*?3L*"B\O+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+R\*"G1E;7!L871E/'1Y<&5N86UE(%0^"FEN;&EN M92!V;VED"G1E<W1?<F]U=&EN92@@<W1D.CIO<W1R96%M)B!O<RP@5"!C;VYS M="8@=B I"GL*(" @(&)O;W-T.CIF=7-I;VXZ.F9O<E]E86-H*"!V+"!A8V-U M;75L871O<B@@;W,@*2 I.PI]"@HO+RTM+2TM+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2\O"@IT96UP;&%T93QT>7!E;F%M M92!4/@II;FQI;F4@9&]U8FQE"G1I;65?<F]U=&EN92@@5"!C;VYS="8@=B I M"GL*(" @(&9N9#HZ86-C7W1I;65R('1I;65R.PH*(" @(&EN="!C;VYS=" @ M(%)%4$5!5%].54T@/2 S.PH@(" @:6YT(" @(" @(" @<F5P96%T<SL*"B @ M("!L;VYG(&QO;F<@("!I=&5R(" @(" @(#T@-# Y-CL*(" @(&QO;F<@;&]N M9R @(&-O=6YT97(["@H@(" @9&\@>PH@(" @(" @(&ET97(@*CT@,CL*"B @ M(" @(" @=&EM97(N<W1A<G0H*3L*"B @(" @(" @9F]R*"!C;W5N=&5R(#T@ M,#L@8V]U;G1E<B \(&ET97([("LK8V]U;G1E<B I"B @(" @(" @(" @('1E M<W1?<F]U=&EN92@@<W1D.CIC97)R+"!V("D["@H@(" @(" @('1I;65R+G-T M;W H*3L*(" @('T@=VAI;&4H('1I;65R+F5L87!S961?;6MS*"D@/" U,# P M,# P("D["@H@(" @;&]N9R!L;VYG(" @(" @96QA<'-E9%]T:6UE(#T@-3 P M,# P,"HQ,#L*"B @(" O+R!R97!E870@=&5S="!A;F0@<F5P;W)T(&QE87-T M('9A;'5E(&9O<B!C;VYS:7-T96YC>3H*(" @(&9O<B@@<F5P96%T<R ](# [ M(')E<&5A=',@/"!215!%051?3E5-.R K*W)E<&5A=',@*2!["@H@(" @(" @ M('1I;65R+G-T87)T*"D["@H@(" @(" @(&9O<B@@8V]U;G1E<B ](# [(&-O M=6YT97(@/"!I=&5R.R K*V-O=6YT97(@*0H@(" @(" @(" @("!T97-T7W)O M=71I;F4H('-T9#HZ8V5R<BP@=B I.PH*(" @(" @("!T:6UE<BYS=&]P*"D[ M"@H@(" @(" @(&5L87!S961?=&EM92 ]("AS=&0Z.FUI;BDH(&5L87!S961? M=&EM92P@=&EM97(N96QA<'-E9%]M:W,H*2 I.PH@(" @?0H*(" @('-T9#HZ M8V]U=" \/" B3G5M8F5R(&]F(&ET97)A=&EO;CH@(B \/"!I=&5R(#P\('-T M9#HZ96YD;#L*"B @("!R971U<FX@*&1O=6)L92DH(&5L87!S961?=&EM92 J M(#$P,# @+R!I=&5R("D@+R Q,# P.PI]"@HO+RTM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2\O"@II;G0@9&]?=&5S M="@I"GL*(" @(&EN=" @(" @("!J(#T@,#L*(" @(&1O=6)L92 @("!T97-T M7W1I;64["@H@(" @>PH@(" @(" @('1Y<&5D968@8F]O<W0Z.F9U<VEO;CHZ M=F5C=&]R/%0P+%0Q+%0R+%0S/B!T97-T7W1Y<&4["B @(" @(" @=&5S=%]T M>7!E('8H(&,P+&,Q+&,R+&,S("D["@H@(" @(" @('1E<W1?=&EM92 ]('1I M;65?<F]U=&EN92@@=B I.PH*(" @(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@ M=F5C=&]R('-I>F4Z("(@/#P@<VEZ96]F*'1E<W1?='EP92D@/#P@<W1D.CIE M;F1L.PH@(" @(" @('-T9#HZ8V]U=" \/" B5&5S="!T:6UE.B B(#P\('1E M<W1?=&EM92 \/"!S=&0Z.F5N9&P["B @(" @(" @<W1D.CIC;W5T(#P\(")4 M97-T(')E<W5L=#H@(B \/"!J(#P\('-T9#HZ96YD;#L*(" @('T*(" @('L* M(" @(" @("!T>7!E9&5F(&)O;W-T.CIF=7-I;VXZ.G9E8W1O<CQ4,"Q4,2Q4 M,BQ4,RQ4,#X@=&5S=%]T>7!E.PH@(" @(" @('1E<W1?='EP92!V*"!C,"QC M,2QC,BQC,RQC-" I.PH*(" @(" @("!T97-T7W1I;64@/2!T:6UE7W)O=71I M;F4H('8@*3L*"B @(" @(" @<W1D.CIC;W5T(#P\(")497-T('9E8W1O<B!S M:7IE.B B(#P\('-I>F5O9BAT97-T7W1Y<&4I(#P\('-T9#HZ96YD;#L*(" @ M(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@=&EM93H@(B \/"!T97-T7W1I;64@ M/#P@<W1D.CIE;F1L.PH@(" @(" @('-T9#HZ8V]U=" \/" B5&5S="!R97-U M;'0Z("(@/#P@:B \/"!S=&0Z.F5N9&P["B @("!]"B @("!["B @(" @(" @ M='EP961E9B!B;V]S=#HZ9G5S:6]N.CIV96-T;W(\5# L5#$L5#(L5#,L5# L M5#$^('1E<W1?='EP93L*(" @(" @("!T97-T7W1Y<&4@=B@@8S L8S$L8S(L M8S,L8S0L8S4@*3L*"B @(" @(" @=&5S=%]T:6UE(#T@=&EM95]R;W5T:6YE M*"!V("D["@H@(" @(" @('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ M93H@(B \/"!S:7IE;V8H=&5S=%]T>7!E*2 \/"!S=&0Z.F5N9&P["B @(" @ M(" @<W1D.CIC;W5T(#P\(")497-T('1I;64Z("(@/#P@=&5S=%]T:6UE(#P\ M('-T9#HZ96YD;#L*(" @(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT M.B B(#P\(&H@/#P@<W1D.CIE;F1L.PH@(" @?0H@(" @>PH@(" @(" @('1Y M<&5D968@8F]O<W0Z.F9U<VEO;CHZ=F5C=&]R/%0P+%0Q+%0R+%0S+%0P+%0Q M+%0R/B!T97-T7W1Y<&4["B @(" @(" @=&5S=%]T>7!E('8H(&,P+&,Q+&,R M+&,S+&,T+&,U+&,V("D["@H@(" @(" @('1E<W1?=&EM92 ]('1I;65?<F]U M=&EN92@@=B I.PH*(" @(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@=F5C=&]R M('-I>F4Z("(@/#P@<VEZ96]F*'1E<W1?='EP92D@/#P@<W1D.CIE;F1L.PH@ M(" @(" @('-T9#HZ8V]U=" \/" B5&5S="!T:6UE.B B(#P\('1E<W1?=&EM M92 \/"!S=&0Z.F5N9&P["B @(" @(" @<W1D.CIC;W5T(#P\(")497-T(')E M<W5L=#H@(B \/"!J(#P\('-T9#HZ96YD;#L*(" @('T*(" @('L*(" @(" @ M("!T>7!E9&5F(&)O;W-T.CIF=7-I;VXZ.G9E8W1O<CQ4,"Q4,2Q4,BQ4,RQ4 M,"Q4,2Q4,BQ4,SX@=&5S=%]T>7!E.PH@(" @(" @('1E<W1?='EP92!V*"!C M,"QC,2QC,BQC,RQC-"QC-2QC-BQC-R I.PH*(" @(" @("!T97-T7W1I;64@ M/2!T:6UE7W)O=71I;F4H('8@*3L*"B @(" @(" @<W1D.CIC;W5T(#P\(")4 M97-T('9E8W1O<B!S:7IE.B B(#P\('-I>F5O9BAT97-T7W1Y<&4I(#P\('-T M9#HZ96YD;#L*(" @(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@=&EM93H@(B \ M/"!T97-T7W1I;64@/#P@<W1D.CIE;F1L.PH@(" @(" @('-T9#HZ8V]U=" \ M/" B5&5S="!R97-U;'0Z("(@/#P@:B \/"!S=&0Z.F5N9&P["B @("!]"B @ M("!["B @(" @(" @='EP961E9B!B;V]S=#HZ9G5S:6]N.CIV96-T;W(\5# L M5#$L5#(L5#,L5# L5#$L5#(L5#,L5# ^('1E<W1?='EP93L*(" @(" @("!T M97-T7W1Y<&4@=B@@8S L8S$L8S(L8S,L8S0L8S4L8S8L8S<L8S@@*3L*"B @ M(" @(" @=&5S=%]T:6UE(#T@=&EM95]R;W5T:6YE*"!V("D["@H@(" @(" @ M('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ93H@(B \/"!S:7IE;V8H M=&5S=%]T>7!E*2 \/"!S=&0Z.F5N9&P["B @(" @(" @<W1D.CIC;W5T(#P\ M(")497-T('1I;64Z("(@/#P@=&5S=%]T:6UE(#P\('-T9#HZ96YD;#L*(" @ M(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D M.CIE;F1L.PH@(" @?0H@(" @>PH@(" @(" @('1Y<&5D968@8F]O<W0Z.F9U M<VEO;CHZ=F5C=&]R/%0P+%0Q+%0R+%0S+%0P+%0Q+%0R+%0S+%0P+%0Q/B!T M97-T7W1Y<&4["B @(" @(" @=&5S=%]T>7!E('8H(&,P+&,Q+&,R+&,S+&,T M+&,U+&,V+&,W+&,X+&,Y("D["@H@(" @(" @('1E<W1?=&EM92 ]('1I;65? M<F]U=&EN92@@=B I.PH*(" @(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@=F5C M=&]R('-I>F4Z("(@/#P@<VEZ96]F*'1E<W1?='EP92D@/#P@<W1D.CIE;F1L M.PH@(" @(" @('-T9#HZ8V]U=" \/" B5&5S="!T:6UE.B B(#P\('1E<W1? M=&EM92 \/"!S=&0Z.F5N9&P["B @(" @(" @<W1D.CIC;W5T(#P\(")497-T M(')E<W5L=#H@(B \/"!J(#P\('-T9#HZ96YD;#L*(" @('T*(" @('L*(" @ M(" @("!T>7!E9&5F(&)O;W-T.CIF=7-I;VXZ.G9E8W1O<CQ4,"Q4,2Q4,BQ4 M,RQ4,"Q4,2Q4,BQ4,RQ4,"Q4,2Q4,CX@=&5S=%]T>7!E.PH@(" @(" @('1E M<W1?='EP92!V*"!C,"QC,2QC,BQC,RQC-"QC-2QC-BQC-RQC."QC.2QC,3 @ M*3L*"B @(" @(" @=&5S=%]T:6UE(#T@=&EM95]R;W5T:6YE*"!V("D["@H@ M(" @(" @('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ93H@(B \/"!S M:7IE;V8H=&5S=%]T>7!E*2 \/"!S=&0Z.F5N9&P["B @(" @(" @<W1D.CIC M;W5T(#P\(")497-T('1I;64Z("(@/#P@=&5S=%]T:6UE(#P\('-T9#HZ96YD M;#L*(" @(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@ M/#P@<W1D.CIE;F1L.PH@(" @?0H@(" @>PH@(" @(" @('1Y<&5D968@8F]O M<W0Z.F9U<VEO;CHZ=F5C=&]R/%0P+%0Q+%0R+%0S+%0P+%0Q+%0R+%0S+%0P M+%0Q+%0R+%0S/B!T97-T7W1Y<&4["B @(" @(" @=&5S=%]T>7!E('8H(&,P M+&,Q+&,R+&,S+&,T+&,U+&,V+&,W+&,X+&,Y+&,Q,"QC,3$@*3L*"B @(" @ M(" @=&5S=%]T:6UE(#T@=&EM95]R;W5T:6YE*"!V("D["@H@(" @(" @('-T M9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ93H@(B \/"!S:7IE;V8H=&5S M=%]T>7!E*2 \/"!S=&0Z.F5N9&P["B @(" @(" @<W1D.CIC;W5T(#P\(")4 M97-T('1I;64Z("(@/#P@=&5S=%]T:6UE(#P\('-T9#HZ96YD;#L*(" @(" @ M("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D.CIE M;F1L.PH@(" @?0H@(" @>PH@(" @(" @('1Y<&5D968@8F]O<W0Z.F9U<VEO M;CHZ=F5C=&]R/%0P+%0Q+%0R+%0S+%0P+%0Q+%0R+%0S+%0P+%0Q+%0R+%0S M+%0P/B!T97-T7W1Y<&4["B @(" @(" @=&5S=%]T>7!E('8H(&,P+&,Q+&,R M+&,S+&,T+&,U+&,V+&,W+&,X+&,Y+&,Q,"QC,3$L8S$R("D["@H@(" @(" @ M('1E<W1?=&EM92 ]('1I;65?<F]U=&EN92@@=B I.PH*(" @(" @("!S=&0Z M.F-O=70@/#P@(E1E<W0@=F5C=&]R('-I>F4Z("(@/#P@<VEZ96]F*'1E<W1? M='EP92D@/#P@<W1D.CIE;F1L.PH@(" @(" @('-T9#HZ8V]U=" \/" B5&5S M="!T:6UE.B B(#P\('1E<W1?=&EM92 \/"!S=&0Z.F5N9&P["B @(" @(" @ M<W1D.CIC;W5T(#P\(")497-T(')E<W5L=#H@(B \/"!J(#P\('-T9#HZ96YD M;#L*(" @('T*(" @('L*(" @(" @("!T>7!E9&5F(&)O;W-T.CIF=7-I;VXZ M.G9E8W1O<CQ4,"Q4,2Q4,BQ4,RQ4,"Q4,2Q4,BQ4,RQ4,"Q4,2Q4,BQ4,RQ4 M,"Q4,3X@=&5S=%]T>7!E.PH@(" @(" @('1E<W1?='EP92!V*"!C,"QC,2QC M,BQC,RQC-"QC-2QC-BQC-RQC."QC.2QC,3 L8S$Q+&,Q,BQC,3,@*3L*"B @ M(" @(" @=&5S=%]T:6UE(#T@=&EM95]R;W5T:6YE*"!V("D["@H@(" @(" @ M('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ93H@(B \/"!S:7IE;V8H M=&5S=%]T>7!E*2 \/"!S=&0Z.F5N9&P["B @(" @(" @<W1D.CIC;W5T(#P\ M(")497-T('1I;64Z("(@/#P@=&5S=%]T:6UE(#P\('-T9#HZ96YD;#L*(" @ M(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D M.CIE;F1L.PH@(" @?0H@(" @>PH@(" @(" @('1Y<&5D968@8F]O<W0Z.F9U M<VEO;CHZ=F5C=&]R/%0P+%0Q+%0R+%0S+%0P+%0Q+%0R+%0S+%0P+%0Q+%0R M+%0S+%0P+%0Q+%0R/B!T97-T7W1Y<&4["B @(" @(" @=&5S=%]T>7!E('8H M(&,P+&,Q+&,R+&,S+&,T+&,U+&,V+&,W+&,X+&,Y+&,Q,"QC,3$L8S$R+&,Q M,RQC,30@*3L*"B @(" @(" @=&5S=%]T:6UE(#T@=&EM95]R;W5T:6YE*"!V M("D["@H@(" @(" @('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ93H@ M(B \/"!S:7IE;V8H=&5S=%]T>7!E*2 \/"!S=&0Z.F5N9&P["B @(" @(" @ M<W1D.CIC;W5T(#P\(")497-T('1I;64Z("(@/#P@=&5S=%]T:6UE(#P\('-T M9#HZ96YD;#L*(" @(" @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B M(#P\(&H@/#P@<W1D.CIE;F1L.PH@(" @?0H@(" @>PH@(" @(" @('1Y<&5D M968@8F]O<W0Z.F9U<VEO;CHZ=F5C=&]R/%0P+%0Q+%0R+%0S+%0P+%0Q+%0R M+%0S+%0P+%0Q+%0R+%0S+%0P+%0Q+%0R+%0S/B!T97-T7W1Y<&4["B @(" @ M(" @=&5S=%]T>7!E('8H(&,P+&,Q+&,R+&,S+&,T+&,U+&,V+&,W+&,X+&,Y M+&,Q,"QC,3$L8S$R+&,Q,RQC,30L8S$U("D["@H@(" @(" @('1E<W1?=&EM M92 ]('1I;65?<F]U=&EN92@@=B I.PH*(" @(" @("!S=&0Z.F-O=70@/#P@ M(E1E<W0@=F5C=&]R('-I>F4Z("(@/#P@<VEZ96]F*'1E<W1?='EP92D@/#P@ M<W1D.CIE;F1L.PH@(" @(" @('-T9#HZ8V]U=" \/" B5&5S="!T:6UE.B B M(#P\('1E<W1?=&EM92 \/"!S=&0Z.F5N9&P["B @(" @(" @<W1D.CIC;W5T M(#P\(")497-T(')E<W5L=#H@(B \/"!J(#P\('-T9#HZ96YD;#L*(" @('T* M"B @("!R971U<FX@:CL*?0H*+R\M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TO+PH*?0H*:6YT"FUA:6XH*0I["B @ <("!R971U<FX@=&5S=#HZ9&]?=&5S="@I.PI]"@`` ` end begin 666 a.cpp M(VEN8VQU9&4@/&EO<W1R96%M/@HC:6YC;'5D92 \86QG;W)I=&AM/@HC:6YC M;'5D92 \=F5C=&]R/@H*(VEN8VQU9&4@/&9N9"]A8V-?=&EM97(N:'!P/@HC M:6YC;'5D92 \8F]O<W0O=F%R:6%N="YH<' ^"@IN86UE<W!A8V4@=&5S="![ M"@IT>7!E9&5F(&EN=" @(%0P.PIT>7!E9&5F(&-H87(@(%0Q.PIT>7!E9&5F M('-H;W)T(%0R.PIT>7!E9&5F(&QO;F<@(%0S.PH*='EP961E9B!B;V]S=#HZ M=F%R:6%N=#Q4,"Q4,2Q4,BQ4,SX@=F%R:6%N=%]T>7!E.PIT>7!E9&5F('-T M9#HZ=F5C=&]R/'9A<FEA;G1?='EP93X@("!T97-T7W1Y<&4["@HO+RTM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2\O M"@IS=')U8W0@86-C=6UU;&%T;W(@.B!B;V]S=#HZ<W1A=&EC7W9I<VET;W(\ M/B!["B @("!A8V-U;75L871O<B@@<W1D.CIO<W1R96%M)B!O<R I"B @(" Z M(&U?;W,H(&]S("D@>WT*"B @("!T96UP;&%T93QT>7!E;F%M92!4/@H@(" @ M=F]I9"!O<&5R871O<B@I*%0@8V]N<W0@)G0I(&-O;G-T"B @("!["B @(" @ M(" @=&AI<RT^;5]O<R \/"!T(#P\("<L)SL*(" @('T*"B @("!S=&0Z.F]S M=')E86TF(&U?;W,["GT["@HO+RTM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2\O"@IS=')U8W0@=F%R:6%N=%]A8V-U M;75L871O<B![(" @( H@(" @=F%R:6%N=%]A8V-U;75L871O<B@@<W1D.CIO M<W1R96%M)B!O<R I"B @(" Z(&U?;W,H(&]S("D@>WT*"B @("!V;VED(&]P M97)A=&]R*"DH('9A<FEA;G1?='EP92!C;VYS="8@=BD@8V]N<W0*(" @('L* M(" @(" @("!B;V]S=#HZ87!P;'E?=FES:71O<B@@86-C=6UU;&%T;W(H(&U? M;W,@*2P@=B I.PH@(" @?0H*(" @('-T9#HZ;W-T<F5A;28@;5]O<SL*?3L* M"B\O+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+R\*"G9O:60@=&5S=%]R;W5T:6YE*"!S=&0Z.F]S=')E86TF(&]S M+"!T97-T7W1Y<&4@8V]N<W0F('8@*3L*"B\O+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+R\*"FEN;&EN92!D;W5B M;&4*=&EM95]R;W5T:6YE*"!T97-T7W1Y<&4@8V]N<W0F('8@*0I["B @("!F M;F0Z.F%C8U]T:6UE<B!T:6UE<CL*"B @("!I;G0@8V]N<W0@("!215!%051? M3E5-(#T@,SL*(" @(&EN=" @(" @(" @(')E<&5A=',["@H@(" @;&]N9R!L M;VYG(" @:71E<B @(" @(" ](#0P.38["B @("!L;VYG(&QO;F<@("!C;W5N M=&5R.PH*(" @(&1O('L*(" @(" @("!I=&5R("H](#(["@H@(" @(" @('1I M;65R+G-T87)T*"D["@H@(" @(" @(&9O<B@@8V]U;G1E<B ](# [(&-O=6YT M97(@/"!I=&5R.R K*V-O=6YT97(@*0H@(" @(" @(" @("!T97-T7W)O=71I M;F4H('-T9#HZ8V5R<BP@=B I.PH*(" @(" @("!T:6UE<BYS=&]P*"D["B @ M("!]('=H:6QE*"!T:6UE<BYE;&%P<V5D7VUK<R@I(#P@-3 P,# P," I.PH* M(" @(&QO;F<@;&]N9R @(" @(&5L87!S961?=&EM92 ](#4P,# P,# J,3 [ M"@H@(" @+R\@<F5P96%T('1E<W0@86YD(')E<&]R="!L96%S="!V86QU92!F M;W(@8V]N<VES=&5N8WDZ"B @("!F;W(H(')E<&5A=',@/2 P.R!R97!E871S M(#P@4D5014%47TY533L@*RMR97!E871S("D@>PH*(" @(" @("!T:6UE<BYS M=&%R="@I.PH*(" @(" @("!F;W(H(&-O=6YT97(@/2 P.R!C;W5N=&5R(#P@ M:71E<CL@*RMC;W5N=&5R("D*(" @(" @(" @(" @=&5S=%]R;W5T:6YE*"!S M=&0Z.F-E<G(L('8@*3L*"B @(" @(" @=&EM97(N<W1O<"@I.PH*(" @(" @ M("!E;&%P<V5D7W1I;64@/2 H<W1D.CIM:6XI*"!E;&%P<V5D7W1I;64L('1I M;65R+F5L87!S961?;6MS*"D@*3L*(" @('T*"B @("!S=&0Z.F-O=70@/#P@ M(DYU;6)E<B!O9B!I=&5R871I;VXZ("(@/#P@:71E<B \/"!S=&0Z.F5N9&P[ M"@H@(" @<F5T=7)N("AD;W5B;&4I*"!E;&%P<V5D7W1I;64@*B Q,# P("\@ M:71E<B I("\@,3 P,#L*?0H*+R\M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TO+PH*:6YT(&1O7W1E<W0H*0I["B @ M("!I;G0@(" @(" @:B ](# ["B @("!I;G0@(" @(" @;B ](#0["B @("!T M97-T7W1Y<&4@=CL*(" @(&1O=6)L92 @("!T97-T7W1I;64["@H@(" @=BYP M=7-H7V)A8VLH*%0P*3 I.PH@(" @=BYP=7-H7V)A8VLH*%0Q*3$I.PH@(" @ M=BYP=7-H7V)A8VLH*%0R*3(I.PH@(" @=BYP=7-H7V)A8VLH*%0S*3,I.PH* M(" @('1E<W1?=&EM92 ]('1I;65?<F]U=&EN92@@=B I.PH*(" @('-T9#HZ M8V]U=" \/" B5&5S="!V96-T;W(@<VEZ92 H:6YC;'5D97,@=F5C=&]R('-I M>F4@*R!D871A*2 Z("(@"B @(" @(" @(" @(" @/#P@*'-I>F5O9BAV87)I M86YT7W1Y<&4I*FXI*W-I>F5O9BAT97-T7W1Y<&4I(" @/#P@<W1D.CIE;F1L M.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!T:6UE.B B(#P\('1E<W1?=&EM M92 \/"!S=&0Z.F5N9&P["B @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT M.B B(#P\(&H@/#P@<W1D.CIE;F1L.PH*(" @('8N<'5S:%]B86-K*&XK*RD[ M"@H@(" @+R\M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TO+PH*(" @('1E M<W1?=&EM92 ]('1I;65?<F]U=&EN92@@=B I.PH*(" @('-T9#HZ8V]U=" \ M/" B5&5S="!V96-T;W(@<VEZ92 H:6YC;'5D97,@=F5C=&]R('-I>F4@*R!D M871A*2 Z("(@"B @(" @(" @(" @(" @/#P@*'-I>F5O9BAV87)I86YT7W1Y M<&4I*FXI*W-I>F5O9BAT97-T7W1Y<&4I(" @/#P@<W1D.CIE;F1L.PH*(" @ M('-T9#HZ8V]U=" \/" B5&5S="!T:6UE.B B(#P\('1E<W1?=&EM92 \/"!S M=&0Z.F5N9&P["B @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\ M(&H@/#P@<W1D.CIE;F1L.PH*(" @('8N<'5S:%]B86-K*&XK*RD["@H@(" @ M+R\M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TO+PH*(" @('1E<W1?=&EM M92 ]('1I;65?<F]U=&EN92@@=B I.PH*(" @('-T9#HZ8V]U=" \/" B5&5S M="!V96-T;W(@<VEZ92 H:6YC;'5D97,@=F5C=&]R('-I>F4@*R!D871A*2 Z M("(@"B @(" @(" @(" @(" @/#P@*'-I>F5O9BAV87)I86YT7W1Y<&4I*FXI M*W-I>F5O9BAT97-T7W1Y<&4I(" @/#P@<W1D.CIE;F1L.PH*(" @('-T9#HZ M8V]U=" \/" B5&5S="!T:6UE.B B(#P\('1E<W1?=&EM92 \/"!S=&0Z.F5N M9&P["B @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@ M<W1D.CIE;F1L.PH*(" @('8N<'5S:%]B86-K*&XK*RD["@H@(" @+R\M+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TO+PH*(" @('1E<W1?=&EM92 ]('1I M;65?<F]U=&EN92@@=B I.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!V96-T M;W(@<VEZ92 H:6YC;'5D97,@=F5C=&]R('-I>F4@*R!D871A*2 Z("(@"B @ M(" @(" @(" @(" @/#P@*'-I>F5O9BAV87)I86YT7W1Y<&4I*FXI*W-I>F5O M9BAT97-T7W1Y<&4I(" @/#P@<W1D.CIE;F1L.PH*(" @('-T9#HZ8V]U=" \ M/" B5&5S="!T:6UE.B B(#P\('1E<W1?=&EM92 \/"!S=&0Z.F5N9&P["B @ M("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D.CIE M;F1L.PH*(" @('8N<'5S:%]B86-K*&XK*RD["@H@(" @+R\M+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TO+PH*(" @('1E<W1?=&EM92 ]('1I;65?<F]U M=&EN92@@=B I.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ M92 H:6YC;'5D97,@=F5C=&]R('-I>F4@*R!D871A*2 Z("(@"B @(" @(" @ M(" @(" @/#P@*'-I>F5O9BAV87)I86YT7W1Y<&4I*FXI*W-I>F5O9BAT97-T M7W1Y<&4I(" @/#P@<W1D.CIE;F1L.PH*(" @('-T9#HZ8V]U=" \/" B5&5S M="!T:6UE.B B(#P\('1E<W1?=&EM92 \/"!S=&0Z.F5N9&P["B @("!S=&0Z M.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D.CIE;F1L.PH* M(" @('8N<'5S:%]B86-K*&XK*RD["@H@(" @+R\M+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TO+PH*(" @('1E<W1?=&EM92 ]('1I;65?<F]U=&EN92@@ M=B I.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ92 H:6YC M;'5D97,@=F5C=&]R('-I>F4@*R!D871A*2 Z("(@"B @(" @(" @(" @(" @ M/#P@*'-I>F5O9BAV87)I86YT7W1Y<&4I*FXI*W-I>F5O9BAT97-T7W1Y<&4I M(" @/#P@<W1D.CIE;F1L.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!T:6UE M.B B(#P\('1E<W1?=&EM92 \/"!S=&0Z.F5N9&P["B @("!S=&0Z.F-O=70@ M/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D.CIE;F1L.PH*(" @('8N M<'5S:%]B86-K*&XK*RD["@H@(" @+R\M+2TM+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TO+PH*(" @('1E<W1?=&EM92 ]('1I;65?<F]U=&EN92@@=B I.PH* M(" @('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ92 H:6YC;'5D97,@ M=F5C=&]R('-I>F4@*R!D871A*2 Z("(@"B @(" @(" @(" @(" @/#P@*'-I M>F5O9BAV87)I86YT7W1Y<&4I*FXI*W-I>F5O9BAT97-T7W1Y<&4I(" @/#P@ M<W1D.CIE;F1L.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!T:6UE.B B(#P\ M('1E<W1?=&EM92 \/"!S=&0Z.F5N9&P["B @("!S=&0Z.F-O=70@/#P@(E1E M<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D.CIE;F1L.PH*(" @('8N<'5S:%]B M86-K*&XK*RD["@H@(" @+R\M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TO M+PH*(" @('1E<W1?=&EM92 ]('1I;65?<F]U=&EN92@@=B I.PH*(" @('-T M9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ92 H:6YC;'5D97,@=F5C=&]R M('-I>F4@*R!D871A*2 Z("(@"B @(" @(" @(" @(" @/#P@*'-I>F5O9BAV M87)I86YT7W1Y<&4I*FXI*W-I>F5O9BAT97-T7W1Y<&4I(" @/#P@<W1D.CIE M;F1L.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!T:6UE.B B(#P\('1E<W1? M=&EM92 \/"!S=&0Z.F5N9&P["B @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S M=6QT.B B(#P\(&H@/#P@<W1D.CIE;F1L.PH*(" @('8N<'5S:%]B86-K*&XK M*RD["@H@(" @+R\M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TO+PH*(" @ M('1E<W1?=&EM92 ]('1I;65?<F]U=&EN92@@=B I.PH*(" @('-T9#HZ8V]U M=" \/" B5&5S="!V96-T;W(@<VEZ92 H:6YC;'5D97,@=F5C=&]R('-I>F4@ M*R!D871A*2 Z("(@"B @(" @(" @(" @(" @/#P@*'-I>F5O9BAV87)I86YT M7W1Y<&4I*FXI*W-I>F5O9BAT97-T7W1Y<&4I(" @/#P@<W1D.CIE;F1L.PH* M(" @('-T9#HZ8V]U=" \/" B5&5S="!T:6UE.B B(#P\('1E<W1?=&EM92 \ M/"!S=&0Z.F5N9&P["B @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B M(#P\(&H@/#P@<W1D.CIE;F1L.PH*(" @('8N<'5S:%]B86-K*&XK*RD["@H@ M(" @+R\M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TO+PH*(" @('1E<W1? M=&EM92 ]('1I;65?<F]U=&EN92@@=B I.PH*(" @('-T9#HZ8V]U=" \/" B M5&5S="!V96-T;W(@<VEZ92 H:6YC;'5D97,@=F5C=&]R('-I>F4@*R!D871A M*2 Z("(@"B @(" @(" @(" @(" @/#P@*'-I>F5O9BAV87)I86YT7W1Y<&4I M*FXI*W-I>F5O9BAT97-T7W1Y<&4I(" @/#P@<W1D.CIE;F1L.PH*(" @('-T M9#HZ8V]U=" \/" B5&5S="!T:6UE.B B(#P\('1E<W1?=&EM92 \/"!S=&0Z M.F5N9&P["B @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@ M/#P@<W1D.CIE;F1L.PH*(" @('8N<'5S:%]B86-K*&XK*RD["@H@(" @+R\M M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TO+PH*(" @('1E<W1?=&EM92 ] M('1I;65?<F]U=&EN92@@=B I.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!V M96-T;W(@<VEZ92 H:6YC;'5D97,@=F5C=&]R('-I>F4@*R!D871A*2 Z("(@ M"B @(" @(" @(" @(" @/#P@*'-I>F5O9BAV87)I86YT7W1Y<&4I*FXI*W-I M>F5O9BAT97-T7W1Y<&4I(" @/#P@<W1D.CIE;F1L.PH*(" @('-T9#HZ8V]U M=" \/" B5&5S="!T:6UE.B B(#P\('1E<W1?=&EM92 \/"!S=&0Z.F5N9&P[ M"B @("!S=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D M.CIE;F1L.PH*(" @('8N<'5S:%]B86-K*&XK*RD["@H@(" @+R\M+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TO+PH*(" @('1E<W1?=&EM92 ]('1I;65? M<F]U=&EN92@@=B I.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@ M<VEZ92 H:6YC;'5D97,@=F5C=&]R('-I>F4@*R!D871A*2 Z("(@"B @(" @ M(" @(" @(" @/#P@*'-I>F5O9BAV87)I86YT7W1Y<&4I*FXI*W-I>F5O9BAT M97-T7W1Y<&4I(" @/#P@<W1D.CIE;F1L.PH*(" @('-T9#HZ8V]U=" \/" B M5&5S="!T:6UE.B B(#P\('1E<W1?=&EM92 \/"!S=&0Z.F5N9&P["B @("!S M=&0Z.F-O=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D.CIE;F1L M.PH*(" @('8N<'5S:%]B86-K*&XK*RD["@H@(" @+R\M+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TO+PH*(" @('1E<W1?=&EM92 ]('1I;65?<F]U=&EN M92@@=B I.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!V96-T;W(@<VEZ92 H M:6YC;'5D97,@=F5C=&]R('-I>F4@*R!D871A*2 Z("(@"B @(" @(" @(" @ M(" @/#P@*'-I>F5O9BAV87)I86YT7W1Y<&4I*FXI*W-I>F5O9BAT97-T7W1Y M<&4I(" @/#P@<W1D.CIE;F1L.PH*(" @('-T9#HZ8V]U=" \/" B5&5S="!T M:6UE.B B(#P\('1E<W1?=&EM92 \/"!S=&0Z.F5N9&P["B @("!S=&0Z.F-O M=70@/#P@(E1E<W0@<F5S=6QT.B B(#P\(&H@/#P@<W1D.CIE;F1L.PH*(" @ M('8N<'5S:%]B86-K*&XK*RD["@H@(" @+R\M+2TM+2TM+2TM+2TM+2TM+2TM M+2TM+2TM+2TO+PH*(" @(')E='5R;B!J.PI]"@HO+RTM+2TM+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2\O"@IV;VED"G1E M<W1?<F]U=&EN92@@<W1D.CIO<W1R96%M)B!O<RP@=&5S=%]T>7!E(&-O;G-T M)B!V("D*>PH@(" @<W1D.CIF;W)?96%C:"@@=BYB96=I;B@I+"!V+F5N9"@I M+"!V87)I86YT7V%C8W5M=6QA=&]R*"!O<R I("D["GT*"B\O+2TM+2TM+2TM M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+R\*"GT* L"FEN= IM86EN*"D*>PH@(" @<F5T=7)N('1E<W0Z.F1O7W1E<W0H*3L*?0H` ` end

Gennadiy Rozental wrote:
"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:elv64c$qao$1@sea.gmane.org...
Gennadiy Rozental wrote:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:87lkl93hlx.fsf@pereiro.luannocracy.com...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> writes:
This is not clear cut. I do not see in theory why any boost::variant based algorithm couldn't be optimized to almost the same code (module type switching). On the other hand excessive usage of tuples will cause appropriate code bloat, eventually leading to code slow down. Not clear cut at all. When operating on a sequence where all the types are different, the version with variant will be both slower and larger. I love these performance "estimations"
Do you have any numbers to sustain this? Yes. Dan posted some in my answer to your post in the fusion review sometime ago. You must've missed it. Here it is again (see attached). Here's the link to Dan's post:
You must've missed it.
I did not have resources at the time to analize your test.
We see speed differences as much as 20X. In addition to speed, notice the variant size too.
It does indeed looks like the test in hand indicate that for the more or less trivial actors most (not all BTW) are able to optimize fusion version much better. Plus all the type switching and loop iteration is starting to play the role.
But let's get to the problem at hand: output operation. I changed accumulator implementation in your test to still very trivial one like this:
struct accumulator { accumulator( std::ostream& os ) : m_os( os ) {}
template<typename T> void operator()(T const &t) const { this->m_os << t << ','; }
std::ostream& m_os; };
(and the rest of the test appropriately)
See attached files.
When I used onullstream as ostream in test the fusion based version shows stable results of only 20% better performance advantage. onullstream does nothing whatsoever, just some virtual calls. When I used std::cerr instead, both fusion and boost::variant version show exactly the same performance (maybe 1-2% difference, but it depends on how long you run the test). boost::variant based version used offline implementation of test routine and test executable size doesn't grow that much when number of calls increases fusion based used inline implementation of test routine and test executable size grows faster.
So my concluding is that I may use either version for the task at hand. Actual decision will depend on other factors but performance. If I keep the values in vectors of variant anyway I obviously prefer variant based version. If I need to create these collection on a fly I may opt to stick with the fusion. There maybe some other reasoning.
You should state clearly that in your tests, you are not testing the abstraction penalty of fusion/variant because the timings will heavily reflect the stream I/O, not the fusion/variant interfaces. IOTW, what you are actually testing is the speed of I/O operations (even if it is an empty virtual function-- in which case you are testing the virtual function call penalty). You obviously prefer variant, which is perfectly ok. As you say, actual decision will depend on other factors than performance. I actually agree with you on that. I myself use containers of variants too. In some uses, it is the perfect choice. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:em4is4$ph4$1@sea.gmane.org...
You should state clearly that in your tests, you are not testing the abstraction penalty of fusion/variant because the timings will heavily reflect the stream I/O, not the fusion/variant interfaces. IOTW, what you are actually testing is the speed of I/O operations (even if it is an empty virtual function-- in which case you are testing the virtual function call penalty).
One of my original points was that for the tasks relatd to the output operation variant based solution will be as fast. And my test did show that.
You obviously prefer variant, which is perfectly ok.
I am not sure where I gave this impression. Though I must admit that I personally don't have tasks at the moment where I would apply fusion given the choice. But this thread actually gave me some ideas where these facility should be considered.
As you say, actual decision will depend on other factors than performance. I actually agree with you on that. I myself use containers of variants too. In some uses, it is the perfect choice.
Great. Look like now we in agreement. ;) Genandiy

Gennadiy Rozental wrote:
One of my original points was that for the tasks relatd to the output operation variant based solution will be as fast. And my test did show that.
Fair enough.
You obviously prefer variant, which is perfectly ok.
I am not sure where I gave this impression.
Here: "So my concluding is ... If I keep the values in vectors of variant anyway I obviously prefer variant based version."...
Though I must admit that I personally don't have tasks at the moment where I would apply fusion given the choice. But this thread actually gave me some ideas where these facility should be considered.
As you say, actual decision will depend on other factors than performance. I actually agree with you on that. I myself use containers of variants too. In some uses, it is the perfect choice.
Great. Look like now we in agreement. ;)
Agreed :-) Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:emag2s$6tk$1@sea.gmane.org...
You obviously prefer variant, which is perfectly ok.
I am not sure where I gave this impression.
Here:
"So my concluding is ... If I keep the values in vectors of variant anyway I obviously prefer variant based version."...
What I meant is: Let's say you have a class Record { typedef boost::variant<...> field_value_t; std::vector<field_value_t> m_fields; }; than in output function implementation I won;t use fusion, I will use my existing vector of variants. But if I need to do the job on a fly, like here: int index; float balance; Data d; Time t; info merchant; output_csv( index, balance, merchant, d, t ); ... int index; float amount; Data d; Time t; location branch; output_csv( index, branch, amount, d, t ); I would build output_csv implementation based on fusion. Gennadiy

Gennadiy Rozental wrote:
"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:emag2s$6tk$1@sea.gmane.org...
You obviously prefer variant, which is perfectly ok. I am not sure where I gave this impression. Here:
"So my concluding is ... If I keep the values in vectors of variant anyway I obviously prefer variant based version."...
What I meant is:
[snip explanation] Understood. Thanks for the explanation. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Gennadiy Rozental wrote:
"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:elskhd$b32$2@sea.gmane.org...
Gennadiy Rozental wrote:
"John Maddock" <john@johnmaddock.co.uk> wrote in message news:006401c71f85$12d1a8b0$20570252@fuji...
Weapon Liu wrote:
I personally very like this fancy facility, and that's why I present these mumbles here to annoy you guys( if so, my apologies go here:-)) Any comments? I can give you one use I have for tuples:
I have a piece of boilerplate code that accepts a tuple (of any size) and prints out either a csv file or a boost::array C++ code conaining the data passed.
It allows me to output data for graphing, or matrixes of test data very quickly just by creating a short function that returns a tuple, and then passing that function to my boilerplate. If I want more columns of data I just increase the size of tuple by 1.
I suppose I could have used a vector instead, but it's less elegant somehow. How the std::vector<boost::variant> is less elegant?
1. It's could be implemented in cpp file. Your tuple based solution is in header right? 2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around
The only drawback is that you need to know set of types ahead of time. But this is minor IMO and it's quite easy to add an extra type whether a need arise. Another drawback of std::vector<boost::variant> is speed, of course.
This is not clear cut. I do not see in theory why any boost::variant based algorithm couldn't be optimized to almost the same code (module type switching). On the other hand excessive usage of tuples will cause appropriate code bloat, eventually leading to code slow down.
Test it; profile it. You'll see what I mean :) Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
I suppose I could have used a vector instead, but it's less elegant somehow.
How the std::vector<boost::variant> is less elegant?
1. It's could be implemented in cpp file. Your tuple based solution is in header right? 2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around
No std::vector does a deep copy (read memory allocation) with every copy. That means vector based code would be several orders of magnitude slower than passing small-ish tuples up and down the stack. John.

"John Maddock" <john@johnmaddock.co.uk> wrote in message news:00f001c72029$905cb050$a0331b56@fuji...
Joel de Guzman wrote:
You probably reply to ne.
I suppose I could have used a vector instead, but it's less elegant somehow.
How the std::vector<boost::variant> is less elegant?
1. It's could be implemented in cpp file. Your tuple based solution is in header right? 2. It allows dynamically sized entries, so you could skip some of the default values 3. It's as fast of could be even faster since we don't need to pass big structures around
No std::vector does a deep copy (read memory allocation) with every copy. That means vector based code would be several orders of magnitude slower than passing small-ish tuples up and down the stack.
I am sorry. I don't follow you. Could you write an example? Gennadiy

Weapon Liu wrote:
However, I found it bothering that the documents didn't even mention one real-world application( boost libs aside).
Like MPL, Fusion looks to me like it is something of a foundation library. You don't write applications in terms of Fusion; you write APIs and their implementations in terms of Fusion. It is good for writing libraries, especially libraries with complex compile-time or compile/runtime requirements like Boost.Parameter, Boost.Spirit, Andy's dimensional unit library, all that stuff that does loads of things at compile-time but also must tie in with the runtime side of things (which is where MPL lacks). Sebastian Redl

Weapon Liu <weapon.liu@gmail.com> writes:
I think this is the right place to ask this question. As cool as boost::fusion is, there's still one question that bothers me, that is, if boost::fusion is the hammer, then what's the nail?
I sure know that it's generic( more so than boost::tuple) and it's complete( with a bundle of algorithms and cool utilities), and it seems that it "solves" some problem quite well( when I read the documents).
However, I found it bothering that the documents didn't even mention one real-world application( boost libs aside).
Well, by nature the uses for static metaprogramming systems tend to be in writing libraries. Generally speaking, programs that process the types and values in a tuple (that's what Fusion is for), allow us to build interfaces that are user-configurable to work with any collection of heterogeneous types. One good example is http://boost.org/libs/iterator/zip_iterator.html. If you only ever had to zip together three specific sequence types (e.g. an array of longs, a vector of doubles, and array of strings), there might not be much point in using something like fusion. However, once you realize you also need to zip together the elements of three lists, or five sequences, you don't want to have to repeat yourself. You write a template to handle it, and now you have a library. Why you'd want a zip_iterator is covered somewhat at the link above, but a simple answer is to consider the std::transform algorithm, which has overload that take one or two input sequences. What if you want to transform three, four, or five input sequences? How many overloads do we need? You can compare a version of zip_iterator that uses fusion (zip_iterator_fusion) with one that just uses boost::tuple (HEAD) at http://tinyurl.com/y9ax7t I've also used tuples to do high-performance computations on groups of matrices that may have different shapes and layouts encoded in their types (think of just doing matrix addition for a simple example). If you don't want to rewrite the code every time you reconfigure the set of matrices you're working with, you need a heterogeneous collection that you can traverse programmatically. HTH, -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Weapon Liu <weapon.liu@gmail.com> writes:
I think this is the right place to ask this question. As cool as boost::fusion is, there's still one question that bothers me, that is, if boost::fusion is the hammer, then what's the nail?
I sure know that it's generic( more so than boost::tuple) and it's complete( with a bundle of algorithms and cool utilities), and it seems that it "solves" some problem quite well( when I read the documents).
However, I found it bothering that the documents didn't even mention one real-world application( boost libs aside).
Well, by nature the uses for static metaprogramming systems tend to be in writing libraries. Generally speaking, programs that process the types and values in a tuple (that's what Fusion is for), allow us to build interfaces that are user-configurable to work with any collection of heterogeneous types.
One good example is http://boost.org/libs/iterator/zip_iterator.html. If you only ever had to zip together three specific sequence types (e.g. an array of longs, a vector of doubles, and array of strings), there might not be much point in using something like fusion. However, once you realize you also need to zip together the elements of three lists, or five sequences, you don't want to have to repeat yourself. You write a template to handle it, and now you have a library.
Why you'd want a zip_iterator is covered somewhat at the link above, but a simple answer is to consider the std::transform algorithm, which has overload that take one or two input sequences. What if you want to transform three, four, or five input sequences? How many overloads do we need?
You can compare a version of zip_iterator that uses fusion (zip_iterator_fusion) with one that just uses boost::tuple (HEAD) at http://tinyurl.com/y9ax7t
I've also used tuples to do high-performance computations on groups of matrices that may have different shapes and layouts encoded in their types (think of just doing matrix addition for a simple example). If you don't want to rewrite the code every time you reconfigure the set of matrices you're working with, you need a heterogeneous collection that you can traverse programmatically.
Another potential use would be in generalizing GIL's color-spaces. If you have N such things and M functions for each, you'll have to implement NxM functions. Generic programming will narrow that to just M algorithms for all N color-spaces. If you have this kind of repetitious programming, fusion should be your friend. Inspection of GIL code reveals that, in many cases, you can even simply reuse the fusion algorithms, or use it as building blocks, much like STL algos. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Weapon Liu wrote:
I think this is the right place to ask this question. As cool as boost::fusion is, there's still one question that bothers me, that is, if boost::fusion is the hammer, then what's the nail?
I sure know that it's generic( more so than boost::tuple) and it's complete( with a bundle of algorithms and cool utilities), and it seems that it "solves" some problem quite well( when I read the documents).
However, I found it bothering that the documents didn't even mention one real-world application( boost libs aside).
Based on my experience, the most frequent scenarios where boost::tuple/fusion is useful are those where one needs a generated-on-the-fly struct for holding return values bundle, or where one needs to return multiple values transparently( well, nearly so), just like those lua does.
The fusion/tuple seems more generic? Functional languages have curried and uncurried functions. C++ is considered as a langauge that has "fused and unfused" functions? void foo(int, int); // unfused void foo(tuple<int,int>); // fused Note that functional languages regard unfused one as "tupled"; conFusing :-) Well, once an unfused function is converted to fused one, it is unary. Some kind of job will be easy; e.g. function composing. -- Shunsuke Sogame

Shunsuke Sogame wrote:
Weapon Liu wrote:
I think this is the right place to ask this question. As cool as boost::fusion is, there's still one question that bothers me, that is, if boost::fusion is the hammer, then what's the nail?
I sure know that it's generic( more so than boost::tuple) and it's complete( with a bundle of algorithms and cool utilities), and it seems that it "solves" some problem quite well( when I read the documents).
However, I found it bothering that the documents didn't even mention one real-world application( boost libs aside).
Based on my experience, the most frequent scenarios where boost::tuple/fusion is useful are those where one needs a generated-on-the-fly struct for holding return values bundle, or where one needs to return multiple values transparently( well, nearly so), just like those lua does.
The fusion/tuple seems more generic? Functional languages have curried and uncurried functions. C++ is considered as a langauge that has "fused and unfused" functions?
void foo(int, int); // unfused void foo(tuple<int,int>); // fused
Note that functional languages regard unfused one as "tupled"; conFusing :-)
Well, once an unfused function is converted to fused one, it is unary. Some kind of job will be easy; e.g. function composing.
Kinda con-fusing :-) But once you get the essence, it really makes sense :P Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
Shunsuke Sogame wrote:
Weapon Liu wrote:
I think this is the right place to ask this question. As cool as boost::fusion is, there's still one question that bothers me, that is, if boost::fusion is the hammer, then what's the nail?
I sure know that it's generic( more so than boost::tuple) and it's complete( with a bundle of algorithms and cool utilities), and it seems that it "solves" some problem quite well( when I read the documents).
However, I found it bothering that the documents didn't even mention one real-world application( boost libs aside).
Based on my experience, the most frequent scenarios where boost::tuple/fusion is useful are those where one needs a generated-on-the-fly struct for holding return values bundle, or where one needs to return multiple values transparently( well, nearly so), just like those lua does. The fusion/tuple seems more generic? Functional languages have curried and uncurried functions. C++ is considered as a langauge that has "fused and unfused" functions?
void foo(int, int); // unfused void foo(tuple<int,int>); // fused
Note that functional languages regard unfused one as "tupled"; conFusing :-)
Well, once an unfused function is converted to fused one, it is unary. Some kind of job will be easy; e.g. function composing.
Kinda con-fusing :-) But once you get the essence, it really makes sense :P
FYI, here is 'compose' implementation using con-fusing. It might be no practical use, though. :-) http://tinyurl.com/y9uamg Regards, -- Shunsuke Sogame

Shunsuke Sogame wrote:
Joel de Guzman wrote:
The fusion/tuple seems more generic? Functional languages have curried and uncurried functions. C++ is considered as a langauge that has "fused and unfused" functions?
void foo(int, int); // unfused void foo(tuple<int,int>); // fused
Note that functional languages regard unfused one as "tupled"; conFusing :-)
Well, once an unfused function is converted to fused one, it is unary. Some kind of job will be easy; e.g. function composing. Kinda con-fusing :-) But once you get the essence, it really makes sense :P
FYI, here is 'compose' implementation using con-fusing. It might be no practical use, though. :-) http://tinyurl.com/y9uamg
Yep, I know perfectly what you mean. I do that all the time (e.g Phoenix, which is actually one of the motivations behind Fusion). Pardon the con-fusing pun. It was all intentional :-) Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

On 12/14/2006 06:36 AM, Weapon Liu wrote:
And by the bye, that it can be used to build a handy pseudo-struct that enables full introspection really is a good and fancy feature. However, there's something fundamentally irremediable of it, that is, it can't be used to build a full-fledged class( rather than a POD-struct), which, I think, will too reduce its usefulness w.r.t. this kinda situations.
I think a "full fledged class" (a.k.a. FFC) can be done with mpl::fold and CRTP. Each method can be in a separate class, and each field may be in another class. By inheriting each of these separate classes (actually template classes where 1 template arg is the most derived class, thus making the pattern an instance of CRTP) with mpl's inherit linearly, you've inherited both the data members and the methods, and the CRTP allows any method to call any other method, whether that method is above or below it in the inheritance heirarchy. I tried this once with an earlier version of the proto_static_disp.cpp in boost value under Home/Strings - Text Processing, but I found it wasn't needed; so, I removed it. I could retrive the earlier version if you want to have a look.

Larry Evans wrote:
On 12/14/2006 06:36 AM, Weapon Liu wrote:
And by the bye, that it can be used to build a handy pseudo-struct that enables full introspection really is a good and fancy feature. However, there's something fundamentally irremediable of it, that is, it can't be used to build a full-fledged class( rather than a POD-struct), which, I think, will too reduce its usefulness w.r.t. this kinda situations.
I think a "full fledged class" (a.k.a. FFC) can be done with mpl::fold and CRTP. Each method can be in a separate class, and each field may be in another class. By inheriting each of these separate classes (actually template classes where 1 template arg is the most derived class, thus making the pattern an instance of CRTP) with mpl's inherit linearly, you've inherited both the data members and the methods, and the CRTP allows any method to call any other method, whether that method is above or below it in the inheritance heirarchy. I tried this once with an earlier version of the proto_static_disp.cpp in boost value under Home/Strings - Text Processing, but I found it wasn't needed; so, I removed it. I could retrive the earlier version if you want to have a look.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
I'm really intrigued, Larry :-) So, please. BTW. How do we handle the accessibility?

On 12/15/2006 02:33 AM, Weapon Liu wrote:
Larry Evans wrote: [snip]
I think a "full fledged class" (a.k.a. FFC) can be done with mpl::fold and CRTP. Each method can be in a separate class, and each field may be [snip] I'm really intrigued, Larry :-) So, please.
See boost vault under Template Metaprogramming in tuple_methods_proto.cpp.
BTW. How do we handle the accessibility?
I don't. Everything is public. Is that what you mean?

Larry Evans wrote:
On 12/15/2006 02:33 AM, Weapon Liu wrote:
Larry Evans wrote:
[snip]
I think a "full fledged class" (a.k.a. FFC) can be done with mpl::fold and CRTP. Each method can be in a separate class, and each field may be
[snip]
I'm really intrigued, Larry :-) So, please.
See boost vault under Template Metaprogramming in tuple_methods_proto.cpp.
BTW. How do we handle the accessibility?
I don't. Everything is public. Is that what you mean?
Sorry for not being clear :) I meant, if we're gonna build a full-fledged class using this kinda tricks, then how do we handle the accessibility of the member functions/data. For example, how do we make a member private? Anyway, I'm gonna take a look at the vault, thanks for the heads up, Larry! :)

Weapon Liu wrote:
I think this is the right place to ask this question. As cool as boost::fusion is, there's still one question that bothers me, that is, if boost::fusion is the hammer, then what's the nail?
I sure know that it's generic( more so than boost::tuple) and it's complete( with a bundle of algorithms and cool utilities), and it seems that it "solves" some problem quite well( when I read the documents).
However, I found it bothering that the documents didn't even mention one real-world application( boost libs aside).
Yep. It's a work in progress. Lots of developments happened since then and we have more examples now than ever before. We intend to have more examples available in the docs. Yet, as many folks have mentioned, fusion, like MPL, is typically a tool to make tools. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net
participants (12)
-
David Abrahams
-
Felipe Magno de Almeida
-
Gennadiy Rozental
-
Joel de Guzman
-
John Maddock
-
Larry Evans
-
Nicola Musatti
-
Sebastian Redl
-
Shunsuke Sogame
-
Thorsten Ottosen
-
Tobias Schwinger
-
Weapon Liu