Topic: STL map interface improvment (C++0x).


Author: "Balog Pal" <pasa@lib.hu>
Date: Thu, 21 Jun 2001 13:43:10 GMT
Raw View
"Dietmar Kuehl" <dietmar_kuehl@yahoo.com> wrote in message news:3B29734B.8DCE7295@yahoo.com...

> > And what could beat for_each(M) in compactness?
>
> I can offer an implementation of 'for_each()' which would allow
> something  like this:
>
>   std::for_each(M, &Object::Draw);

Just look how extremely limited that thing is. The body is limited to a single function, taking and returning void. While the other way you have a regular for-block body, call as many functions you want, with whatever params, use the visible objects, even use break & continue if convenient.

Paul

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Igor Krasnopeev <dark__raven@mail.ru>
Date: Thu, 21 Jun 2001 17:30:37 GMT
Raw View
Balog Pal wrote:

>"Dietmar Kuehl" <dietmar_kuehl@yahoo.com> wrote in message news:3B29734B.8DCE7295@yahoo.com...
>
>>   std::for_each(M, &Object::Draw);
>
>Just look how extremely limited that thing is. The body is limited to a single function, taking and returning void.

It can return anything, not only void:

find_if(M, &Object::IsVisible); - IsVisible returns bool.

And it can take 1 argument, which means, you can transfer anything you need into:

struct RotateParams {
...
};

RotateParams RP;

...

for_each(M, bind2nd(&Object::Rotate, &RP));


>While the other way you have a regular for-block body, call as many functions you want, with whatever params,
>use the visible objects, even use break & continue if convenient.

Yes, I can do this things, and if I'll really need them, I'll use for-block.
But why to use it if I don't need all of this (to draw objects, for example)?

--
Igor Krasnopeev

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Balog Pal" <pasa@lib.hu>
Date: Thu, 14 Jun 2001 22:49:01 GMT
Raw View
"Andrei Alexandrescu" <andrewalex@hotmail.com> wrote in message news:9fggka$41vn8$1@ID-14036.news.dfncis.de...

> IMHO the ultimate solution to for_each is lambda functions. You know, the
> for statement is too good to beat. Each of your proposed variants are
> improvements to the current state of affairs and many foster idiomatic uses,
> but, well, WHY all that if you have for? Why go through all the mess of
> simulating variable argument list in with()? It's counterproductive.
>
> I'd say for_each is a petty issue that wastes a lot of energy. IMHO within
> the current language it can't get much better.

Sure it can, if a few of the lastly suggested features (all of which is good by its own) are added to the language.

The whole purpose to avoid for would be to remove redundancy. (That is why the "solutions" of STL fail mostly.) So let's see what we have in for:

>  for (map<int, Object *>::iterator i = M.begin(); i != M.end(); ++i)
>  {
>      (i->second)->Draw();
>  }
>

The first thing jumping to the eye is 'map<int, Object *>::'. I want an ierator of M, so why on earth should I write all that crap?

for (M.iterator i = M.begin(); i != M.end(); ++i)

removes that.
Feature #1 to add:  operators . and -> must have access to all elements, including types.

For those not liking that use a less atractive form for the situation, but pretty useful in others:

for (typeof(M)::iterator i = M.begin(); i != M.end(); ++i)

Feature #2 to add:  typeof. It even passed the Glassborow test, and I heard no negative comments on it.

But it's still pretty much redundancly, what about the proposed auto:

for (auto i = M.begin(); i != M.end(); ++i)

Feature #3 to add:
For those who missed the proposal, auto in the above situation is substituted by the type of the initialising expression by the compiler. A real redundancy killer.

Then, having all, or some of those features, whoever still seing too much redundancy can write straightforward macros.

FOR_EACH(M)  // #define FOR_EACH(M) for (M.iterator i = M.begin(); i != M.end(); ++i)
{
      (i->second)->Draw(); // name i is hardcoded as iterator name
}

A less hardcoded one:

FOR_EACH(M, i)  // same as above
{
  FOR_EACH(*i, j)
  {
     j->Draw();
  }
}

[I wander how such a double for_each would look using the current stuff :]

Sure, macros are evil, but redundancy is much more evil, so if we decide the gain outweighs the possible problems... FOR_EACH_CONST could use const_iterator, etc. Unfortunately templates do not expand the way a macro does, to make use of the statement that follows as an integral part.

The features listed above are pretty easy to add, and would solve most of the foreach problem without more weighty stuff like lambda, just using the regular {} blocks, with whatever content. And what could beat for_each(M) in compactness?

Paul




---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Dietmar Kuehl <dietmar_kuehl@yahoo.com>
Date: Fri, 15 Jun 2001 16:09:06 GMT
Raw View
Hi,
Balog Pal wrote:
> And what could beat for_each(M) in compactness?

I can offer an implementation of 'for_each()' which would allow
something
like this:

  std::for_each(M, &Object::Draw);

Actually, I posted most of this stuff already to this thread. The only
thing missing is an overload of 'for_each()' taking a container, ie.
something like

  template <typename Cont, typename Func>
  void for_each(Cont c, Func func) {
    for_each(c.begin(), c.end(), func);
  }

Note, that the implementation of 'for_each()' has to be more clever
than currently required by the standard to allow appropriate selection
of the functor. In addition, the implemenation has to do some tricks
to find out what the function is exactly to be applied to. All this is
doable and demonstrated in the code I posted.

Concerning nested loops: I have also posted a solution to this stuff,
basically by using a function object which just applies 'for_each()' to
the argument.

Personally, I think that adding lambdas would be the right thing to
avoid things like the macro approach in more complex situations: Apart
from disliking macros, it is also often useful to store functors for
later use. This would also be possible with lambdas, although probably
at the cost of not using the closure for such lambdas: The referenced
objects be simply non-existant. That is, I don't think that there
should be something like garbage collection be used to ensure that the
closure is available during the life-time of lambda function.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Fri, 15 Jun 2001 17:09:42 GMT
Raw View
[Sorry, I've been out of town for a bit and am catching up]

Bjarne Stroustrup <bs@research.att.com> wrote:

: someone wrote

Me.  :)

:> to a lot of people.  But my experience has been that the thinking
:> required to get the syntax, order of arguments, etc. right outweighs
:> the saved keystrokes.  Keystrokes are cheap.  Looking up in manuals
:> is not.

: That points to a very serious problem.

Several, in fact.

: The problem is that "just writing what seems nice without looking up from
: the screen" maximizes the amount of code written at the expense of use of
: libraries and of use of language facilities that a programmer isn't already
: completely familiar with. For an organization, this implies a significant
: maintenance cost. For an individual, it implies a slowing of professional
: growth.

Certainly one must strive to keep up with the times.  I try to read all the
highly recommended books.  But there is also a reality of expense and time
that comes into play.  At some point I need to get work done and be able
to feed myself.  :)

: be fairly uninteresting boilerplate resembling what you have done before

Absolutely.

: have written many times before. This kind of coding is often as creative
: and constructive as cut-and-paste.

Agreed.

: Fundamental system complexity goes somewhere - it doesn't just disappear.
: Maybe it is in systems calls, maybe it is in the language, maybe it is in
: the standard library, maybe it is in a domain-specific library, maybe it
: is in the application code. From a maintenance perspetive and from a

True.  The STL and standard library in general try to provide an
implementation of some of the complexity.  IMVHO, the interfaces
themselves are too complex -- the library hasn't effectively hidden
the complexity as well as it could have.

Of course there is a tradeoff between complexity and flexibility, but
there are some things in the standard library that just don't _feel_
right.  The binders are just one example.  The string interface is another.

: Unless we spend a considerable time with textbooks, manuals, good code of
: kinds to which we are not acustomed, etc. we will stay relative novices.

Please understand that I promote this view myself.  HOWEVER, it should be
"reasonably" possible to take some knowledge about a language or library
(_especially_ a standard one!) and extrapolate it to create new constructs
without getting into the gory details of understanding implementations,
which is exactly what is necessary to use the binders (my earlier point
about bind2nd on mem_fun adaptors).

                                   -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Dietmar Kuehl <dietmar_kuehl@yahoo.com>
Date: Thu, 7 Jun 2001 22:52:42 GMT
Raw View
Hi,
Bjarne Stroustrup wrote:
> The primary gain from writing
>
>         std::for_each(m.begin(), m.end(), mem_fun(&Object::Draw));
>
> is that mem_fun() allows for_each() (and its cousins) and Object::draw()
> (and its cousins) to be used unmodified. That is, mem_fun() allows us to
> keep independent decisions to be represented independently.
>
> Simpler syntax shouldn't be achieved at the cost of avoidable dependencies.

If you look at the code implementing the "binderless for_each()" you
will
see that the "binderlessness" is reusable because the body looks
something
like

  std::for_each_intern(begin, end, get_functor(func));

Actually, with the possibility to write this, we can simplify the other
approach, too, such that we can get away with not caring about the
details of the functor at all. We would just write

  std::for_each(begin, end, std::fun(&Object::Draw));
  std::for_each(begin, end, std::fun(std::rand));

I can settle with this notation, too, and making compromises is the
normal Standard Fun ;-)
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: pdimov@mmltd.net (Peter Dimov)
Date: Tue, 5 Jun 2001 14:41:43 GMT
Raw View
Francis Glassborow <francis.glassborow@ntlworld.com> wrote in message news:<wNqJvVA0s+G7EwDS@ntlworld.com>...
> In article <GEF7o9.MBp@research.att.com>, Bjarne Stroustrup
> <bs@research.att.com> writes
> >Unless we spend a considerable time with textbooks, manuals, good code of
> >kinds to which we are not acustomed, etc. we will stay relative novices.
>
> Unfortunately far too many programmers will never read Bjarne's words
> because they never read.

And some will never read his words because his posts, sadly, don't
appear in Google Groups.

--
Peter Dimov
Multi Media Ltd.

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Tue, 5 Jun 2001 15:33:45 GMT
Raw View
Hi,
Joachim Achtzehnter <joachim@kraut.ca> wrote:
> So, this deals with yet another small subset of problems that true
> lambda expressions (or closures) would address more generally.

I'm not arguing against lambda expressions or how to remove the need
for lambda expressions. In fact, lambda expressions are the single
most important missing feature in C++, IMO. No doubt, there is a
large design space lurking here (eg. the life time of the closure,
if there is such thing as a closure in the first place) but something
like this is definitely desirable, eg. to make the algorithms more
useful. I don't think that it can be emulated in a reasonable way
although a suitable approach can be implemented as some form of
preprocessor (no, not with the C preprocessor...).

That said, note that the code I have provided solves an orthogonal
problem, namely deciding what exactly the functor is to be applied
to. Even if you have lambda expressions it is reasonable to have
the compiler decide to which component a function is really to be
applied, eg. to use the same functor in generic code to deal with
'std::vector<T>' and with 'std::map<K, T>': In the former case the
functor is to be applied to '*it' in the latter it is to be applied
to 'it->second'.

> You may
> argue that the 10kB are in library code and of no concern to ordinary
> programmers. But the whole point of generic programming and the STL is
> extensibility, hence ordinary programmers may want to write such
> algorithms too.

No doubt about this one. Note, however, that the code would be much
smaller if it were implemented as a solution specific to 'for_each()'!
It is implemented in a way which makes it possible to reuse the stuff
in other algorithms. This considerably contributes to the size of the
solution. I'm not arguing that it is good that there is additional
knowledge necessary to implement algorithms with a good interface but
I'm arguing that it not as bad as it sounds. ... and you will have to
know something anyway to create good interfaces.

> It is also questionable whether such "magic" solutions are the way to
> go. It would not be a big deal to explicitly specify the extra step of
> dereferencing the value_type's "second" member, if it was possible to
> do so without great pain. The code would be much easier to understand
> than to rely on algorithms that magically discern how to navigate to
> an object to which the functor can be applied.

I agree that "magic" solutions are in general indeed questionable
and probably such an algorithm should go into namespace 'dwim'. On
the other hand, the resulting notation does indeed just decribe what
is desired: Apply a member function to all elements in a sequence.
That it happens to be the 'second' element to which it is actually
applied is a detail requiring a fair amount of code obfuscation
(at least without lambda expressions) leading to abandon the use of
algorithms all together and, in any case, leads to a loss of
genericity, even if lambda expressions are available. If you dislike
this approach, consider it as an idea which can help solving problems,
actually a problem which is *not* solved entirely with lambda
expressions: Not even those beasts are a solution to everything :-)

> The generic programming paradigm that was introduced into standard C++
> with the STL cries out for language support of lambda expressions.

I second this, even though it is entirely unrelated to the stuff
did with my for_each() tweaks :-) It is important that lambda
expressions, if added to the language, should be done in a C++ way,
that is, we should not get carried away and suddenly require a
garbage collector just because it would make closures nicer! BTW,
a concise C++ like syntax for lambda expression which would make
them easy to use is something I haven't come up with yet.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Andrei Alexandrescu" <andrewalex@hotmail.com>
Date: Mon, 4 Jun 2001 17:57:17 GMT
Raw View
"Nick Thurn" <nickt@kipling.aus.deuba.com> wrote in message
news:9fcosg$87v$1@kilmer.bain.oz.au...
> I wonder at what point for_each would become useful? I'm sure a binderless
> version is possible but as you say this doesn't scale.

IMHO the ultimate solution to for_each is lambda functions. You know, the
for statement is too good to beat. Each of your proposed variants are
improvements to the current state of affairs and many foster idiomatic uses,
but, well, WHY all that if you have for? Why go through all the mess of
simulating variable argument list in with()? It's counterproductive.

I'd say for_each is a petty issue that wastes a lot of energy. IMHO within
the current language it can't get much better.


--
Andrei

--
Check out THE C++ Seminar:  3 Days with 5 Experts
http://www.gotw.ca/cpp_seminar



---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Mon, 4 Jun 2001 22:21:52 GMT
Raw View
In article <GEF7o9.MBp@research.att.com>, Bjarne Stroustrup
<bs@research.att.com> writes
>Unless we spend a considerable time with textbooks, manuals, good code of
>kinds to which we are not acustomed, etc. we will stay relative novices.

Unfortunately far too many programmers will never read Bjarne's words
because they never read. The lack of continued professional development
among many programmers is one of the most depressing things about the
software industry. Of course, almost by definition, those who read this
NG are not the problem.


Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: nickt@kipling.aus.deuba.com (Nick Thurn)
Date: Tue, 5 Jun 2001 00:16:21 GMT
Raw View
Andrei Alexandrescu wrote:
> "Nick Thurn" <nickt@kipling.aus.deuba.com> wrote in message
> news:9fcosg$87v$1@kilmer.bain.oz.au...
> > I wonder at what point for_each would become useful? I'm sure a binderless
> > version is possible but as you say this doesn't scale.
>
> IMHO the ultimate solution to for_each is lambda functions. You know, the
> for statement is too good to beat. Each of your proposed variants are
> improvements to the current state of affairs and many foster idiomatic uses,
> but, well, WHY all that if you have for? Why go through all the mess of
> simulating variable argument list in with()? It's counterproductive.
>
Well I did get the idea from a little red book by someone very like
yourself ;-). Having said that "with" is one of my (tm) dumb ideas.
Obviously (to me now anyway) some sort of expansion of binder to
handle variable numbers of arguments would be better.

There are probably cases where for_each makes sense and gives
a cleaner syntax than naked for.

 for_each(objects,binder(&Shape::Offset,x,y,z));
 for_each(matrix,assign(normal_rand,&seed));
 for_each(mymap,multiply(some_fiddle_factor));

Lambda functions? Yes but to implement them we end up with all the
overhead of blitz++ or have to change the language.

I was sort of alluding to this asking is it all worth the effort.
To make for_each usable the amount of machinery and compile time
overhead may outweigh all the benefits.

> I'd say for_each is a petty issue that wastes a lot of energy. IMHO within
> the current language it can't get much better.
>
I agree for for_each but I think it's the key. As pretty much the
simplest algorithm if it's not usable then the rest of the library
is also less usable.

cheers
Nick nick.thurn@db.com

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: nickt@kipling.aus.deuba.com (Nick Thurn)
Date: Tue, 5 Jun 2001 09:52:33 GMT
Raw View
Bjarne Stroustrup wrote:
>
> The problem is not to write a "binderless for_each()". The problem is that
> once you have done so, you'd want to do similarly to every standard algorithm
> that can take a member function. Once you have done that, we'll want to do
> the same for standard-library-style algorithms that we use, but just happens
> not to be in the standard library.
>
> The primary gain from writing
>
>  std::for_each(m.begin(), m.end(), mem_fun(&Object::Draw));
>
> is that mem_fun() allows for_each() (and its cousins) and Object::draw()
> (and its cousins) to be used unmodified. That is, mem_fun() allows us to
> keep independent decisions to be represented independently.
>
> Simpler syntax shouldn't be achieved at the cost of avoidable dependencies.
>
for_each already accepts regular functions and user defined functors. Not
accepting member functions and requiring special syntax seems like a wart IMO.

I'm probably thick but what dependencies are introduced on Object::draw()
that aren't introduced by mem_fun(&Object::draw)? why would it have to be
modified?

If binders were provided in the library with more automation (ie similar
to that provided in Andrei's Loki) they or their building blocks
could be used internally to deal with conversion to an appropriate functor
or typedef to an appropriate inline wrapper function. This would leave the external
interface unchanged. It *would* propagate through the library internally but apart
from not being in the current standard why is this bad?

BTW Thank you for C++, despite constant complaints I do love it.

cheers
Nick nick.thurn@db.com

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Igor Krasnopeev <dark__raven@mail.ru>
Date: Tue, 5 Jun 2001 14:40:31 GMT
Raw View
Bjarne Stroustrup wrote:

>
>nickt@kipling.aus.deuba.com (Nick Thurn) writes:
>
>> Dietmar Kuehl wrote:
>> >   std::for_each(m.begin(), m.end(), &Object::Draw);
>> >
>> > is a pretty good notation and as I pointed out in another article in
>> > this thread this can be achieved if the algorithms are made more
>> > flexible (in this article I only outlined how to go about this and
>> > had no implementation; if anybody is interested in this implementation
>> > I can post it: together with supporting definitions it is about 10kB).
>> >
>> I for one would be very interested. I've produced my own binderless for_each
>> over the last couple of days as an exercise. It weighs in at 5.5k but
>> includes <utility> (It's also very likely to be only half tested and
>> doesn't deal with volatile and other dark things). It does include
>> for_each_key which iterates over the keys of an assocIative container
>> (for_each iterates over values as the default case). I haven't studied
>> your other post in detail as yet but that's the next thing on my
>> parttime agenda.
>
>The problem is not to write a "binderless for_each()". The problem is that
>once you have done so, you'd want to do similarly to every standard algorithm
>that can take a member function. Once you have done that, we'll want to do
>the same for standard-library-style algorithms that we use, but just happens
>not to be in the standard library.

I saw Dietmar solution, and it's more complex, than just  "binderless for_each()".

>The primary gain from writing
>
> std::for_each(m.begin(), m.end(), mem_fun(&Object::Draw));
>
>is that mem_fun() allows for_each() (and its cousins) and Object::draw()
>(and its cousins) to be used unmodified. That is, mem_fun() allows us to
>keep independent decisions to be represented independently.
>

The definition is:

  template <typename It, typename Func>
  void for_each(It beg, It end, Func f)
  {
    for_each_intern(beg, end, get_functor(f));
  }

where get_functor returns appropriate "something, derived from unary_function".
For example:

  template <typename R, typename S>
  std::mem_fun_ref_t<R, S> get_functor(R (S::*member)()) {
    return std::mem_fun_ref_t<R, S>(member);
  }

The only problem is: there is no way to differ mem_fun_ref_t from mem_fun_t only
with arguments of get_functor. In both case it's R (S::*member)().

But it can also be solved by using knowledge about It::value_type.

>Simpler syntax shouldn't be achieved at the cost of avoidable dependencies.

So, with get_functor() added, any algo, which accepts Fun may be written like for_each
in my example. And there will be no new dependeces between functions
(i.e. Object::draw()) and algo's (i.e. for_each).

--
Igor Krasnopeev

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: nickt@kipling.aus.deuba.com (Nick Thurn)
Date: Sat, 2 Jun 2001 21:39:15 CST
Raw View
Dietmar Kuehl wrote:
>   std::for_each(m.begin(), m.end(), &Object::Draw);
>
> is a pretty good notation and as I pointed out in another article in
> this thread this can be achieved if the algorithms are made more
> flexible (in this article I only outlined how to go about this and
> had no implementation; if anybody is interested in this implementation
> I can post it: together with supporting definitions it is about 10kB).
>
I for one would be very interested. I've produced my own binderless for_each
over the last couple of days as an exercise. It weighs in at 5.5k but
includes <utility> (It's also very likely to be only half tested and
doesn't deal with volatile and other dark things). It does include
for_each_key which iterates over the keys of an assocIative container
(for_each iterates over values as the default case). I haven't studied
your other post in detail as yet but that's the next thing on my
parttime agenda.

cheers
Nick nick.thurn@db.com

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Mirek Fidler" <cxl@volny.cz>
Date: Sat, 2 Jun 2001 21:39:32 CST
Raw View
> > ArrayMap<int, Object> M;
> >
> > for(int i = 0; i < M.GetCount(); i++)
> >     M[i].Draw();
>
> Using today's knowledge of templates, STL could easily be changed to
> allow
>
>   std::for_each(M.begin(), M.end(), &Object::Draw);
>
> Not a problem at all :-) (yes, I know that 'Object' is not the value
> type
> of the map's iterator) ... and, the best thing about it, if someone as
> a clever idea how to iterate over a containers (and there are ideas
> which
> can make a major difference, eg. a factor of two if there is only a
> simple operation in the functor; of course, in the "Draw()" case, the
> dominant part will probably be the "Draw()" function, not the iteration;
> just adding an offset to the members will fall in the category I'm
> talking
> of) it can be used underneath.

    In fact, we could write

    ArrayMap<int, Object> M;

    std::for_each(M.Begin(), M.End(), &Object::Draw);

    too, just with this syntax, using normal stl algorithm. But I found
using offsets for normal high-level algorithms better, because

- the are easier to use (less verbose, easier to understand)
- they can be easily bound checked in debug mode - something you cannot with
iterators (and yes, it cathes a LOT of errors)
- they can be used to couple data - you can use same offset for two
containers for some reason - that simplifies construction of algorithms
- there is no performance penalty on current optimizing compilers

> Also, if it turns out that it is more
> reasonable to use a list structure underneath, eg. because most
> operations
> are insertions and removal of objects but only rare uses of iteration,
> the
> code has not to be rewritten.

    Yes, this argument is repeated over and over, but in 16 years of my
practice, I was never in situation I would need such thing. In fact, I found
a little use for list container as it is.

> >     Not a problem at all :) Best thing that could be done to STL is to
> > remove it from standard (I know it is impossible). It just blocks place
for
> > better solutions ("New container set ? Why ? We have STL in standard
!").
>
> Although I guess you are not alone with thinking that your library is a
> better approach, I'm pretty sure that you are wrong in this thinking!

    I'm absolutely sure I am not ;-) But that was not the point. You could
be right considering our library, but I think you would say the same thing
about any other library just because it is not STL and/or it has slightly
different approach.

> small portion of the interface you showed indicates that your library is
> inferior to STL by a rather wide margin. Without seeing the your library
> it is, of course, impossible to tell for sure but given a reasonable
> measure of library quality (if there is demand I can outline what I
> consider appropriate in this category)

    I do consider following points:

- easy of use. Library should solve your problems instead of creating them.
I think we are better in this, as described in above example.

- orthogonality. Have you ever tried to do something like this in STL

    struct Foo {
        vector<Boo> xxx;
        int y;
    };

    vector<Foo> yyy;

?

    Yes, it is legal, but its performance is horrible. Thus STL forces you
to use workarounds complicating whole your code - so it spoils easy of use
point. We have no such tradeoffs in our library.

- performance. As for this, we benchmarked our library and found it 2-3
times faster than STL (original HP implementation) - due to better
interfaces. Btw, have you noticed that

vector<int *> a;
vector<double *> b;

    would generate two sets of template code which are completely same (at
least on most compilers), thus increasing code size with no reason ?

> I would bet big bucks against
> your
> library from just seeing the small excerpt you have given!

    ;-) Would you like to know more ?

> I'm not claiming that STL is perfect or that it is the best library.
> However, the underlying principle is hard to beat. The only way STL
> failed

    Yes, I like it too- from theoretical point of view. But I found it next
to unusable in normal work.

Mirek



---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: nickt@kipling.aus.deuba.com (Nick Thurn)
Date: Sun, 3 Jun 2001 02:41:57 GMT
Raw View
Dietmar Kuehl wrote:
> I think that
>
>   std::for_each(m.begin(), m.end(), &Object::Draw);
>
> is a pretty good notation and as I pointed out in another article in
> this thread this can be achieved if the algorithms are made more
> flexible (in this article I only outlined how to go about this and
> had no implementation; if anybody is interested in this implementation
> I can post it: together with supporting definitions it is about 10kB).
>
Well, I'll show you mine if you show me yours ;-)

This includes conversion from T* to T& for function invocation.
So assumes Function accepts a T&. Perhaps this is bad?
Invokes the function on pair<K,T>::second as default and
includes for_each_key for invoking on pair<K,T>::first.
It also includes versions that operate on containers where
a container has a begin and end method.

I'm not a guru here and am somewhat worried about the passing
of Function by value to the call_function helper but I guess
that's how the rest of the stl works. Testing has only
been done on g++ and I havent tried a case where T != C in
the member function versions or virtual inheritance etc.

Any comments appreciated.

cheers
Nick nick.thurn@db.com

#include <utility>

namespace std
{
    namespace Private
    {
        template <class T, class F>
        inline void call_function(T& t, F fn)
        { fn(t); }

        template <class T, class F>
        inline void call_function(T*& t, F fn)
        { fn(*t); }

        template <class T, class R, class C>
        inline void call_function(T& t, R (C::*fn)())
        { (t.*fn)(); }

        template <class T, class R, class C>
        inline void call_function(T*& t, R (C::*fn)())
        { (t->*fn)(); }

        template <class T, class R, class C>
        inline void call_function(T const& t, R ( C::*fn)() const)
        { (t.*fn)(); }

        template <class T, class R, class C>
        inline void call_function(T* const& t, R (C::*fn)() const)
        { (t->*fn)(); }

        // pair<K,T> invokes F for pair<K,T>::second
        template <class K, class I, class F>
        inline void call_function(pair<K,I>& p, F fn)
        { call_function(p.second,fn); }

        template <class K, class I, class R, class C>
        inline void call_function(pair<K,I>& p, R (C::*fn)())
        { call_function(p.second,fn); }

        template <class K, class I, class F>
        inline void call_function(pair<K,I> const& p, F fn)
        { call_function(p.second,fn); }

        template <class K, class I, class R, class C>
        inline void call_function(pair<K,I> const& p, R (C::*fn)() const)
        { call_function(p.second,fn); }

    }

    template <class Input, class Function>
    Function for_each(Input first, Input last, Function f) {
        for ( ; first != last; ++first)
            Private::call_function(*first,f);
        return f;
    }

    template <class Container, class Function>
    inline Function for_each(Container& c, Function f) {
         return for_each(c.begin(),c.end(),f);
    }

    // new algorithm for iterating over keys of an association
    template <class Input, class Function>
    Function for_each_key(Input first, Input last, Function f) {
        for ( ; first != last; ++first)
            Private::call_function(first->first,f);
        return f;
    }

    template <class Container, class Function>
    inline Function for_each_key(Container& c, Function f) {
         return for_each_key(c.begin(),c.end(),f);
    }

}


---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Dietmar Kuehl <dietmar_kuehl@yahoo.com>
Date: Sun, 3 Jun 2001 05:46:27 GMT
Raw View
Hi,
Mirek Fidler wrote:
>     In fact, we could write
>     ArrayMap<int, Object> M;
>     std::for_each(M.Begin(), M.End(), &Object::Draw);
>
>     too, just with this syntax, using normal stl algorithm.

Just a dumb question: What is your library about: Containers or
algorithms? If it is about the former, don't mind my earlier
comments: I don't care about the details of container libraries at all!
That's the reason I like STL: It is easy to adapt basically arbitrary
sequence to make them accessible with STL algorithms.

What I'm caring about are algorithms, more precisely avoiding the
combinatorial explosion of algorithm implementations necessary when
providing all algorithms for all containers on which they make sense.

> - the are easier to use (less verbose, easier to understand)

The only case where I consider indices to be easier to understand is
when doing things which inherently only work on random access sequences.
In this case it does not really matter whether an iterator or an index
is used because the algorithm cannot be applied to a non-random access
sequence anyway. In general, I prefer iterators but, of course, this is
to some degree personal preference.

> - they can be easily bound checked in debug mode - something you cannot with
> iterators (and yes, it cathes a LOT of errors)

Iterators can be bound checked as well, although it normally requires
a little bit more though. On the other hand, algorithms can later be
implemented in a way which hoists the check out of the inner loops, at
least, improving the performance of debug code.

> - they can be used to couple data - you can use same offset for two
> containers for some reason - that simplifies construction of algorithms

That's an interesting one. Actually, my major problem with the STL
concepts is coupling of the concept for positioning with the concept of
data access: These should be split into two separate concepts, ie. into
iterators and propert maps (don't get distracted by the name "map": it
is as efficient as the current access). This would directly solve this
problem in a convenient way to: There would be just one iterator with
two property maps.

For many algorithms it is, however, inapplicable anyway because there is
no such thing as random access and it does not make much sense to
restrict
the applicability of an algorithm just to ease its implementation. ...
and it is worth noting that some algorithms, when applied to a pair of
random access sequence, actually use indices internally as an
optimiztion.

> > Also, if it turns out that it is more
> > reasonable to use a list structure underneath, eg. because most
> > operations
> > are insertions and removal of objects but only rare uses of iteration,
> > the
> > code has not to be rewritten.
>
>     Yes, this argument is repeated over and over, but in 16 years of my
> practice, I was never in situation I would need such thing. In fact, I found
> a little use for list container as it is.

List containers are no the only non-random access sequence. Much more
important sequences in this category are some forms of stream (eg. TCP
connection) providing an input sequence and results of data base queries
providing some sort of read-only bidirectional sequence. It surprises me
that you haven't come around such sequences in your 16 years of
experience: I see them all over and there are only very few programs
I was involved with got away without them.

> > Although I guess you are not alone with thinking that your library is a
> > better approach, I'm pretty sure that you are wrong in this thinking!
>
>     I'm absolutely sure I am not ;-) But that was not the point. You could
> be right considering our library, but I think you would say the same thing
> about any other library just because it is not STL and/or it has slightly
> different approach.

My basic objection to typical algorithms libraries is that they are
bound to
a fixed set of containers: If you want to use an algorithm, you have to
use the corresponding containers. This is unacceptable. Any algorithm
library taking such approach is close to useless. Most algorithms
libraries
I have seen actually fall into this category. There were a few attempts
using an object oriented approach to do what STL does with template
arguments which were simply too slow for any practical application. If
you
have an efficient algorithms library which is applicable to my
containers
I would be very interested in how you are doing it, unless, of course,
you
are actually using the STL approach: I know how and that this works.

Like I mentioned above, if your library basically provides just a bunch
of
containers, that is just fine with me: Everybody tossed together
suitable
containers. What is important is that these can be used with algorithms
coming from somewhere. If these are implemented STL-like, it is possible
to adapt basically arbitrary containers. That is, it is again just the
algorithms library which has to take care of doing the right thing. Of
course, if the containers already follow the STL conventions, providing
iterators following the STL concepts, it is easier but this is not a
requirement.

> - easy of use. Library should solve your problems instead of creating them.
> I think we are better in this, as described in above example.

STL solves my problems with sequences. I don't see that STL creates
problems for me. In places the interface is not as convenient as it
could
be but this is something I want to change :-)

> - orthogonality. Have you ever tried to do something like this in STL
>
>     struct Foo {
>         vector<Boo> xxx;
>         int y;
>     };
>     vector<Foo> yyy;
>
> ?
>
>     Yes, it is legal, but its performance is horrible.

Personally, I cannot see any performance problem in the above code,
mostly
because it does nothing... I'm not sure what your point about this code
and its performance trade-off is. Also, if you need a dynamically sized
array containing dynamically, independently sized array, I don't see how
you can be much better than this. Also, I don't really care about
containers: I consider the containers in the standard C++ library to be
mere examples which are useful to start working with. The whole point
about the STL is that you can just kick out the containers and replace
them by more suitable ones: The containers are not the center of the
world.

> Thus STL forces you
> to use workarounds complicating whole your code - so it spoils easy of use
> point. We have no such tradeoffs in our library.

I don't see any application of an algorithm here. Thus, I don't see
where
the STL does complicate things at all. From what you have said I have
the
impression that your library is actually about containers. Great! Use
your container where they are more suitable than the STL containers. You
can even use the STL algorithms with these containers, if necessary
after
providing suitable iterators.

> - performance. As for this, we benchmarked our library and found it 2-3
> times faster than STL (original HP implementation) - due to better
> interfaces.

Better interface to what and what exactly have you benchmarked? You have
a sort algorithm which performs 2-3 faster than 'std::sort()' on eg. an
'std::vector<int>'? Cool! You must have a big algorithmic improvement
:-)

> Btw, have you noticed that
>
> vector<int *> a;
> vector<double *> b;
>
>     would generate two sets of template code which are completely same (at
> least on most compilers), thus increasing code size with no reason ?

Yes, most compilers ship with really bad implementations. Basically,
most even struggle to ship with conforming implementations. But who
cares? It is possible to implement the library to avoid eg. the problem
you mentioned (just partially specialize for pointers and use a
'vector<void*>' underneath; basically a 'vector<T*>' does mostly
casting).
But then, who uses pointers in application level code anyway? Of course,
the same optimization as with 'T*' should also be done with eg.
'boost::shared_ptr<T>' which, however, requires more thought and even
some cooperation with 'boost::shared_ptr<T>'...

>     ;-) Would you like to know more ?

If it is about algorithms independent from containers, yes. Since you
claim that algorithms are in general 2 - 3 times faster on your
interfaces,
it would also be helpful to see those interfaces: Maybe the concepts
used
with STL are grossly wrong and should be corrected (however, I consider
this to be rather unlikely).

>
> > I'm not claiming that STL is perfect or that it is the best library.
> > However, the underlying principle is hard to beat. The only way STL
> > failed
>
>     Yes, I like it too- from theoretical point of view. But I found it next
> to unusable in normal work.

I'm using it in my daily work (for different purposes than implementing
it: I also have to work for money) and I consider it to be rather
helpful.
For one, the algorithms make my code clearer simply be stating what is
going on rather than writing some loop. The other equally important
aspect is that there is no question how sequences are passed around:
They
are passed around as iterator pairs, not as some container with a type
unusable at the other end anyway... It is possible that my work (lots
of operation on sequences read from files or from data base queries) are
particularily well suited to be handled with these concepts.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: nickt@kipling.aus.deuba.com (Nick Thurn)
Date: Sun, 3 Jun 2001 07:49:49 GMT
Raw View
Andrei Alexandrescu wrote:
> I offer to avoid this redundancy by using the good old for loop. I don't
> understand why I'd have to obstinate to use for_each if it's harder to
> write, harder to read, less maintainable, more redundant.
>
> Of course, there are simpler situations in which for_each does make sense.
> However, that doesn't scale: as soon as I try to change a simple for_each
> into a slightly more elaborate for_each, the syntax explodes. Not so with
> the good old for statement... for_each has you fall in love with for.
>
Hi Andrei,

I wonder at what point for_each would become useful? I'm sure a binderless
version is possible but as you say this doesn't scale.

The code I posted was edited to remove additional versions that
accepted a parameter to the member function or function/functor as
this caused ambiguity when using builtin arrays (but I'm gonna fix it!!).

I can imagine the following as a useful syntax that is also implementable:
Associative containers default to pair<K,T>::second. The "with" function
binds together an arbitrary number of parameters to the preceeding
function (perhaps this should include the function and be called bind??)

        // for builtin arrays and containers
        for_each(a, fn);
        for_each(a, fn, with(p1, p2, ...) );

        for_each(a, X::static_method);
        for_each(a, X::static_method, with(p1, p2, ...) );

        for_each(a, &X::method);
        for_each(a, &X::method, with(p1, p2, ...) );

        // for pointers and builtin array subranges
        for_each(a, a+size, fn);
        for_each(a, a+size, fn, with(p1, p2, ...) );

        for_each(a, a+size, X::static_method);
        for_each(a, a+size, X::static_method, with(p1, p2, ...) );

        for_each(a, a+size&, X::method);
        for_each(a, a+size&, X::method, with(p1, p2, ...) );

        // for iterator ranges and container subranges
        for_each(a.begin(), a.end(), fn);
        for_each(a.begin(), a.end(), fn, with(p1, p2, ...) );

        for_each(a.begin(), a.end(), X::static_method);
        for_each(a.begin(), a.end(), X::static_method, with(p1, p2, ...) );

        for_each(a.begin(), a.end(), &X::method);
        for_each(a.begin(), a.end(), &X::method, with(p1, p2, ...) );

        // for associative container keys, pair<K,T>::first
        for_each_key(a, fn);
        for_each_key(a, fn, with(p1, p2, ...) );

        for_each_key(a, X::static_method);
        for_each_key(a, X::static_method, with(p1, p2, ...) );

        for_each_key(a, &X::method);
        for_each_key(a, &X::method, with(p1, p2, ...) );

        // for associative container key subranges
        for_each_key(a.begin(), a.end(), fn);
        for_each_key(a.begin(), a.end(), fn, with(p1, p2, ...) );

        for_each_key(a.begin(), a.end(), X::static_method);
        for_each_key(a.begin(), a.end(), X::static_method, with(p1, p2, ...) );

        for_each_key(a.begin(), a.end(), &X::method);
        for_each_key(a.begin(), a.end(), &X::method, with(p1, p2, ...) );

The big questions are "is this useful?" "is it worth the effort?".

Looking beyond for_each what other areas of stl would or could be
streamlined? Would this be useful?

What do you think? Is this all just an academic exercise?

cheers
Nick    nick.thurn@db.com


---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Mirek Fidler" <cxl@volny.cz>
Date: Sun, 3 Jun 2001 18:11:12 GMT
Raw View
> >     In fact, we could write
> >     ArrayMap<int, Object> M;
> >     std::for_each(M.Begin(), M.End(), &Object::Draw);
> >
> >     too, just with this syntax, using normal stl algorithm.
>
> Just a dumb question: What is your library about: Containers or
> algorithms?

    Well, in fact it as about everything we need. So it is about containers,
algorithms, persistenece, streams, synchronization, GUI, SQL, graphics,
parsing etc, etc. But here I was talking about containers part of it.

> What I'm caring about are algorithms, more precisely avoiding the
> combinatorial explosion of algorithm implementations necessary when
> providing all algorithms for all containers on which they make sense.

    OK, than it was just a little misunderstanding - I was talking about
containers and their interfaces...

> > - the are easier to use (less verbose, easier to understand)
>
> The only case where I consider indices to be easier to understand is
> when doing things which inherently only work on random access sequences.
> In this case it does not really matter whether an iterator or an index
> is used because the algorithm cannot be applied to a non-random access
> sequence anyway.

   But that is the point - we found effective implementation in which ALL
containers have random access - including maps.

> In general, I prefer iterators but, of course, this is
> to some degree personal preference.

    We use iterators as lower-level facility - in algorithms.

> > - they can be easily bound checked in debug mode - something you cannot
with
> > iterators (and yes, it cathes a LOT of errors)
>
> Iterators can be bound checked as well, although it normally requires
> a little bit more though.

    Yes, you are right. But it is ussually rather hard to implement.

> On the other hand, algorithms can later be
> implemented in a way which hoists the check out of the inner loops, at
> least, improving the performance of debug code.

    Accidentaly, this is not problem in our code - as I described, library
algorithms ussually use iterators, which we leave unchecked... (nothing
wrong can happen in library code).

> > - they can be used to couple data - you can use same offset for two
> > containers for some reason - that simplifies construction of algorithms
>
> For many algorithms it is, however, inapplicable anyway because there is
> no such thing as random access and it does not make much sense to

    As I described above, there is allways random access.

> >     Yes, this argument is repeated over and over, but in 16 years of my
> > practice, I was never in situation I would need such thing. In fact, I
found
> > a little use for list container as it is.
>
> List containers are no the only non-random access sequence. Much more
> important sequences in this category are some forms of stream (eg. TCP
> connection) providing an input sequence and results of data base queries
> providing some sort of read-only bidirectional sequence. It surprises me
> that you haven't come around such sequences in your 16 years of
> experience: I see them all over and there are only very few programs
> I was involved with got away without them.

    I was not talking about fact that some entities do not have random
access. What I was talking about is that I never had to find that out later
and change container from random-access to non-random.

> >     I'm absolutely sure I am not ;-) But that was not the point. You
could
> > be right considering our library, but I think you would say the same
thing
> > about any other library just because it is not STL and/or it has
slightly
> > different approach.
>
> My basic objection to typical algorithms libraries is that they are
> bound to
> a fixed set of containers: If you want to use an algorithm, you have to
> use the corresponding containers. This is unacceptable. Any algorithm

   I would agree with that.

> arguments which were simply too slow for any practical application. If
> you
> have an efficient algorithms library which is applicable to my
> containers
> I would be very interested in how you are doing it, unless, of course,
> you
> are actually using the STL approach: I know how and that this works.

    OK, I was rather talking about fact that STL containers are
unpractical - see another discussion threads for examples - non of them
would cause a problem in our library. In reality, we use a lot of STL
approach for algorithms (besides fact we can use stl algorithms as they
are).

> course, if the containers already follow the STL conventions, providing
> iterators following the STL concepts, it is easier but this is not a
> requirement.

    In fact, it does (if you dont mind other naming conventions, e.g. Begin
vs begin...)

> >     struct Foo {
> >         vector<Boo> xxx;
> >         int y;
> >     };
> >     vector<Foo> yyy;
> >
> > ?
> >
> >     Yes, it is legal, but its performance is horrible.
>
> Personally, I cannot see any performance problem in the above code,
> mostly because it does nothing... I'm not sure what your point about this
code
> and its performance trade-off is.

    When vector<Foo> is expanding, it has to copy all inner Vector<Boo> to
new memory location. Something that is unnecessary in fact.

> Also, if you need a dynamically sized
> array containing dynamically, independently sized array, I don't see how
> you can be much better than this.

    Avoid unnecessary copying...

> Also, I don't really care about
> containers: I consider the containers in the standard C++ library to be
> mere examples which are useful to start working with. The whole point
> about the STL is that you can just kick out the containers and replace
> them by more suitable ones: The containers are not the center of the
> world.

    As I said I praise STL as a new way of doing things and we borrow a lot
from it. But sometimes it seems to me as STL was never tested in real
world...

> I don't see any application of an algorithm here. Thus, I don't see where
> the STL does complicate things at all. From what you have said I have
> the impression that your library is actually about containers. Great! Use
> your container where they are more suitable than the STL containers. You
> can even use the STL algorithms with these containers, if necessary
> after providing suitable iterators.

    Yes. But in reality, how much algorithms do you use ? I ussually use
Sort, Find, BinaryFind, FindIndex, ForwardSort, GetSortOrder (I am not sure
about exact STL counterparts, perhaps it is sort, search, ?, ?, stable_sort,
?). I have a little use for others.

    Then, of course, you have your custom algorithms, but still enough
generic to write them as templates. But there is a little use for STL in
this other than showing the way - sometimes.

> > - performance. As for this, we benchmarked our library and found it 2-3
> > times faster than STL (original HP implementation) - due to better
> > interfaces.
>
> Better interface to what and what exactly have you benchmarked? You have
> a sort algorithm which performs 2-3 faster than 'std::sort()' on eg. an
> 'std::vector<int>'? Cool! You must have a big algorithmic improvement
> :-)

    Well, I was talking about containers and what we actually benchmarked
was adding elements to vector<> containers - something that in fact is
rather common thing.

> But then, who uses pointers in application level code anyway? Of course,
> the same optimization as with 'T*' should also be done with eg.
> 'boost::shared_ptr<T>' which, however, requires more thought and even
> some cooperation with 'boost::shared_ptr<T>'...

    There are situation where you need to use them. But you can go even
further - you can share code for most part of vector<int>, vector<void *>
and vector<string>...

> >     Yes, I like it too- from theoretical point of view. But I found it
next
> > to unusable in normal work.
>
> I'm using it in my daily work (for different purposes than implementing
> it: I also have to work for money) and I consider it to be rather
> helpful.
> For one, the algorithms make my code clearer simply be stating what is
> going on rather than writing some loop.

    The problem, also mentioned by Andrei, is that often writing the loop
results actually in clearer code than some horrible composition like

for_each(M.begin(), M.end(),
               compose1(mem_fun(&Object::Draw),
              select2nd<map<int, Object *>::value_type>()));

    BTW, I wonder how much often you apply algorithm on something else than
whole container (begin-end). There certainly should be some simplified form
(for_each(M, ....)).

Mirek


---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Dietmar Kuehl <dietmar_kuehl@yahoo.com>
Date: Sun, 3 Jun 2001 18:11:50 GMT
Raw View
Hi,
Nick Thurn wrote:
>=20
> I for one would be very interested. I've produced my own binderless for=
_each
> over the last couple of days as an exercise. It weighs in at 5.5k but
> includes <utility> (It's also very likely to be only half tested and
> doesn't deal with volatile and other dark things). It does include
> for_each_key which iterates over the keys of an assocIative container
> (for_each iterates over values as the default case). I haven't studied
> your other post in detail as yet but that's the next thing on my
> parttime agenda.

OK. My version mostly concentrates on the aspects of turning an
arbitrary
function or functor into a functor and selecting on which argument the
function is to be called on: The whole value, the first, or the second
component. Using partial specialization it is possible to extend the
selection part to other value_types than pairs, too. The code does not
address the problem of possibly dereferencing the argument, that is, it
is assumed that the functor can be applied directly. I think it would be
doable to include something like this, too, at the cost of increased
complexity but probably it is not that bad at all.

My feeling is that the standard library would benefit from consistently
using something like this: Algorithms would be easy to use even for
more complex stuff. Actually, to provide an easy to use approach to
functors in general, something like "lambdas", that is functions created
on the fly, probably mostly from just a function body, would be highly
desirable... (no, I don't want to into this here :-)
--=20
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>
--- CUT HERE ---
// -*-C++-*- for_each.cpp
//
<!!----------------------------------------------------------------------=
->=20
// <!! Copyright =A9 2001 Dietmar K=FChl, All Rights
Reserved                    >=20
//
<!!                                                                     =20
>=20
// <!! Permission to use, copy, modify, distribute and sell
this             >=20
// <!! software for any purpose is hereby granted without fee,
provided      >=20
// <!! that the above copyright notice appears in all copies and
that        >=20
// <!! both that copyright notice and this permission notice appear
in       >=20
// <!! supporting documentation. Dietmar K=FChl makes no representations
about >=20
// <!! the suitability of this software for any purpose. It is
provided      >=20
// <!! "as is" without express or implied
warranty.                          >=20
//
<!!----------------------------------------------------------------------
>=20

// Author: Dietmar Kuehl dietmar_kuehl@yahoo.com=20
// Title:  for_each() doing some nifty things
// Version: $Id: for_each1.cpp,v 1.1 2001/05/31 01:46:28 kuehl Exp $=20

//
-------------------------------------------------------------------------=
--=20

#include <iostream>
#include <map>
#include <vector>
#include <utility>

//
-------------------------------------------------------------------------=
--=20

namespace foo
{
  //
-------------------------------------------------------------------------
  // The function get_functor() turns an arbitrary unary function or
  // functor into a functor object independent on whether the original
  // is already a functor, a non-member function, a static member
  // function, or a non-static member function. There are probably
  // some conversions still missing but the basics should be
  // obvious...

  template <typename Func> Func get_functor(Func f) { return f; }

  template <typename R, typename S>
  std::pointer_to_unary_function<S, R> get_functor(R (*fun)(S)) {
    return std::pointer_to_unary_function<S, R>(fun);
  }

  template <typename R, typename S>
  std::mem_fun_ref_t<R, S> get_functor(R (S::*member)()) {
    return std::mem_fun_ref_t<R, S>(member);=20
  }
  template <typename R, typename S>
  std::const_mem_fun_ref_t<R, S> get_functor(R (S::*member)() const) {
    return std::const_mem_fun_ref_t<R, S>(member);=20
  }

  //
-------------------------------------------------------------------------
  // Here are a few general purpose property maps:
  // - default_pm_t just provides access to the value returned from an
  //   iterator's operator*(); it can be conveniently created using
  //   the function default_pm() with an iterator object.
  // - first_pm_t provides access to the first member of a pair obtained
  //   from an iterator, ie. to it->first; it can be conveniently
  //   created using the function first_pm() with an iterator object.
  // - second_pm_t provides access to the second member of a pair
obtained
  //   from an iterator, ie. to it->second; it can be conveniently
  //   created using the function second_pm() with an iterator object.
  // Although this stuff looks rather complex, it is actually just some
  // type deduction...

  template <typename T>
  struct default_pm_t
  {
    typedef T& value_type;
  };
  template <typename T, typename It>
  T& get(default_pm_t<T> const&, It it) { return *it; }

  template <typename It>
    default_pm_t<typename std::iterator_traits<It>::value_type>
    default_pm(It) {
      return default_pm_t<typename
std::iterator_traits<It>::value_type>();
  }


  template <typename P>
  struct first_pm_t
  {
    typedef typename P::first_type& value_type;
  };
  template <typename P, typename It>
    typename first_pm_t<P>::value_type
    get(first_pm_t<P> const&, It it) {
      return it->first;
  }

  template <typename It>
    first_pm_t<typename std::iterator_traits<It>::value_type>
    first_pm(It) {=20
      return first_pm_t<typename
std::iterator_traits<It>::value_type>();
  }


  template <typename P>
  struct second_pm_t
  {
    typedef typename P::second_type& value_type;
  };
  template <typename P, typename It>
    typename second_pm_t<P>::value_type
    get(second_pm_t<P> const&, It it) {
      return it->second;
  }

  template <typename It>
    second_pm_t<typename std::iterator_traits<It>::value_type>
    second_pm(It) {=20
      return second_pm_t<typename
std::iterator_traits<It>::value_type>();
  }

  //
-------------------------------------------------------------------------
  // type_traits provides access to various, well, type traits. This
  // version actually just gives access to the unmodified type
  // underlying a given type, ie. the type with all const and volatile
  // modifiers removed.

  template <typename T>
  struct type_traits
  {
    template <typename S> struct unmodified_type_t {
      typedef S type;
    };
    template <typename S> struct unmodified_type_t<S const> {
      typedef S type;
    };
    template <typename S> struct unmodified_type_t<S volatile> {
      typedef S type;
    };
    template <typename S> struct unmodified_type_t<S const volatile> {
      typedef S type;
    };
    typedef typename unmodified_type_t<T>::type unmodified_type;
  };

  //
-------------------------------------------------------------------------
  // select is just a temporary hack to select a type by a type: If
  // the first and the second argument are identical, the result is
  // the third argument. If the first and the forth argument are
  // identical, the result is the fifth argument. If both or neither
  // hold, this deduction just fails. This is just a simple approach
  // for the problem at hand...

  template <typename S, typename T1, typename D1, typename T2, typename
D2>
  struct select;

  template <typename S, typename D1, typename T2, typename D2>
  struct select<S, S, D1, T2, D2> { typedef D1 type; };
  template <typename S, typename T1, typename D1, typename D2>
  struct select<S, T1, D1, S, D2> { typedef D2 type; };

  //
-------------------------------------------------------------------------
  // pm_finder tries to determine a property map fitting the argument
  // of a function given a type returned from an iterator. This is
  // used to implicitly direct a function to a part of the structure
  // returned from an iterator's operator*(). This version only
  // supports three possible choices:
  // - the argument and the value_type match; in this case the default
  //   property map is used.
  // - the value type is a pair whose first element matches the argument
  //   type; in this case a first_pm_t is used.
  // - the value type is a pair whose second element matches the
argument
  //   type; in this case a second_pm_t is used.

  template <typename S, typename T> struct pm_finder {
    typedef default_pm_t<S> property_map;
  };

  template <typename S, typename T1, typename T2>
  struct pm_finder<std::pair<T1, T2>, S> {
    typedef typename type_traits<S>::unmodified_type plain_S;
    typedef typename type_traits<T1>::unmodified_type plain_T1;
    typedef typename type_traits<T2>::unmodified_type plain_T2;

    typedef typename select<plain_S,
      plain_T1, first_pm_t<std::pair<T1, T2> >,
      plain_T2, second_pm_t<std::pair<T2, T2> > >::type property_map;
  };

  template <typename T1, typename T2>
  struct pm_finder<std::pair<T1, T2>, std::pair<T1, T2> > {
    typedef default_pm_t<std::pair<T1, T2> > property_map;
  };
  template <typename T1, typename T2, typename T3, typename T4>
  struct pm_finder<std::pair<T1, T2>, std::pair<T3, T4> > {
    typedef default_pm_t<std::pair<T1, T2> > property_map;
  };

  //
-------------------------------------------------------------------------
  // The function for_each() is the actual goal of this stuff... It is
  // implemented by delegating to for_each_intern() with the functor
  // argument really being converted to a function, even if it is
  // originally some other function. This function in turn is
  // implemented by delegating to another for_each_intern() using
  // property maps with an appropriate property map being selected.

  template <typename It, typename Func, typename PM>
  void for_each_intern(It beg, It end, Func f, PM pm)
  {
    for (; beg !=3D end; ++beg)
      f(get(pm, beg));
  }

  template <typename It, typename Func>
  void for_each_intern(It beg, It end, Func f)
  {
    typedef typename std::iterator_traits<It>::value_type value_type;
    typedef typename Func::argument_type arg_type;
    typedef typename type_traits<arg_type>::unmodified_type type;
    typedef typename pm_finder<value_type, type>::property_map pm;

    for_each_intern(beg, end, f, pm());
  }

  template <typename It, typename Func>
  void for_each(It beg, It end, Func f)
  {
    for_each_intern(beg, end, get_functor(f));
  }
}

//
-------------------------------------------------------------------------=
--=20
// finally a small test for this stuff...

struct val
{
  val(double v =3D 0.0): m_val(v) {}
  void print() { std::cout << "p" << m_val << "\n"; }
  void print_c() const { std::cout << "c" << m_val << "\n"; }
  void print_v() volatile { std::cout << "v" << m_val << "\n"; }
  void print_cv() const volatile { std::cout << "b" << m_val << "\n"; }
  double m_val;
};

void iprint(int i) { std::cout << "g" << i << "\n"; }
void vprint(val v) { std::cout << "g" << v.m_val << "\n"; }
void pprint(std::pair<const int, val> v) {
  std::cout << "g" << "(" << v.first << ", " << v.second.m_val << ")\n";
}

template <typename It>
void call(It beg, It end)
{
  foo::for_each(beg, end, &val::print);
  foo::for_each(beg, end, &val::print_c);
  foo::for_each(beg, end, &val::print_v);
  foo::for_each(beg, end, &val::print_cv);

  foo::for_each(beg, end, pprint);
  foo::for_each(beg, end, iprint);
  foo::for_each(beg, end, vprint);
}

int main()
{
  std::cout.setf(std::ios::fixed, std::ios::floatfield);
  std::cout.precision(1);

  std::map<int, val> m;
  m[1] =3D 3.14;
  call(m.begin(), m.end());

  std::vector<std::pair<int, val> > v;
  v.push_back(std::make_pair(2, 2.71));
  call(v.begin(), v.end());
}

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Joachim Achtzehnter <joachim@kraut.ca>
Date: Sun, 3 Jun 2001 19:58:13 GMT
Raw View
Dietmar Kuehl writes:
>
> I think that
>
>   std::for_each(m.begin(), m.end(), &Object::Draw);
>
> is a pretty good notation

Ok.

> and ... this can be achieved if the algorithms are made more
> flexible ... (...if anybody is interested in this implementation I
> can post it: together with supporting definitions it is about 10kB).

So, this deals with yet another small subset of problems that true
lambda expressions (or closures) would address more generally. You may
argue that the 10kB are in library code and of no concern to ordinary
programmers. But the whole point of generic programming and the STL is
extensibility, hence ordinary programmers may want to write such
algorithms too.

It is also questionable whether such "magic" solutions are the way to
go. It would not be a big deal to explicitly specify the extra step of
dereferencing the value_type's "second" member, if it was possible to
do so without great pain. The code would be much easier to understand
than to rely on algorithms that magically discern how to navigate to
an object to which the functor can be applied.

In that sense, the function composition approach is better. But it is
just too ugly. I don't want to belittle the laudable work invested
into template-based approaches to simulate lambda expressions,
starting with the standard bind1st, and more ambitious recent work
such as the lambda library, but is there any hope that this will ever
be more than a crutch that works in some cases but not others? And
honestly, the syntax is still horrible. There are often nice-looking
examples, but try to use it for real and you usually end up with a
mess.

Andrei is quite right to focus on this point. One of the most
important functions of a programming language is to help the
programmer think clearly about the problem she wants to solve. Simple
concepts must be expressible with simple syntax or the language fails
this goal.

The generic programming paradigm that was introduced into standard C++
with the STL cries out for language support of lambda expressions.

Joachim

--
work:    joachima@netacquire.com (http://www.netacquire.com)
private: joachim@kraut.ca        (http://www.kraut.ca)

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Sun, 3 Jun 2001 20:07:54 GMT
Raw View
Dietmar Kuehl <dietmar_kuehl@yahoo.com> wrote:

: David Greene wrote:
:> for_each(myBigMap.begin(), myBigMap.end(), ...(for_each(...)));

: Close:

:  for_each(myBigMap.begin(), myBigMap.end(), for_eacher(...));

: where 'for_eacher(...)' is a functor along the lines

Why didn't I think of that?  :)  Lack of experience, I guess.  It seems
so bloody obvious now.

: You mentioned that you want to apply it to sequence of pairs: This
: is not covered by the above code but can be added using suitable
: partial specialization. Things could become quite interesting if
: it is supposed to find out whether the sequence is to be obtained
: using the 'first' or the 'second' element of the argument but with
: a little bit of container traits this should be doable. Also, this

Unfortunately, such traits don't exist in the standard, AFAIK.  Should
they be added?  Writing them would not be terribly difficult, but seems
wasteful.

For another approach, would an extra template argument to for_eacher_t be
warranted to provided an object on which to invoke begin()/end()?  It could
default to identity<>.  There's another useful item  not in the standard.
I definitely like the traits method better, but an extra template argument
requires me to write less, as I don't have to write a traits template and
specialize it for map<>, etc.

: addresses the stuff about applying algorithms to whole containers:

:  for_eacher(some_algo)(cont);

I like this, though it's not as clear as sort(list<>).  What's better,
lots of specialized sort()'s or lots of specialized for_eacher()'s?  This
does have the advantage of adding less to the standard.

Speaking of standard extensions, I'm starting at a piece of GNU libstdc++
pre-3.0 header.  It seems they place select1st<>, identity<>, and other
extensions in the std namespace.  Is this allowed?  I'd rather know
when I'm using a non-standard extension and at the same time get the
auto-documentation that a certain piece of code relies on such extensions.

                                      -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: kanze@gabi-soft.de (James Kanze)
Date: Wed, 30 May 2001 21:17:57 GMT
Raw View
"Igor Krasnopeev" <captaincomatoz@mail.ru> wrote in message news:<9eu5ji$3jm$1@josh.sovintel.ru>...

> I often use std::map so it's very annoying for me, that code example below
> is not possible:

> class Object {
>   ...
>   public:
>     void Draw();
>   ...
> };

> map<int, Object*> M;

> for_each(M.begin(), M.end(), mem_fun(&Object::Draw));

> So, I can't use map directly instead of vector or list. And it's all
> because map::iterator returns reference to map::value_type, which is
> pair<const Key, value>.

> So, my suggestion is to provide new type of iterators for map:
> mapped_iterator (const_mapped_iterator & so on), which returns
> reference to mapped_value (this type can easily be implemented
> through map::iterator), and member functions mbegin() & mend(),
> which returns mapped_iterator.

Shades of Java:-).

In the Java collections, it is possible to obtain a "view" of the map
as a set.  Three views are available: entries (which is basically the
pairs), values and keys.  The C++ library seems to eschew such views,
and prefers iterators, but what is to prevent adding the functions:
beginKeys, endKeys, beginValues and endValues, all of which return an
appropriate iterator.

Implementing the new iterators is trivial: just use a copy of the
existing iterator.  All of the functions except operator* and
operator-> forward to the base iterator; these two obtain the pair
from the base iterator, and return a reference or a pointer to the
corresponing element of the pair.

Of course, it is pretty trivial to implement such iterators today,
since they really don't have to be members.

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Igor Krasnopeev" <dark__raven@mail.ru>
Date: Thu, 31 May 2001 13:10:45 GMT
Raw View
| : | for (Map::iterator i = M.begin(); i != M.end(); ++i)
| : | {
| : |     (i->second)->Draw();
| : | }

| :  for (map<int, Object *>::iterator i = M.begin(); i != M.end(); ++i)
| :  {
| :      (i->second)->Draw();
| :  }

| Well, to be _completely_ honest (with both implementations), we'd use
typedefs
| to clean up the syntax somewhat.

Not always :(...

| The compose method does have the advantage of providing a functor which
| can be passed around to lots of places.  That's not relevant to this
| particular example, but it's nice to know how to do it if necessary.

Agreed.

| : for_each(iterator2nd(M.begin()), iterator2nd(M.end()),
| : mem_fun(&Object::Draw));
|
| Yep, iterator adaptors/wrappers are nice.  I think I like this solution
| the most for a simple situation like this.

Yes, I implement and test iterator2nd and now think that it provide the
optimal solution in this case.

--

Igor Krasnopeev


---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Steve Burt" <steve.burt@cursor-system.com>
Date: Thu, 31 May 2001 13:10:45 GMT
Raw View
"David Greene" <greened@eecs.umich.edu> wrote in message
news:9f1qjf$e6m$2@news.eecs.umich.edu...
> Nick Thurn <nickt@kipling.aus.deuba.com> wrote:
> : Andrei Alexandrescu wrote:
> :> "David Greene" <greened@eecs.umich.edu> wrote in message
> :> news:9eufqn$akf$1@news.eecs.umich.edu...
> :> > Use the (non-standard) select2nd functor:
> :> >
> :> >   for_each(M.begin(), M.end(),
> :> >            compose1(mem_fun(&Object::Draw),
> :> >                     select2nd<map<int, Object *>::value_type>()));
> :>
> :> Is anyone else around here thinking (but afraid of saying) that this is
an
> :> abomination? To me it looks like those "obfuscated" contests "write
function
> :> that does this and that without using for and while statements or
> :> recursion".
>
> : Dead right!
>
> Hear, hear!
>
> : However it would be really useful if you could say both:
>
> : for_each(M.begin(),M.end(),&Object::Draw);
> : for_each(M,&Object::Draw);
>
> : with value_type implicit as the usual case. What would make sense for
the
> : key_type? Is this even possible with specialisations? Dunno, but the
syntax
> : is at least clear and simple for the usual case. The tortured syntax
above
> : is a real barrier to using stl algorithms IMHO. Certainly it is easier
>
> I agree with this wholeheartedly.  I've found the STL algorithms to be
> almost useless because of this very issue.  C++ itself is far too
> syntax-happy.  Witness the several calls for a "function definition
> namespace" to avoid continually rewriting complex class names (e.g.
> when templates are involved).
>
Something like the Modula2 'using' keyword would be very nice:

using horrible.long.structure.name {
    blah = 1;
    bleah = 2;
}

instead of:

horrible.long.structure.name.blah = 1;
horrible.long.structure.name.bleah = 2;




---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Igor Krasnopeev" <dark__raven@mail.ru>
Date: Thu, 31 May 2001 15:56:25 GMT
Raw View
| Ok, let me rephrase my comment: I have found little use for the STL
| algorithms in my particular environment.  Of course they are useful
| to a lot of people.  But my experience has been that the thinking
| required to get the syntax, order of arguments, etc. right outweighs
| the saved keystrokes.  Keystrokes are cheap.  Looking up in manuals
| is not.

Yes, but once writen, algorithms make application more easy to support.

| No, I am not a C++ expert.  Far from it.  I'm continually learning.
| Perhaps the STL algorithms and syntax will get easier with time.  I've
| found that that time is much longer than it is in C due to the C++
| syntax.  Now that we have some good books on the standard library,
| I'm sure things will get a bit easier.

Sure, they will, since I have the same problem and it slowly solving.

--

Igor Krasnopeev


---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Igor Krasnopeev" <dark__raven@mail.ru>
Date: Thu, 31 May 2001 15:56:35 GMT
Raw View
| Shades of Java:-).
|
| In the Java collections, it is possible to obtain a "view" of the map
| as a set.  Three views are available: entries (which is basically the
| pairs), values and keys.  The C++ library seems to eschew such views,
| and prefers iterators, but what is to prevent adding the functions:
| beginKeys, endKeys, beginValues and endValues, all of which return an
| appropriate iterator.

[snip]

| Of course, it is pretty trivial to implement such iterators today,
| since they really don't have to be members.

As reverse_iterator, these iterators must be adaptors to iterator, which
retrurns pair, i.e.

template <class Iter> iterator1st {...}; // if *Iter == pair<key, value>&,
*iterator1st<Iter> == key&
template <class Iter> iterator2nd {...}; // if *Iter == pair<key, value>&,
*iterator2nd<Iter> == value&

--

Igor Krasnopeev


---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: Thu, 31 May 2001 15:58:13 GMT
Raw View
Steve Burt wrote:
...
> Something like the Modula2 'using' keyword would be very nice:
>
> using horrible.long.structure.name {
>     blah = 1;
>     bleah = 2;
> }
>
> instead of:
>
> horrible.long.structure.name.blah = 1;
> horrible.long.structure.name.bleah = 2;

That's a bit simpler than the closest C++ equivalent, but there IS a C++
equivalent:

{
 appropriate_type &temp = horrible.long.structure.name;

 temp.blah = 1;
 temp.bleah = 2;
}

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Thu, 31 May 2001 16:44:28 GMT
Raw View
In article <9f3l0k$gsu$1@news.eecs.umich.edu>, David Greene
<greened@eecs.umich.edu> writes
>No, I am not a C++ expert.  Far from it.  I'm continually learning.
>Perhaps the STL algorithms and syntax will get easier with time.  I've
>found that that time is much longer than it is in C due to the C++
>syntax.  Now that we have some good books on the standard library,
>I'm sure things will get a bit easier.

OK, so you have this sequence of items that you want to sort by two
criteria, exactly how do you propose to do it in C? Of course it can be
done, but code design and debugging is orders of magnitude more
expensive than reaching for a manual. As times go by you will find that
you are reaching for that manual less, but if you insist on writing it
in C, you will find your books on Data Structures and Algorithms need
replacing as they fall apart.


Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Marcin 'Qrczak' Kowalczyk" <qrczak@knm.org.pl>
Date: Thu, 31 May 2001 18:51:46 GMT
Raw View
Thu, 31 May 2001 13:10:45 GMT, Steve Burt <steve.burt@cursor-system.com> =
pisze:

> Something like the Modula2 'using' keyword would be very nice:
>=20
> using horrible.long.structure.name {
>     blah =3D 1;
>     bleah =3D 2;
> }

It makes name lookup dependent on types more than currently. This is
evil. Imagine this:

template <class T> void f (const T &x)
{
    using x
    {
        blah =3D 1;
        bleah (2);
    }
}

Do these names refer to x or to entities defined outside? Which
functions named bleah are considered for overloading? Is bleah
considered dependent on the template parameter?

--=20
 __("<  Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/
 \__/
  ^^                      SYGNATURA ZAST=CAPCZA
QRCZAK

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Andrei Alexandrescu" <andrewalex@hotmail.com>
Date: Thu, 31 May 2001 19:32:07 GMT
Raw View
"Igor Krasnopeev" <dark__raven@mail.ru> wrote in message
news:9f2da7$1h1t$1@josh.sovintel.ru...
> | I finally gathered my courage to say: this sucks. I hope I don't sound
> like
> | a conservative moron because I am on record for using C++ for what it
> wasn't
> | intended to do. But I did so only when I saw some benefit in it.
>
> If you don't like it, don't use it.

Joke: three guys on a desert island. They go in search for food. One of them
didn't get any food at all and sees another guy who had been lucky and was
eating some fried meat by a fire. The unlucky one sits by the fire and,
trying to come up with some idea, says: "You know, I really don't like the
other guy." The answer comes: "If you don't like him, don't eat him." :o)

>  for (map<int, Object *>::iterator i = M.begin(); i != M.end(); ++i)
>  {
>      (i->second)->Draw();
>  }
>
> As for me, it's more clearly to understand:
>
> for_each(M.begin(), M.end(),
>             compose1(mem_fun(&Object::Draw),
>                    select2nd<Map::value_type>()));
>
> if I know, that compose1(f, g) does f(g()), simple math function
> composition.

I think one can objectively argue that there is much more information to
absorb to understand the latter form. In addition, the syntax pretty much
obfuscates the intent. There is also a lot of redundancy: you must specify
that &Object::Draw is a member function, and that select2nd selects the
member of type Map::value_type. In other words, you /repeat/ information
that you know, the compiler knows, everybody knows, only because the
compiler needs it in order to figure out the construct.

Plus, you "cheated": you removed my implicit use of a typedef but you did
use it in your sample. Without the typedef in, the little monster looks like
so:

for_each(M.begin(), M.end(),
    compose1(mem_fun(&Object::Draw),
    select2nd<map<int, Object *>::value_type>()));

I would agree that this is a minor point because in a real-world
application, as David Greene said, there is a typedef in place anyway.

I wrote such code once or twice. It took a while. I admit it made me feel
really cool. But to argue that the net result is better (more maintainable,
more expressive, more efficient, etc. etc.) in any way, shape, or form, is
to me like saying that emperor's clothes are wonderful.


Andrei

--
Check out THE C++ Seminar:  3 Days with 5 Experts
http://www.gotw.ca/cpp_seminar



---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Thu, 31 May 2001 21:31:23 GMT
Raw View
Igor Krasnopeev <dark__raven@mail.ru> wrote:
: | Ok, let me rephrase my comment: I have found little use for the STL
: | algorithms in my particular environment.  Of course they are useful
: | to a lot of people.  But my experience has been that the thinking
: | required to get the syntax, order of arguments, etc. right outweighs
: | the saved keystrokes.  Keystrokes are cheap.  Looking up in manuals
: | is not.

: Yes, but once writen, algorithms make application more easy to support.

Clearly, as long as one understands what they do.  Until now, there hasn't
been a good reference on the standard algorithms (to my knowledge).
Now that we have them, things should get easier.  A C++ newbie (and
even some well-seasoned users!) sees the standard algorithms as "cute" tricks,
not unlike the contortions of operator().

Does this mean the algorithms are useless?  Of course not!  But some of
the syntax could be cleaned up and/or things could be made slightly
more consistent throughout the standard library.

                                -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Thu, 31 May 2001 21:58:02 GMT
Raw View
James Kuyper Jr. <kuyper@wizard.net> wrote:

:> Something like the Modula2 'using' keyword would be very nice:
:>
:> using horrible.long.structure.name {
:>     blah = 1;
:>     bleah = 2;
:> }

: That's a bit simpler than the closest C++ equivalent, but there IS a C++
: equivalent:

: {
:        appropriate_type &temp = horrible.long.structure.name;

:        temp.blah = 1;
:        temp.bleah = 2;
: }

This doesn't solve the original problem of member (function) definitions.

                           -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Thu, 31 May 2001 21:58:41 GMT
Raw View
Francis Glassborow <francis.glassborow@ntlworld.com> wrote:
: In article <9f3l0k$gsu$1@news.eecs.umich.edu>, David Greene
: <greened@eecs.umich.edu> writes

: OK, so you have this sequence of items that you want to sort by two
: criteria, exactly how do you propose to do it in C? Of course it can be
: done, but code design and debugging is orders of magnitude more
: expensive than reaching for a manual. As times go by you will find that
: you are reaching for that manual less, but if you insist on writing it
: in C, you will find your books on Data Structures and Algorithms need
: replacing as they fall apart.

You misunderstand me.  I most definitely do NOT advocate (re)writing things
in C, C++ or any other language!  The main issue I have is with some of the
syntax, or maybe it's a simple matter of naming.  When I look at some of the
uses of the standard algorithms, especially with bind2nd, etc., I often have
to stop and take a good look at that piece of code to figure out what is
going on.  The binders are notoriously bad when used on member functions,
as in this example from Bjarne's book:

for_each(ls.begin(), ls.end(), bind2nd(mem_fun(&Shape::rotate), angle));

Why should I know to use bind2nd when invoking a member function?  Yes,
_I_ know why, but most mid-level C++ programmers do not or at least need
to stop and think about it.

Before you argue that my complaints are with the binders/adaptors/whatever,
bear in mind that it's all tied together in a (mostly) elegant package.

In fact, it is my opinion that the standard library provides a wonderful
(if not complete and totally consistent) set of tools for the C++ programmer.
Certainly it is one of the richest standard libraries around and I am
thankful every day for its existence.  :)

                              -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Thu, 31 May 2001 22:39:29 GMT
Raw View
Andrei Alexandrescu <andrewalex@hotmail.com> wrote:
: "Igor Krasnopeev" <dark__raven@mail.ru> wrote in message

:> As for me, it's more clearly to understand:
:>
:> for_each(M.begin(), M.end(),
:>             compose1(mem_fun(&Object::Draw),
:>                    select2nd<Map::value_type>()));
:>
:> if I know, that compose1(f, g) does f(g()), simple math function
:> composition.

: I think one can objectively argue that there is much more information to
: absorb to understand the latter form. In addition, the syntax pretty much
: obfuscates the intent. There is also a lot of redundancy: you must specify
: that &Object::Draw is a member function, and that select2nd selects the
: member of type Map::value_type. In other words, you /repeat/ information
: that you know, the compiler knows, everybody knows, only because the
: compiler needs it in order to figure out the construct.

Absolutely.  This is one of my big beefs with C++ (or maybe it's just my
use of it.  :)).  There seems to be an awful lot of redundancy.  In fact,
when I wrote the above code, I thought to myself how stupid it is that I
have to specify Map::value_type.

In particular, I get very annoyed with having to specify the (complex
templated) types of declared variables (or return types) when the compiler
should be able to figure it out.  typedef'ing only helps to a degree -- if
I only use a particular set of arguments to a template once, they don't help
much.  I understand their need in declarations, but why definitions?  Heck,
since we often have the declaration available in the same translation
unit as the definition, why do we need to specify argument and return types
at all (well, except for overloaded stuff)?  No, I'm not advocating a
change, but just pointing out some of the redundancy that exists.

Another case where this happens is class derivation (or composition).  When
I derive from a template class, I often like to "typedef-promote" the
typedefs from the base into the dervied (_a_la_ the standard library).  I've
run across several cases where the number of typedefs is small, so that
typedef'ing the base class doesn't gain much.  Is there a better way to do
this?

: I wrote such code once or twice. It took a while. I admit it made me feel
: really cool. But to argue that the net result is better (more maintainable,
: more expressive, more efficient, etc. etc.) in any way, shape, or form, is
: to me like saying that emperor's clothes are wonderful.

I would agree in this case.  However, is this an instance where the
standard library has failed?  As Igor and others have pointed out,
it would be nice to have some sort of facility to easily do this.  Sure,
it's simple to write, but so is "for_each."  :)

                           -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Mirek Fidler" <cxl@volny.cz>
Date: Fri, 1 Jun 2001 08:16:54 GMT
Raw View
> However it would be really useful if you could say both:
>
> for_each(M.begin(),M.end(),&Object::Draw);
> for_each(M,&Object::Draw);

    It would be even better if you could say

    for_each(M, Draw);

    (inside Object:: scope, of course)

   Does anybody know why pointer to member syntax is made so verbose ? I do
not care about for_each, but is is uncomfortable when you need to define GUI
callbacks...

Mirek





---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Igor Krasnopeev <dark__raven@mail.ru>
Date: Fri, 1 Jun 2001 16:46:14 GMT
Raw View
Andrei Alexandrescu wrote:

>"Igor Krasnopeev" <dark__raven@mail.ru> wrote in message
>news:9f2da7$1h1t$1@josh.sovintel.ru...
>> As for me, it's more clearly to understand:
>>
>> for_each(M.begin(), M.end(),
>>             compose1(mem_fun(&Object::Draw),
>>                    select2nd<Map::value_type>()));
>>
>> if I know, that compose1(f, g) does f(g()), simple math function
>> composition.
>
>I think one can objectively argue that there is much more information to
>absorb to understand the latter form. In addition, the syntax pretty much
>obfuscates the intent. There is also a lot of redundancy: you must specify
>that &Object::Draw is a member function, and that select2nd selects the
>member of type Map::value_type.

And how you offers to avoid this redundancy?

--
Igor Krasnopeev

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Dietmar Kuehl <dietmar_kuehl@yahoo.com>
Date: Fri, 1 Jun 2001 17:25:48 GMT
Raw View
Hi,
Igor Krasnopeev wrote:
> And how you offers to avoid this redundancy?

I think that

  std::for_each(m.begin(), m.end(), &Object::Draw);

is a pretty good notation and as I pointed out in another article in
this thread this can be achieved if the algorithms are made more
flexible (in this article I only outlined how to go about this and
had no implementation; if anybody is interested in this implementation
I can post it: together with supporting definitions it is about 10kB).

Basically, since the STL was introduced we gained a lot of insight
what can be done with templates to support the user. Many of the
techniques now developed were not yet known when the STL interface was
originally designed. I think it makes sense to make the STL entities
easier to use without breaking the existing interface. Allowing the
above use of 'for_each()', even if the 'value_type' of the iterator
used is not 'Object', makes the interface easier and it is possible
to allow selection of the member by the compiler. If the algorithm
cannot do the selection (due to failed matches or ambigious matches),
it is still an error but simple cases can be handled using simple
code.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Mirek Fidler" <cxl@volny.cz>
Date: Fri, 1 Jun 2001 19:13:04 GMT
Raw View
> I often use std::map so it's very annoying for me, that code example below
> is not possible:
>
> class Object {
>   ...
>   public:
>     void Draw();
>   ...
> };
>
> map<int, Object*> M;
>
> for_each(M.begin(), M.end(), mem_fun(&Object::Draw));
>
> So, I can't use map directly instead of vector or list.
> And it's all because map::iterator returns reference to map::value_type,
> which is pair<const Key, value>.

    Heh. These examples remind me how STL is crippled again and again.

    Using our library, above code would look like this:

ArrayMap<int, Object> M;

for(int i = 0; i < M.GetCount(); i++)
    M[i].Draw();

    Not a problem at all :) Best thing that could be done to STL is to
remove it from standard (I know it is impossible). It just blocks place for
better solutions ("New container set ? Why ? We have STL in standard !").

Mirek



---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: jk@steel.orel.ru (Eugene Karpachov)
Date: Fri, 1 Jun 2001 19:14:25 GMT
Raw View
Tue, 29 May 2001 19:12:30 GMT Andrei Alexandrescu =CE=C1=D0=C9=D3=C1=CC:
>Compare the noise above with the good ol' for loop:
>
>for (Map::iterator i =3D M.begin(); i !=3D M.end(); ++i)
      ^^^ kind of cheating, isn't it? :)=20
>{
>    (i->second)->Draw();
>}

With typeof it would be even better:

for( typeof(M)::iterator i =3D M.begin(); i !=3D M.end(); ++i) {
        // ...

>
>Makes you live C++. And *please* spare me the argument that that thing
>precomputes end() and the for loop doesn't :o).

Why to spare?

for( typeof(M)::iterator i =3D M.begin(), e =3D M.end(); i !=3D e; ++i) {
        // ...
}

--=20
jk

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Dennis Yelle <dy1951@eskimo.com>
Date: Fri, 1 Jun 2001 19:26:00 GMT
Raw View
Eugene Karpachov wrote:
>=20
> Tue, 29 May 2001 19:12:30 GMT Andrei Alexandrescu IAD=C9OAI:
> >Compare the noise above with the good ol' for loop:
> >
> >for (Map::iterator i =3D M.begin(); i !=3D M.end(); ++i)
>       ^^^ kind of cheating, isn't it? :)
> >{
> >    (i->second)->Draw();
> >}
>=20
> With typeof it would be even better:
>=20
> for( typeof(M)::iterator i =3D M.begin(); i !=3D M.end(); ++i) {
>         // ...
>=20
> >
> >Makes you live C++. And *please* spare me the argument that that thing
> >precomputes end() and the for loop doesn't :o).
>=20
> Why to spare?
>=20
> for( typeof(M)::iterator i =3D M.begin(), e =3D M.end(); i !=3D e; ++i)=
 {
>         // ...
> }

Why not just make this legal:

for( M::iterator i =3D M.begin(), e =3D M.end(); i !=3D e; ++i) {
        // ...
}

Dennis Yelle
--=20
I am a computer programmer and I am looking for a job.
There is a link to my resume here: =20
http://www.eskimo.com/~dy1951/

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Dietmar Kuehl <dietmar_kuehl@yahoo.com>
Date: Fri, 1 Jun 2001 20:49:44 GMT
Raw View
Mirek Fidler wrote:

>     Using our library, above code would look like this:
>
> ArrayMap<int, Object> M;
>
> for(int i = 0; i < M.GetCount(); i++)
>     M[i].Draw();

Using today's knowledge of templates, STL could easily be changed to
allow

  std::for_each(M.begin(), M.end(), &Object::Draw);

Not a problem at all :-) (yes, I know that 'Object' is not the value
type
of the map's iterator) ... and, the best thing about it, if someone as
a clever idea how to iterate over a containers (and there are ideas
which
can make a major difference, eg. a factor of two if there is only a
simple operation in the functor; of course, in the "Draw()" case, the
dominant part will probably be the "Draw()" function, not the iteration;
just adding an offset to the members will fall in the category I'm
talking
of) it can be used underneath. Also, if it turns out that it is more
reasonable to use a list structure underneath, eg. because most
operations
are insertions and removal of objects but only rare uses of iteration,
the
code has not to be rewritten.

>     Not a problem at all :) Best thing that could be done to STL is to
> remove it from standard (I know it is impossible). It just blocks place for
> better solutions ("New container set ? Why ? We have STL in standard !").

Although I guess you are not alone with thinking that your library is a
better approach, I'm pretty sure that you are wrong in this thinking!
The
small portion of the interface you showed indicates that your library is
inferior to STL by a rather wide margin. Without seeing the your library
it is, of course, impossible to tell for sure but given a reasonable
measure of library quality (if there is demand I can outline what I
consider appropriate in this category) I would bet big bucks against
your
library from just seeing the small excerpt you have given!

I'm not claiming that STL is perfect or that it is the best library.
However, the underlying principle is hard to beat. The only way STL
failed
is to be as STL-like as it could be! This failure can and should be
corrected, probably at the same time as making use of STL easier.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Fri, 1 Jun 2001 21:14:44 GMT
Raw View
Dietmar Kuehl <dietmar_kuehl@yahoo.com> wrote:

: The only way STL failed is to be as STL-like as it could be! This failure
: can and should be corrected, probably at the same time as making use of
: STL easier.

You have said here much better what I've been trying to say in my last few
messages.  Thanks!  :)

                                 -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Fri, 1 Jun 2001 21:25:29 GMT
Raw View
Igor Krasnopeev <captaincomatoz@mail.ru> wrote:

: for_each(M.begin(), M.end(), mem_fun(&Object::Draw));

: So, I can't use map directly instead of vector or list.
: And it's all because map::iterator returns reference to map::value_type,
: which is pair<const Key, value>.

To take this to another level, is it possible to do a for_each of
a for_each (to implement nested loops)?  The main problem I see is
how to pass the begin and end iterators from the "current" object
in the outer loop to the for_each representing the inner loop.

To make it concrete, suppose I have:

typedef map<key2, value> innerMap;
typedef map<key1, innerMap> outerMap;
outerMap myBigMap;

for_each(myBigMap.begin(), myBigMap.end(), ...(for_each(...)));

Where ... represents things like binders or composers that might be
needed.

I just used maps because Igor likes them.  :)  Any container would
face the same problems.  Is there a nice way to do this or am I
thinking about this the wrong way?

                              -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Dietmar Kuehl <dietmar_kuehl@yahoo.com>
Date: Fri, 1 Jun 2001 23:40:07 GMT
Raw View
Hi,
David Greene wrote:
> for_each(myBigMap.begin(), myBigMap.end(), ...(for_each(...)));

Close:

  for_each(myBigMap.begin(), myBigMap.end(), for_eacher(...));

where 'for_eacher(...)' is a functor along the lines

  template <typename Func>
  struct for_eacher_t {
    for_eacher_t(Func f): m_f(f) {}
    template <typename T>
    void operator()(T& t) const { for_each(t.begin(), t.end(), f); }
  };
  template <typename Func>
  for_eacher_t<Func> for_eacher(Func f) { return for_eacher_t(f); }

You mentioned that you want to apply it to sequence of pairs: This
is not covered by the above code but can be added using suitable
partial specialization. Things could become quite interesting if
it is supposed to find out whether the sequence is to be obtained
using the 'first' or the 'second' element of the argument but with
a little bit of container traits this should be doable. Also, this
addresses the stuff about applying algorithms to whole containers:

  for_eacher(some_algo)(cont);
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Andrei Alexandrescu" <andrewalex@hotmail.com>
Date: Sat, 2 Jun 2001 18:55:44 GMT
Raw View
"Igor Krasnopeev" <dark__raven@mail.ru> wrote in message
news:t4bfhtcro3bgv4ht3a7dbpc0mb64455qic@4ax.com...
> Andrei Alexandrescu wrote:
> >I think one can objectively argue that there is much more information to
> >absorb to understand the latter form. In addition, the syntax pretty much
> >obfuscates the intent. There is also a lot of redundancy: you must
specify
> >that &Object::Draw is a member function, and that select2nd selects the
> >member of type Map::value_type.
>
> And how you offers to avoid this redundancy?

I offer to avoid this redundancy by using the good old for loop. I don't
understand why I'd have to obstinate to use for_each if it's harder to
write, harder to read, less maintainable, more redundant.

Of course, there are simpler situations in which for_each does make sense.
However, that doesn't scale: as soon as I try to change a simple for_each
into a slightly more elaborate for_each, the syntax explodes. Not so with
the good old for statement... for_each has you fall in love with for.

As Dietmar points out, we can overload for_each for member functions... the
scaling problem stays, but I would enjoy using it sometimes.


Andrei

--
Check out THE C++ Seminar:  3 Days with 5 Experts
http://www.gotw.ca/cpp_seminar



---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: nickt@kipling.aus.deuba.com (Nick Thurn)
Date: Wed, 30 May 2001 01:57:35 GMT
Raw View
Andrei Alexandrescu wrote:
> "David Greene" <greened@eecs.umich.edu> wrote in message
> news:9eufqn$akf$1@news.eecs.umich.edu...
> > Use the (non-standard) select2nd functor:
> >
> >   for_each(M.begin(), M.end(),
> >            compose1(mem_fun(&Object::Draw),
> >                     select2nd<map<int, Object *>::value_type>()));
>
> Is anyone else around here thinking (but afraid of saying) that this is an
> abomination? To me it looks like those "obfuscated" contests "write function
> that does this and that without using for and while statements or
> recursion".
>
> I finally gathered my courage to say: this sucks. I hope I don't sound like
> a conservative moron because I am on record for using C++ for what it wasn't
> intended to do. But I did so only when I saw some benefit in it.
>
> Compare the noise above with the good ol' for loop:
>
> for (Map::iterator i = M.begin(); i != M.end(); ++i)
> {
>     (i->second)->Draw();
> }
>
> Makes you live C++. And *please* spare me the argument that that thing
> precomputes end() and the for loop doesn't :o).
>
Dead right!

However it would be really useful if you could say both:

 for_each(M.begin(),M.end(),&Object::Draw);
 for_each(M,&Object::Draw);

with value_type implicit as the usual case. What would make sense for the
key_type? Is this even possible with specialisations? Dunno, but the syntax
is at least clear and simple for the usual case. The tortured syntax above
is a real barrier to using stl algorithms IMHO. Certainly it is easier
to write the for loops over and over than earn PhD's in decyphering
compiler errors ;-). David's a brave man if he actually uses it.

cheers
Nick

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Wed, 30 May 2001 10:44:49 GMT
Raw View
Nick Thurn <nickt@kipling.aus.deuba.com> wrote:
: Andrei Alexandrescu wrote:
:> "David Greene" <greened@eecs.umich.edu> wrote in message
:> news:9eufqn$akf$1@news.eecs.umich.edu...
:> > Use the (non-standard) select2nd functor:
:> >
:> >   for_each(M.begin(), M.end(),
:> >            compose1(mem_fun(&Object::Draw),
:> >                     select2nd<map<int, Object *>::value_type>()));
:>
:> Is anyone else around here thinking (but afraid of saying) that this is an
:> abomination? To me it looks like those "obfuscated" contests "write function
:> that does this and that without using for and while statements or
:> recursion".

: Dead right!

Hear, hear!

: However it would be really useful if you could say both:

:  for_each(M.begin(),M.end(),&Object::Draw);
:  for_each(M,&Object::Draw);

: with value_type implicit as the usual case. What would make sense for the
: key_type? Is this even possible with specialisations? Dunno, but the syntax
: is at least clear and simple for the usual case. The tortured syntax above
: is a real barrier to using stl algorithms IMHO. Certainly it is easier

I agree with this wholeheartedly.  I've found the STL algorithms to be
almost useless because of this very issue.  C++ itself is far too
syntax-happy.  Witness the several calls for a "function definition
namespace" to avoid continually rewriting complex class names (e.g.
when templates are involved).

Template specialization is particularly difficult, IMHO.

: compiler errors ;-). David's a brave man if he actually uses it.

Not that brave...I took Igor's question more as a simple puzzler than
as a real-world situation.

                        -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Wed, 30 May 2001 10:47:23 GMT
Raw View
Hi,
nickt@kipling.aus.deuba.com (Nick Thurn) wrote in message news:
> However it would be really useful if you could say both:
>
>  for_each(M.begin(),M.end(),&Object::Draw);

If the value type of 'M.begin()' is 'Object' this can be made to work although
I consider

        for_each(M.begin(), M.end(), mem_fun_ref(&Object::Draw));

also an acceptable solution. Since the value type of 'M.begin()' is not at all
'Object' we need to select the this type first. Although not possible with the
current definition of STL this can be made to work like this:

   for_each(M.begin(), M.end(), mem_fun_ref(&Object::Draw), second_pm(M.end());

Basically, this assumes a 'for_each()' function which operates using a
"property map" rather then 'operator*()' to access the properties identified
by an iterator:

  template <typename It, typename Func, typename PM>
  void for_each(It beg, It end, Func f, PM pm) {
    for (; beg != end; ++beg)
      f(get(pm, it)); // without property maps this line is 'f(*it)'
  }

... and here is how the above mentioned property map can be implemented:

  template <typename P>
  struct second_pm_t {
    typedef typename P::second_type value_type;
  };
  template <typename PM, typename It>
  typename PM::value_type const& get(PM const&, It it) {
    return it->second;
  }
  template <typename It>
  second_pm_t<typename std::iterator_traits<It>::value_type> second_pm(It) {
    return second_pm_t<typename std::iterator_traits<It>::value_type>();
  }

This code is not tested but I think it should work. Of course, this is just a
trivial version of both 'for_each()' and the property map: Both are probably
implemented just in terms of more generic stuff.

>  for_each(M,&Object::Draw);

Like mentioned above, I don't consider it too bad using 'mem_fun()' but if
necessary, functions like this can be provided:

   template <typename Cont, typename Func>
   void for_each(Cont const& cont, Func f) {
     for_each(cont.begin(), cont.end(), f);
   }
   template <typename Cont, typename RC, typename S>
   void for_each(Cont const& cont, RC (S::*member)()) {
     for_each(cont, mem_fun_ref(member));
   }

This is basically just a question of what convenience layers are provided to
call the actual function underneath. Again, of course, it is necessary to
provide the property map for appropriate selection of the object to which the
member functions are applied.

Hm... Actually, we could try to play tricks on this one: If the iterator
value type does not match the type on which the member is applied, we could
try other alternatives to find a match if it is unique and come up with an
appropriate property map implicitly. That is, we would do something like this

  template <typename It, typename RC, typename S>
  void for_each(It beg, It end, (RC S::*member)()) {
    for_each(beg, end, mem_fun_ref(member),
             typename
             pm_finder<It, typename std::iterator_traits<It>::value_type, S>
               ::property_map());
  }

where 'pm_finder' is a class whose sole purpose is to figure out a suitable
property map allowing application of 'member' to some part of the iterator's
value type. Here is a rough sketch how this could look like:

  // the general version cannot be used...
  template <typename It, typename S, typename T> struct pm_finder;

  // the member can be applied directly to the value type:
  template <typename It, typename S> struct pm_finder<It, S, S> {
    typedef default_pm_t<It> property_map;
  };

  // value_type is a pair and we can apply the member to one of the elements:
  template <typename It, typename S, typename T>
  struct pm_finder<It, std::pair<S, T>, S> {
    typedef first_pm_t<std::pair<S, T> > property_map;
  };
  template <typename It, typename S, typename T>
  struct pm_finder<It, std::pair<S, T>, T> {
    typedef second_pm_t<std::pair<S, T> > property_map;
  };
  // but it may also be ambigious: this is an error:
  template <typename It, tpyename S> struct pm_finder<It, std::pair<S, S>, S>;

Currently I don't have a compiler with me on which I can test stuff like this.
I will try this out later today but I think the basic approach is workable.
What this comes down to is this: We can probably come up with a much better
interface at the cost of lots of work at the library side. Seems to be an
acceptable trade to me: Have the library implementer do the work for the
benefit of the whole user community.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Wed, 30 May 2001 15:40:37 GMT
Raw View
In article <9f1qjf$e6m$2@news.eecs.umich.edu>, David Greene
<greened@eecs.umich.edu> writes
>I agree with this wholeheartedly.  I've found the STL algorithms to be
>almost useless because of this very issue.

Yet many of us find them very useful:) I suggest what you meant was that
they were of little use for some types of programming task. So what is
new? All code is written in some problem domain, even if some is only in
a programming domain.


Francis Glassborow      ACCU
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Wed, 30 May 2001 16:31:14 GMT
Raw View
Andrei Alexandrescu <andrewalex@hotmail.com> wrote:
: "David Greene" <greened@eecs.umich.edu> wrote in message
: news:9eufqn$akf$1@news.eecs.umich.edu...
:> Use the (non-standard) select2nd functor:
:>
:>   for_each(M.begin(), M.end(),
:>            compose1(mem_fun(&Object::Draw),
:>                     select2nd<map<int, Object *>::value_type>()));

: Is anyone else around here thinking (but afraid of saying) that this is an
: abomination? To me it looks like those "obfuscated" contests "write function
: that does this and that without using for and while statements or
: recursion".

I'm not afraid.  I'll be the first to join you!  :)

Let it be said right now that I've NEVER written code like this.  I was
responding to a question about how to invoke a method on the second object
of a map pair in a for_each statement.  I assume there was some reason
this was desired.

: I finally gathered my courage to say: this sucks. I hope I don't sound like
: a conservative moron because I am on record for using C++ for what it wasn't
: intended to do. But I did so only when I saw some benefit in it.

It does suck.  It's much too confusing.

: Compare the noise above with the good ol' for loop:

: for (Map::iterator i = M.begin(); i != M.end(); ++i)
: {
:     (i->second)->Draw();
: }

This is, in fact, how I've always written it, save for a few formatting
issues that I will defend religiously.  :)

                          -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Igor Krasnopeev" <dark__raven@mail.ru>
Date: Wed, 30 May 2001 16:32:58 GMT
Raw View
| I finally gathered my courage to say: this sucks. I hope I don't sound
like
| a conservative moron because I am on record for using C++ for what it
wasn't
| intended to do. But I did so only when I saw some benefit in it.

If you don't like it, don't use it.

| Compare the noise above with the good ol' for loop:
|
| for (Map::iterator i = M.begin(); i != M.end(); ++i)
| {
|     (i->second)->Draw();
| }

To be completely honest, let's write:

 for (map<int, Object *>::iterator i = M.begin(); i != M.end(); ++i)
 {
     (i->second)->Draw();
 }

As for me, it's more clearly to understand:

for_each(M.begin(), M.end(),
            compose1(mem_fun(&Object::Draw),
                   select2nd<Map::value_type>()));

if I know, that compose1(f, g) does f(g()), simple math function
composition.

example with iterators is pretty good too:

for_each(iterator2nd(M.begin()), iterator2nd(M.end()),
mem_fun(&Object::Draw));


--

Igor Krasnopeev


---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Wed, 30 May 2001 20:49:38 GMT
Raw View
Francis Glassborow <francis.glassborow@ntlworld.com> wrote:
: In article <9f1qjf$e6m$2@news.eecs.umich.edu>, David Greene
: <greened@eecs.umich.edu> writes
:>I agree with this wholeheartedly.  I've found the STL algorithms to be
:>almost useless because of this very issue.

: Yet many of us find them very useful:) I suggest what you meant was that
: they were of little use for some types of programming task. So what is
: new? All code is written in some problem domain, even if some is only in
: a programming domain.

Ok, let me rephrase my comment: I have found little use for the STL
algorithms in my particular environment.  Of course they are useful
to a lot of people.  But my experience has been that the thinking
required to get the syntax, order of arguments, etc. right outweighs
the saved keystrokes.  Keystrokes are cheap.  Looking up in manuals
is not.

No, I am not a C++ expert.  Far from it.  I'm continually learning.
Perhaps the STL algorithms and syntax will get easier with time.  I've
found that that time is much longer than it is in C due to the C++
syntax.  Now that we have some good books on the standard library,
I'm sure things will get a bit easier.

                                        -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Wed, 30 May 2001 16:04:49 CST
Raw View
Igor Krasnopeev <dark__raven@mail.ru> wrote:

: | Compare the noise above with the good ol' for loop:
: |
: | for (Map::iterator i = M.begin(); i != M.end(); ++i)
: | {
: |     (i->second)->Draw();
: | }

: To be completely honest, let's write:

:  for (map<int, Object *>::iterator i = M.begin(); i != M.end(); ++i)
:  {
:      (i->second)->Draw();
:  }

Well, to be _completely_ honest (with both implementations), we'd use typedefs
to clean up the syntax somewhat.

: As for me, it's more clearly to understand:

: for_each(M.begin(), M.end(),
:             compose1(mem_fun(&Object::Draw),
:                    select2nd<Map::value_type>()));

: if I know, that compose1(f, g) does f(g()), simple math function
: composition.

I suspect the form preferred depends on one's level of mathematical
background.  I find the "compose" arguments backward, though, since it
looks (to me) like we're invoking Draw and selecting the second element
of the result.  That may just be me, though.

The compose method does have the advantage of providing a functor which
can be passed around to lots of places.  That's not relevant to this
particular example, but it's nice to know how to do it if necessary.

: example with iterators is pretty good too:

: for_each(iterator2nd(M.begin()), iterator2nd(M.end()),
: mem_fun(&Object::Draw));

Yep, iterator adaptors/wrappers are nice.  I think I like this solution
the most for a simple situation like this.

                         -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Nicola Musatti <objectway@divalsim.it>
Date: Tue, 29 May 2001 12:43:19 GMT
Raw View

Igor Krasnopeev wrote:
>
> Hello, all.
>
> I often use std::map so it's very annoying for me, that code example below
> is not possible:
>
> class Object {
>   ...
>   public:
>     void Draw();
>   ...
> };
>
> map<int, Object*> M;
>
> for_each(M.begin(), M.end(), mem_fun(&Object::Draw));
>
> So, I can't use map directly instead of vector or list.
> And it's all because map::iterator returns reference to map::value_type,
> which is pair<const Key, value>.
>
> So, my suggestion is to provide new type of iterators for map:
> mapped_iterator (const_mapped_iterator & so on), which returns reference to
> mapped_value (this type can easily be implemented through map::iterator),
> and member functions mbegin() & mend(), which returns mapped_iterator.
[...]

My own approach to this problem is to define an iterator adaptor that
extracts the second element from the pair returned by the adaptee. I'm
not offering my implementation because it is only half tested; maybe
something similar could be considered for inclusion in the Boost
libraries.

Best regards,
Nicola Musatti

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Igor Krasnopeev" <dark__raven@mail.ru>
Date: Tue, 29 May 2001 12:43:34 GMT
Raw View
| Use the (non-standard) select2nd functor:
|   for_each(M.begin(), M.end(),
|            compose1(mem_fun(&Object::Draw),
|                     select2nd<map<int, Object *>::value_type>()));

Yes, I was think about select2nd too, but could not image how to apply it,
so thanks with hint about compose1.

But the question still is - it's not standard extension.

--

Igor Krasnopeev


---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Pete Becker <petebecker@acm.org>
Date: Tue, 29 May 2001 17:22:20 GMT
Raw View
Igor Krasnopeev wrote:
>
> | Use the (non-standard) select2nd functor:
> |   for_each(M.begin(), M.end(),
> |            compose1(mem_fun(&Object::Draw),
> |                     select2nd<map<int, Object *>::value_type>()));
>
> Yes, I was think about select2nd too, but could not image how to apply it,
> so thanks with hint about compose1.
>
> But the question still is - it's not standard extension.
>

That's a statement, not a question. If you need it, write it.

--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Andrei Alexandrescu" <andrewalex@hotmail.com>
Date: Tue, 29 May 2001 19:12:30 GMT
Raw View
"David Greene" <greened@eecs.umich.edu> wrote in message
news:9eufqn$akf$1@news.eecs.umich.edu...
> Use the (non-standard) select2nd functor:
>
>   for_each(M.begin(), M.end(),
>            compose1(mem_fun(&Object::Draw),
>                     select2nd<map<int, Object *>::value_type>()));

Is anyone else around here thinking (but afraid of saying) that this is an
abomination? To me it looks like those "obfuscated" contests "write function
that does this and that without using for and while statements or
recursion".

I finally gathered my courage to say: this sucks. I hope I don't sound like
a conservative moron because I am on record for using C++ for what it wasn't
intended to do. But I did so only when I saw some benefit in it.

Compare the noise above with the good ol' for loop:

for (Map::iterator i = M.begin(); i != M.end(); ++i)
{
    (i->second)->Draw();
}

Makes you live C++. And *please* spare me the argument that that thing
precomputes end() and the for loop doesn't :o).


Andrei

--
Check out THE C++ Seminar:  3 Days with 5 Experts
http://www.gotw.ca/cpp_seminar



---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Tue, 29 May 2001 19:14:14 GMT
Raw View
Igor Krasnopeev <dark__raven@mail.ru> wrote:
: | Use the (non-standard) select2nd functor:
: |   for_each(M.begin(), M.end(),
: |            compose1(mem_fun(&Object::Draw),
: |                     select2nd<map<int, Object *>::value_type>()));

: Yes, I was think about select2nd too, but could not image how to apply it,
: so thanks with hint about compose1.

You're welcome.  The STL lacks a lot of useful composition functors.  Boost
provides them.

: But the question still is - it's not standard extension.

True enough.  You can use the Boost libraries, or feel free to take my
implementation.  If the standard doesn't define it, we just have to
write the code or snarf it from others.  :)

                                 -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Igor Krasnopeev" <dark__raven@mail.ru>
Date: Tue, 29 May 2001 19:15:57 GMT
Raw View
| That's a statement, not a question. If you need it, write it.

If is frequenly used by many people, than it may be considered to add to
standard.

--

Igor Krasnopeev



---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Tue, 29 May 2001 19:17:33 GMT
Raw View
Hi,
"Igor Krasnopeev" <dark__raven@mail.ru> wrote in message news:<9evpae$55l$1@josh.sovintel.ru>...
> | Use the (non-standard) select2nd functor:
> |   for_each(M.begin(), M.end(),
> |            compose1(mem_fun(&Object::Draw),
> |                     select2nd<map<int, Object *>::value_type>()));
>
> Yes, I was think about select2nd too, but could not image how to apply it,
> so thanks with hint about compose1.
>
> But the question still is - it's not standard extension.

We are talking about standard extensions, aren't we? Thus, the question is not
whether it is in the current standard but rather how to address problems like
this in the next round. Using a different kind of iterator or a suitable
functor are just about two options. There are other possibilities (eg. my
favoured approach for issues like this are "property maps" because they solve
a more general problem), too.

Actually, there are multiple issues mentioned here:

- First of all, the current library is rather incomplete and is, in particular,
  lacking support for important functors. Things like 'compose1' and 'identity'
  come to mind in this area.

- The next thing is whether the currently used abstraction is complete and
  sufficient. Since there are adaptors and/or functors which can cope with this
  problem, it is probably sufficient in this case. There are, however, others
  where this is not the case ("proxy containers" come to mind).

- Should the standard containers provide convenience functions to adapt to the
  certain situations? They already do things like this with respect to
  reversing a container ('rbegin()' and 'rend()' returning reverse iterators).
  Is this an error or should there be other convenience members to? If the
  functions are implemented global, they can be implemented generically and in
  some kind of "added" to the container. Are the concepts used thus the right
  ones or do they have to undergo some change? (I think they should be adapted
  with retaining the current interface as a sort of "convenience and
  compatibility layer").

My guess is that the next standard would probably support more than one
approach how to address this problem, eg. appropriate functors and something
like property maps. I don't care that much for the functors but I definitely
care for property maps :-)
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Julian Smith" <jules@REMOVETHIS.op59.net>
Date: Tue, 29 May 2001 23:50:48 GMT
Raw View
"Andrei Alexandrescu" <andrewalex@hotmail.com> wrote in message
news:9f0qdr$1jvqa$1@ID-14036.news.dfncis.de...
> "David Greene" <greened@eecs.umich.edu> wrote in message
> news:9eufqn$akf$1@news.eecs.umich.edu...
> > Use the (non-standard) select2nd functor:
> >
> >   for_each(M.begin(), M.end(),
> >            compose1(mem_fun(&Object::Draw),
> >                     select2nd<map<int, Object *>::value_type>()));
>
> Is anyone else around here thinking (but afraid of saying) that this is an
> abomination? To me it looks like those "obfuscated" contests "write
function
> that does this and that without using for and while statements or
> recursion".
>
> I finally gathered my courage to say: this sucks. I hope I don't sound
like
> a conservative moron because I am on record for using C++ for what it
wasn't
> intended to do. But I did so only when I saw some benefit in it.
>
> Compare the noise above with the good ol' for loop:
>
> for (Map::iterator i = M.begin(); i != M.end(); ++i)
> {
>     (i->second)->Draw();
> }

FWIW, I agree whole-heartedly.

It seems to me that most algorithms that involve iterating through each item
in a container in turn are much better done with a for() loop - it's /so/
much clearer than using algorithms, as Andrei's example above
demonstrates. And `typeof' would make things even clearer.

Maybe if we had a much cleaner syntax then algorithms would be more useful?
But that would probably involve local functions/closures etc etc, which seem
to turn into monsters whenever they are discussed here.

- Julian
--
http://www.op59.net/





---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: "Igor Krasnopeev" <captaincomatoz@mail.ru>
Date: Mon, 28 May 2001 13:34:22 CST
Raw View
Hello, all.

I often use std::map so it's very annoying for me, that code example below
is not possible:

class Object {
  ...
  public:
    void Draw();
  ...
};

map<int, Object*> M;

for_each(M.begin(), M.end(), mem_fun(&Object::Draw));

So, I can't use map directly instead of vector or list.
And it's all because map::iterator returns reference to map::value_type,
which is pair<const Key, value>.

So, my suggestion is to provide new type of iterators for map:
mapped_iterator (const_mapped_iterator & so on), which returns reference to
mapped_value (this type can easily be implemented through map::iterator),
and member functions mbegin() & mend(), which returns mapped_iterator.

Here is some example of implementation of mapped_iterator:

class map {
     ...
  public:
    class mapped_iterator : public iterator
    {
      public:
        typedef iterator::value_type::second_type& reference;
        typedef iterator::value_type::second_type* pointer;

        mapped_iterator()
          : iterator() {}
        mapped_iterator(parent::iterator i)
          : iterator(i) {}

        reference operator*() const
          { return iterator::operator*().second; }
        pointer operator->() const
          { return &iterator::operator*().second; }

        bool operator== (const mapped_iterator& y) const
          { return iterator::operator==(y); }
        bool operator!= (const mapped_iterator& y) const
          { return iterator::operator!=(y); }

        mapped_iterator& operator++()
          { iterator::operator++(); return *this; }
        mapped_iterator operator++(int)
          { mapped_iterator tmp = *this; ++*this; return tmp; }

        mapped_iterator& operator--()
          { iterator::operator--(); return *this; }
        mapped_iterator operator--(int)
          { mapped_iterator tmp = *this; --*this; return tmp; }
    };

    ...

    mapped_iterator mbegin() {return mapped_iterator(begin());}
    mapped_iterator mend() {return mapped_iterator(end());}
};

In this example any inerator may be used to create corresponding
mapped_iterator, and mapped_iterator may be used anywere, where iterator
needed.

Now for_each(M.mbegin(), M.mend(), mem_fun(&Object::Draw)) will works fine!

Has this improvement any chance to be standardized?

--

Igor Krasnopeev




---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: David Greene <greened@eecs.umich.edu>
Date: Mon, 28 May 2001 22:53:20 GMT
Raw View
Igor Krasnopeev <captaincomatoz@mail.ru> wrote:

: map<int, Object*> M;

: for_each(M.begin(), M.end(), mem_fun(&Object::Draw));

: So, I can't use map directly instead of vector or list.
: And it's all because map::iterator returns reference to map::value_type,
: which is pair<const Key, value>.

[Here's a properly-formatted response in case my original message wasn't
 killed.]

Use the (non-standard) select2nd functor:

  for_each(M.begin(), M.end(),
           compose1(mem_fun(&Object::Draw),
                    select2nd<map<int, Object *>::value_type>()));

select2nd looks something like this:

  template<typename T> > class select2nd :
    public std::unary_function<T, typename T::second_type> {
  public:
    typedef std::unary_function<T, typename T::second_type> baseType;
    typedef typename baseType::result_type result_type;
    typedef typename baseType::argument_type argument_type;

    const result_type &operator()(const argument_type &p) const {
      return(p.second);
    };

    result_type &operator()(argument_type &p) const {
      return(p.second);
    };
  };

HTH,

                                 -Dave

--

"Some little people have music in them, but Fats, he was all music,
 and you know how big he was."  --  James P. Johnson

---
[ 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.research.att.com/~austern/csc/faq.html                ]