Topic: Why no simple for_each?
Author: thorsten.ottosen@dezide.com (Thorsten Ottosen)
Date: Tue, 12 Dec 2006 17:54:17 GMT Raw View
kwikius wrote:
> Thorsten Ottosen wrote:
>
>>kwikius wrote:
>>
>>>Having read the in language "for (each..) proposals I am bemused as to
>>>why there are no std for_each overloads as below.
>>
>>This has been proposed as part of the Range lib for TR2.
>
>
> Is the syntax as simple as for_each(c,f) ?
>
well, yes, every time an algothm takes two iterators, it now takes one
Range.
>
>>>The array version is
>>>easily inlined and IMO will have a superior performance to one with
>>>iterators,
>>
>>
>>Why? Do you have performance data to back that up?
>
>
> I havent spent a lot of time on it, but in the following on VC8 the
> souped up array version appears to be around 10% faster (2800 ms to
> 3070 ms or so for the code at the end---->)
> compiler VC8.0, Express. These were the compile flags flags: /Ox /Ob2
> /Oi /Ot /Oy /GT /GL /FD /EHsc /MT /Za /FAs /Fa"Release\\"
> /Fo"Release\\" /Fd"Release\vc80.pdb" /nologo /c /TP /errorReport:prompt
> System Windows XP AMD Athlon 1.25 GHz
well, if you're unrolling the array version, then why not unroll the
iterator version?
> That isnt really the point I was making though. The point is that the
> syntax is a lot more concise than the iterator syntax, and in most of
> my code I use it for iterating the whole container.
Right, exactly.
See
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2006/n2068.html
and
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1871.html
and
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1871.html#overloaded-algorithms
Whether the committee will be willing to work on this, I don't really
know. I guess if concepts pass through core, the whole lot should be
upgraded to use concepts and be part of C++0x, and not a TR.
-Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "kwikius" <andy@servocomm.freeserve.co.uk>
Date: Wed, 13 Dec 2006 09:52:40 CST Raw View
Thorsten Ottosen wrote:
> well, if you're unrolling the array version, then why not unroll the
> iterator version?
Its nowhere near as simple with the variable size container as it is
when the size is a compile time integral constant as with the array.
<...>
> See
>
> http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2006/n2068.html
>
> and
>
> http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1871.html
>
> and
>
> http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1871.html#overloaded-algorithms
OK this seems like a neat idea.
In the case that the range is that of the whole container though, is a
range not conceptually the same as the container? IOW in the case where
the range includes all the elements in the container, why not also
allow a container to be passed to the algorithms as a a valid range?
regards
Andy Little
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: tasjaevan@gmail.com
Date: Wed, 13 Dec 2006 11:59:47 CST Raw View
On Dec 13, 3:52 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
>
> In the case that the range is that of the whole container though, is a
> range not conceptually the same as the container? IOW in the case where
> the range includes all the elements in the container, why not also
> allow a container to be passed to the algorithms as a a valid range?
>
You can. Any object with suitable begin and end member functions
qualifies as a range.
However, if my thinking is correct, mutating algorithms will have to
create std::ranges internally to cater for all of the following ranges
of non-const elements:
a) const ranges (e.g. const range<vector<int>::iterator>>, NB *not*
const_iterator)
b) non-const rvalue ranges
c) containers
The usual const T& and T&& pair of overloads would take care of a) and
b), but an (lvalue) container (case c)) would bind to the const T&
overload, which would give the algorithm only const access to the
elements.
The following boilerplate works for all three cases:
template <typename Range>
void mutator(const Range& r)
{
// This is called for rvalue and/or const ranges (cases a) and b))
std::range nonconst_lvalue_range(r);
mutator(nonconst_lvalue_range);
}
template <typename Range>
void mutator(Range& r)
{
// This is called directly for non-const lvalue ranges, including
containers
// This is where the algorithm's code lives
std::begin(r) // ... etc ...
}
(enable_if could be used to ensure the elements are non-const)
James
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Thorsten Ottosen <thorsten.ottosen@dezide.com>
Date: Thu, 14 Dec 2006 10:48:56 CST Raw View
kwikius wrote:
> Thorsten Ottosen wrote:
>
>
>>well, if you're unrolling the array version, then why not unroll the
>>iterator version?
>
>
> Its nowhere near as simple with the variable size container as it is
> when the size is a compile time integral constant as with the array.
>
If the passed range is an array, the begin and end iterators will be
pointers that is known at compilte-time.
-Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Thorsten Ottosen <thorsten.ottosen@dezide.com>
Date: Thu, 14 Dec 2006 10:57:18 CST Raw View
tasjaevan@gmail.com wrote:
>
> On Dec 13, 3:52 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
>
>>In the case that the range is that of the whole container though, is a
>>range not conceptually the same as the container? IOW in the case where
>>the range includes all the elements in the container, why not also
>>allow a container to be passed to the algorithms as a a valid range?
>>
>
>
> You can. Any object with suitable begin and end member functions
> qualifies as a range.
Exactly.
> However, if my thinking is correct, mutating algorithms will have to
> create std::ranges internally to cater for all of the following ranges
> of non-const elements:
>
> a) const ranges (e.g. const range<vector<int>::iterator>>, NB *not*
> const_iterator)
> b) non-const rvalue ranges
> c) containers
>
> The usual const T& and T&& pair of overloads would take care of a) and
> b), but an (lvalue) container (case c)) would bind to the const T&
> overload, which would give the algorithm only const access to the
> elements.
I'm not sure you need to make it that complicated. For example,
template< class RandomAccessRange >
void sort( RandomAccessRange&& r );
should be enough to support a), b) and c) AFAICT.
-Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "kwikius" <andy@servocomm.freeserve.co.uk>
Date: Thu, 14 Dec 2006 14:26:56 CST Raw View
Thorsten Ottosen wrote:
> tasjaevan@gmail.com wrote:
> >
> > On Dec 13, 3:52 pm, "kwikius" <a...@servocomm.freeserve.co.uk> wrote:
> >
> >>In the case that the range is that of the whole container though, is a
> >>range not conceptually the same as the container? IOW in the case where
> >>the range includes all the elements in the container, why not also
> >>allow a container to be passed to the algorithms as a a valid range?
> >>
> >
> >
> > You can. Any object with suitable begin and end member functions
> > qualifies as a range.
>
> Exactly.
OK. Thats cleared that up. So range is a Concept and a container with
begin, end iterators is a model. I didnt see that in the proposals, so
I thought there must be some reason that it didnt apply. That seems to
be the major selling point of Range in that case,one less hurdle for
beginning C++ programmers when using containers.
regards
Andy Little
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Thorsten Ottosen <thorsten.ottosen@dezide.com>
Date: Fri, 15 Dec 2006 09:45:27 CST Raw View
kwikius wrote:
> Thorsten Ottosen wrote:
>
>>tasjaevan@gmail.com wrote:
>>>You can. Any object with suitable begin and end member functions
>>>qualifies as a range.
>>
>>Exactly.
>
>
> OK. Thats cleared that up. So range is a Concept and a container with
> begin, end iterators is a model. I didnt see that in the proposals,
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2006/n2068.html#range-primitives
Also, maybe the boost docs wuold help:
http://www.boost.org/libs/range/doc/range.html
-Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: tasjaevan@gmail.com
Date: Fri, 15 Dec 2006 09:45:45 CST Raw View
On Dec 14, 4:57 pm, Thorsten Ottosen <thorsten.otto...@dezide.com>
wrote:
>
> I'm not sure you need to make it that complicated. For example,
>
> template< class RandomAccessRange >
> void sort( RandomAccessRange&& r );
>
> should be enough to support a), b) and c) AFAICT.
>
You're right. Sorry for the confusion.
A slight subtlety is that we'll have to 'call' remove_reference on
RandomAccessRange before passing it to a trait metafunction.
James
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: thorsten.ottosen@dezide.com (Thorsten Ottosen)
Date: Fri, 15 Dec 2006 20:41:00 GMT Raw View
tasjaevan@gmail.com wrote:
> On Dec 14, 4:57 pm, Thorsten Ottosen <thorsten.otto...@dezide.com>
> wrote:
> A slight subtlety is that we'll have to 'call' remove_reference on
> RandomAccessRange before passing it to a trait metafunction.
That's not a slight subtlety; it a very good observation, since T now
can be deduced as U& for some type U.
I think we only need to change the sentence
"Let ptr be a variable of type Range*, then the nested type is
typedef decltype( begin(*ptr) ) type;"
to
"Let ptr be a variable of type typename remove_reference<Range>::type*,
then the nested type is
typedef decltype( begin(*ptr) ) type;"
I wonder how the other tr1 type-traits handle this situation.
-Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Beman Dawes <bdawes@acm.org>
Date: Mon, 18 Dec 2006 00:16:05 CST Raw View
Thorsten Ottosen wrote:
> Whether the committee will be willing to work on this, I don't really
> know. I guess if concepts pass through core, the whole lot should be
> upgraded to use concepts and be part of C++0x, and not a TR.
Although "it ain t over till it s over", Concepts were voted into the
Registration Document in Portland. That means that unless something
unforeseen happens, Concepts with be in C++0x.
Doug Gregor's Concept GCC implementation, and the reimplementation of
the standard library using Concepts by the Indiana folks, was convincing.
--Beman
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: tasjaevan@gmail.com
Date: Mon, 18 Dec 2006 10:39:12 CST Raw View
On Dec 15, 8:41 pm, thorsten.otto...@dezide.com (Thorsten Ottosen)
wrote:
>
> I think we only need to change the sentence
>
> "Let ptr be a variable of type Range*, then the nested type is
> typedef decltype( begin(*ptr) ) type;"
>
> to
>
> "Let ptr be a variable of type typename remove_reference<Range>::type*,
> then the nested type is
> typedef decltype( begin(*ptr) ) type;"
>
An algorithm deducing its template parameter through T&& may also get
cv-qualifiers in the type. I think a common example will be along the
lines of:
void f(const std::pair<Iter, Iter>& p) { std::sort(p); }
(p is a const lvalue, but represents a range of mutable elements)
The way I read it, the wording of Range Traits seems fine. As with
iterator_traits today, any range type must be stripped of all
cv-qualifiers and reference/pointer modifiers before being sent to a
range trait.
The alternative would be to widen the range concept to explicitly
encompass cv-qualified types and references. Perhaps not a completely
mad idea, with definite usability benefits, but quite a big change, I
think.
James
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: bdawes@acm.org (Beman Dawes)
Date: Tue, 19 Dec 2006 00:33:47 GMT Raw View
Thorsten Ottosen wrote:
> Whether the committee will be willing to work on this, I don't really=20
> know. I guess if concepts pass through core, the whole lot should be=20
> upgraded to use concepts and be part of C++0x, and not a TR.
Although "it ain=92t over till it=92s over", Concepts were voted into the
Registration Document in Portland. That means that unless something
unforeseen happens, Concepts with be in C++0x.
Doug Gregor's Concept GCC implementation, and the reimplementation of
the standard library using Concepts by the Indiana folks, was convincing.
--Beman
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "kwikius" <andy@servocomm.freeserve.co.uk>
Date: Sun, 10 Dec 2006 13:16:17 CST Raw View
Having read the in language "for (each..) proposals I am bemused as to
why there are no std for_each overloads as below. The array version is
easily inlined and IMO will have a superior performance to one with
iterators, while the container version merely wraps the current
std::foreach. The use of functors is very easy to read, probably easier
than following with an expression, and the functors can be made generic
for common tasks. I have excluded such things as result_of
compatibility in the functors to make the code slightly smaller.
regards
Andy Little
-----------
/*
simple for_each for containers and arrays
*/
// std container version of for_each
template <typename Container, typename F>
void for_each(Container & c, F const & f)
{
std::for_each(c.begin(),c.end(),f);
}
//array version of for_each
template <typename T,int N,typename F>
void for_each(T (&ar)[N],F const & f)
{
for (int i = 0; i < N;++i){
f(ar[i]);
}
}
// sample expressions... full source provided at end
typedef double value_type;
//replace with your favourite vect
using quan::two_d::vect;
vect<value_type> box[] = {
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
};
//load a std::vector from array
std::vector<vect<value_type> > pt_vector;
for_each(box,push_back(pt_vector));
double angle = pi/2;
//rotate for both versions
for_each(box,rotate(angle));
for_each(pt_vector,rotate(angle));
// just to get cleaner output format
std::cout.setf(std::ios_base::fixed,std::ios_base::floatfield);
std::cout.precision(2);
// do output
for_each(box,output(std::cout,'\n'));
for_each(pt_vector,output(std::cout,'\n'));
//-------Full source example with example functors
//------------------------------
// replace with your favourite 2D vector class...
#include <quan/two_d/out/vect.hpp>
//---------------------------------
#include <algorithm>
#include <vector>
/*
simple for_each for containers and arrays
*/
// std container version of for_each
template <typename Container, typename F>
void for_each(Container & c, F const & f)
{
std::for_each(c.begin(),c.end(),f);
}
//array version of for_each
template <typename T,int N,typename F>
void for_each(T (&ar)[N],F const & f)
{
for (int i = 0; i < N;++i){
f(ar[i]);
}
}
// functor to rotate a vector
// number of vertices
template <typename Angle>
struct rotate_{
rotate_(Angle const & angle)
: cos_theta (cos(angle)),
sin_theta(sin(angle)){}
double cos_theta, sin_theta;
template <typename Vect>
Vect&
operator()(Vect & in)const
{
in = in * cos_theta + perp_vector(in) * sin_theta;
return in;
}
};
// function for above so for_each doesnt need
// explicit template param on struct
template <typename Angle>
inline
rotate_<Angle>
rotate(Angle const & in)
{
return rotate_<Angle>(in);
}
// do output
template <typename CharType>
struct output_{
std::basic_ostream<CharType> & os;
CharType sep;
output_(
std::basic_ostream<CharType> & os_in,
CharType sep_in):os(os_in),sep(sep_in){}
template <typename T>
std::basic_ostream<CharType> &
operator()(T const & t)const
{
return os << t << sep;
}
};
// function returns functor above
template <typename CharType>
inline
output_<CharType>
output(std::basic_ostream<CharType>& os, CharType sep)
{
return output_<CharType>(os,sep);
}
// use with foreach to load a std container
// from another constainer or an array
template <typename Container>
struct push_back_{
Container & c;
push_back_(Container & c_in):c(c_in){}
template <typename T>
void
operator()(T const & t)const
{
return c.push_back(t);
}
};
// function returns functor above
template <typename Container>
inline
push_back_<Container> push_back(Container & c)
{
return push_back_<Container>(c);
}
double const pi = 3.14159265358979323846;
int main()
{
typedef double value_type;
using quan::two_d::vect;
vect<value_type> box[] = {
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1)
};
//copy points to a std::vector
std::vector<vect<value_type> > pt_vector;
for_each(box,push_back(pt_vector));
double angle = pi/2;
//test code for both versions
for_each(box,rotate(angle));
for_each(pt_vector,rotate(angle));
//get cleaner output format
std::cout.setf(std::ios_base::fixed,std::ios_base::floatfield);
std::cout.precision(2);
for_each(box,output(std::cout,'\n'));
for_each(pt_vector,output(std::cout,'\n'));
return 0;
}
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Mathias Gaunard <loufoque@remove.gmail.com>
Date: Mon, 11 Dec 2006 10:07:15 CST Raw View
kwikius wrote:
> The array version is
> easily inlined and IMO will have a superior performance to one with
> iterators
Actually some compilers are known to generate the same assembler going
through iterators of std::vector and with a loop like yours.
> //array version of for_each
> template <typename T,int N,typename F>
> void for_each(T (&ar)[N],F const & f)
> {
> for (int i = 0; i < N;++i){
> f(ar[i]);
> }
> }
It should be size_t, not int.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "kwikius" <andy@servocomm.freeserve.co.uk>
Date: Mon, 11 Dec 2006 11:30:35 CST Raw View
Mathias Gaunard wrote:
> kwikius wrote:
> > The array version is
> > easily inlined and IMO will have a superior performance to one with
> > iterators
>
> Actually some compilers are known to generate the same assembler going
> through iterators of std::vector and with a loop like yours.
There is one more step with std::vector, which is checking that N is
constant (as it can change during the vectors life), whereas with the
array this is a guaranteed compile time constant, therefore allowing
less capable and cheaper optimisers to generate good code.
> > //array version of for_each
> > template <typename T,int N,typename F>
> > void for_each(T (&ar)[N],F const & f)
> > {
> > for (int i = 0; i < N;++i){
> > f(ar[i]);
> > }
> > }
>
> It should be size_t, not int.
OK. (With the minor nitpick changed ) Is this signature not preferable
as an alternative to the current approach to for_each for looping
through the complete container?
regards
Andy Little
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: thorsten.ottosen@dezide.com (Thorsten Ottosen)
Date: Mon, 11 Dec 2006 17:42:30 GMT Raw View
kwikius wrote:
> Having read the in language "for (each..) proposals I am bemused as to
> why there are no std for_each overloads as below.
This has been proposed as part of the Range lib for TR2.
> The array version is
> easily inlined and IMO will have a superior performance to one with
> iterators,
Why? Do you have performance data to back that up?
-Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Signal9" <cep_81@msn.com>
Date: Mon, 11 Dec 2006 11:45:02 CST Raw View
kwikius wrote:
> Having read the in language "for (each..) proposals I am bemused as to
> why there are no std for_each overloads as below. The array version is
> easily inlined and IMO will have a superior performance to one with
> iterators, while the container version merely wraps the current
> std::foreach. The use of functors is very easy to read, probably easier
> than following with an expression, and the functors can be made generic
> for common tasks. I have excluded such things as result_of
> compatibility in the functors to make the code slightly smaller.
>
> regards
> Andy Little
>
> -----------
>
>
> /*
> simple for_each for containers and arrays
> */
>
> // std container version of for_each
> template <typename Container, typename F>
> void for_each(Container & c, F const & f)
> {
> std::for_each(c.begin(),c.end(),f);
> }
>
> //array version of for_each
> template <typename T,int N,typename F>
> void for_each(T (&ar)[N],F const & f)
> {
> for (int i = 0; i < N;++i){
> f(ar[i]);
> }
> }
> // sample expressions... full source provided at end
>
> typedef double value_type;
> //replace with your favourite vect
> using quan::two_d::vect;
>
> vect<value_type> box[] = {
> vect<value_type>(0,0),
> vect<value_type>(1,0),
> vect<value_type>(1,1),
> vect<value_type>(0,1),
> };
>
> //load a std::vector from array
> std::vector<vect<value_type> > pt_vector;
> for_each(box,push_back(pt_vector));
>
> double angle = pi/2;
>
> //rotate for both versions
> for_each(box,rotate(angle));
> for_each(pt_vector,rotate(angle));
>
> // just to get cleaner output format
> std::cout.setf(std::ios_base::fixed,std::ios_base::floatfield);
> std::cout.precision(2);
>
> // do output
> for_each(box,output(std::cout,'\n'));
> for_each(pt_vector,output(std::cout,'\n'));
>
> //-------Full source example with example functors
>
> //------------------------------
> // replace with your favourite 2D vector class...
> #include <quan/two_d/out/vect.hpp>
> //---------------------------------
> #include <algorithm>
> #include <vector>
> /*
> simple for_each for containers and arrays
> */
> // std container version of for_each
> template <typename Container, typename F>
> void for_each(Container & c, F const & f)
> {
> std::for_each(c.begin(),c.end(),f);
> }
> //array version of for_each
> template <typename T,int N,typename F>
> void for_each(T (&ar)[N],F const & f)
> {
> for (int i = 0; i < N;++i){
> f(ar[i]);
> }
> }
>
> // functor to rotate a vector
> // number of vertices
> template <typename Angle>
> struct rotate_{
>
> rotate_(Angle const & angle)
> : cos_theta (cos(angle)),
> sin_theta(sin(angle)){}
> double cos_theta, sin_theta;
>
> template <typename Vect>
> Vect&
> operator()(Vect & in)const
> {
> in = in * cos_theta + perp_vector(in) * sin_theta;
> return in;
> }
> };
> // function for above so for_each doesnt need
> // explicit template param on struct
> template <typename Angle>
> inline
> rotate_<Angle>
> rotate(Angle const & in)
> {
> return rotate_<Angle>(in);
> }
>
> // do output
> template <typename CharType>
> struct output_{
> std::basic_ostream<CharType> & os;
> CharType sep;
> output_(
> std::basic_ostream<CharType> & os_in,
> CharType sep_in):os(os_in),sep(sep_in){}
>
> template <typename T>
> std::basic_ostream<CharType> &
> operator()(T const & t)const
> {
> return os << t << sep;
> }
> };
> // function returns functor above
> template <typename CharType>
> inline
> output_<CharType>
> output(std::basic_ostream<CharType>& os, CharType sep)
> {
> return output_<CharType>(os,sep);
> }
> // use with foreach to load a std container
> // from another constainer or an array
> template <typename Container>
> struct push_back_{
> Container & c;
> push_back_(Container & c_in):c(c_in){}
>
> template <typename T>
> void
> operator()(T const & t)const
> {
> return c.push_back(t);
> }
> };
> // function returns functor above
> template <typename Container>
> inline
> push_back_<Container> push_back(Container & c)
> {
> return push_back_<Container>(c);
> }
> double const pi = 3.14159265358979323846;
> int main()
> {
>
> typedef double value_type;
>
> using quan::two_d::vect;
>
> vect<value_type> box[] = {
> vect<value_type>(0,0),
> vect<value_type>(1,0),
> vect<value_type>(1,1),
> vect<value_type>(0,1),
> vect<value_type>(0,0),
> vect<value_type>(2,0),
> vect<value_type>(2,1),
> vect<value_type>(0,1),
> vect<value_type>(0,0),
> vect<value_type>(1,0),
> vect<value_type>(1,1),
> vect<value_type>(0,1),
> vect<value_type>(0,0),
> vect<value_type>(2,0),
> vect<value_type>(2,1),
> vect<value_type>(0,1),
> vect<value_type>(0,0),
> vect<value_type>(1,0),
> vect<value_type>(1,1),
> vect<value_type>(0,1),
> vect<value_type>(0,0),
> vect<value_type>(2,0),
> vect<value_type>(2,1),
> vect<value_type>(0,1),
> vect<value_type>(0,0),
> vect<value_type>(1,0),
> vect<value_type>(1,1),
> vect<value_type>(0,1),
> vect<value_type>(0,0),
> vect<value_type>(2,0),
> vect<value_type>(2,1),
> vect<value_type>(0,1),
> vect<value_type>(0,0),
> vect<value_type>(1,0),
> vect<value_type>(1,1),
> vect<value_type>(0,1),
> vect<value_type>(0,0),
> vect<value_type>(2,0),
> vect<value_type>(2,1),
> vect<value_type>(0,1)
> };
>
> //copy points to a std::vector
> std::vector<vect<value_type> > pt_vector;
> for_each(box,push_back(pt_vector));
>
> double angle = pi/2;
>
> //test code for both versions
> for_each(box,rotate(angle));
> for_each(pt_vector,rotate(angle));
>
> //get cleaner output format
> std::cout.setf(std::ios_base::fixed,std::ios_base::floatfield);
> std::cout.precision(2);
>
> for_each(box,output(std::cout,'\n'));
> for_each(pt_vector,output(std::cout,'\n'));
>
> return 0;
> }
>
> ---
> [ comp.std.c++ is moderated. To submit articles, try just posting with ]
> [ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
> [ --- Please see the FAQ before posting. --- ]
> [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
good read ! I enjoyed that. I think that would make it a lot easier
and cleaner overall. Do you have any performance metrics ? I would be
interested in seeing the your implementation metrics vs other
alternative methods metrics.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "kwikius" <andy@servocomm.freeserve.co.uk>
Date: Mon, 11 Dec 2006 23:07:17 CST Raw View
Thorsten Ottosen wrote:
> kwikius wrote:
> > Having read the in language "for (each..) proposals I am bemused as to
> > why there are no std for_each overloads as below.
>
> This has been proposed as part of the Range lib for TR2.
Is the syntax as simple as for_each(c,f) ?
> > The array version is
> > easily inlined and IMO will have a superior performance to one with
> > iterators,
>
>
> Why? Do you have performance data to back that up?
I havent spent a lot of time on it, but in the following on VC8 the
souped up array version appears to be around 10% faster (2800 ms to
3070 ms or so for the code at the end---->)
compiler VC8.0, Express. These were the compile flags flags: /Ox /Ob2
/Oi /Ot /Oy /GT /GL /FD /EHsc /MT /Za /FAs /Fa"Release\\"
/Fo"Release\\" /Fd"Release\vc80.pdb" /nologo /c /TP /errorReport:prompt
System Windows XP AMD Athlon 1.25 GHz
That isnt really the point I was making though. The point is that the
syntax is a lot more concise than the iterator syntax, and in most of
my code I use it for iterating the whole container.
regards
Andy Little
//------------------------------
// replace with your favourite vector class...
#include <quan/utility/timer.hpp>
#include <quan/two_d/out/vect.hpp>
//---------------------------------
#include <algorithm>
#include <vector>
/*
simple for_each for containers and arrays
*/
// std container version of for_each
template <typename Container, typename F>
void for_each(Container & c, F const & f)
{
std::for_each(c.begin(),c.end(),f);
}
//souped up array version...
// wrap the naked array so we can
// recursively unroll the loop
template <typename T,int N>
class array_wrapper{
T * data_ ;
public:
const static std::size_t size = N;
array_wrapper(T (&a) [N]): data_(a){}
typedef T * iterator;
typedef T const * const_iterator;
iterator begin() {return data_;}
const_iterator begin()const {return data_;}
iterator end() {return data_ + N;}
const_iterator end()const {return data_ + N;}
template<int N1>
T& at(){ return data_[N1];}
T const & at()const{ return data_[N1];}
};
// compile time loop unrolling impl
template <int N>
struct for_x_in_wrapper;
template <>
struct for_x_in_wrapper<0>{
template <typename ArrayWrapper, typename F>
void operator()(ArrayWrapper& a, F const & f)const
{
f(a.at<0>());
}
};
template <int N>
struct for_x_in_wrapper{
template<typename ArrayWrapper, typename F>
void operator()(ArrayWrapper& a, F const & f)const
{
BOOST_STATIC_ASSERT(( ArrayWrapper::size >= N ) );
for_x_in_wrapper<N-1> prev;
prev(a,f);
f(a.at<N>());
}
};
template <typename T,int N,typename F>
void for_each(array_wrapper<T,N> & ar,F const & f)
{
for_x_in_wrapper<N-1> ff;
ff(ar,f);
}
////////////////////////////////////////////////
//array version of for_each
template <typename T,unsigned int N,typename F>
void for_each(T (&ar)[N],F const & f)
{
array_wrapper<T,N> arw(ar);
for_each(arw,f);
/* for (unsigned int i = 0; i != N;++i){
f(ar[i]);
}*/
}
///////////////////////////////////////////////
// functor to rotate a vector
// number of vertices
template <typename Angle>
struct rotate_{
rotate_(Angle const & angle)
: cos_theta (cos(angle)),
sin_theta(sin(angle)){}
double cos_theta, sin_theta;
template <typename Vect>
Vect&
operator()(Vect & in)const
{
in = in * cos_theta + perp_vector(in) * sin_theta;
return in;
}
};
// function for above so for_each doesnt need
// explicit template param on struct
template <typename Angle>
inline
rotate_<Angle>
rotate(Angle const & in)
{
return rotate_<Angle>(in);
}
// use with foreach to load a std container
// from another constainer or an array
template <typename Container>
struct push_back_{
Container & c;
push_back_(Container & c_in):c(c_in){}
template <typename T>
void
operator()(T const & t)const
{
return c.push_back(t);
}
};
// function returns functor above
template <typename Container>
inline
push_back_<Container> push_back(Container & c)
{
return push_back_<Container>(c);
}
typedef double value_type;
using quan::two_d::vect;
//provide separate clean
// array for each version..
vect<value_type> arbox[] = {
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1)
};
vect<value_type> vbox[] = {
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(1,0),
vect<value_type>(1,1),
vect<value_type>(0,1),
vect<value_type>(0,0),
vect<value_type>(2,0),
vect<value_type>(2,1),
vect<value_type>(0,1)
};
int main()
{
double const pi = 3.14159265358979323846;
int const nloops = 10000000;
double const angle = pi/2;
//test code for both versions
quan::timer<> ar_timer;
for (int i = 0; i < nloops; ++i){
for_each(arbox,rotate(angle));
}
ar_timer.stop();
std::vector<vect<value_type> > pt_vector;
for_each(vbox,push_back(pt_vector));
quan::timer<> v_timer;
for (int i = 0; i < nloops; ++i){
for_each(pt_vector,rotate(angle));
}
v_timer.stop();
std::cout << "array time = " << ar_timer() <<'\n';
std::cout << "std::vector time = " <<v_timer() <<'\n';
return 0;
}
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]