Topic: Making STL user-friendly?? [STL find errorprone]


Author: f_clerc@effix.effix.fr (Fabrice Clerc)
Date: 1995/09/28
Raw View
In article <449o3m$17jm@news.doit.wisc.edu>, khan@xraylith.wisc.edu says...
>
>Fabrice Clerc <clerc@gla.ecoledoc.ibp.fr> wrote in comp.std.c++:
>> maybe make containers of pointers a bit easier to use:
>>
>> o allow "standard" comparison functions to be used with
>>   containers of pointers:
>>
>>   set<int*,indirect_apply<less<int> > > spi;
>>
>>   (boy, that sure is synctatic sugar :-)
>>
>
>I always have and still do contend that if you have pointers in your
>containers, you should revisit the design: if you're storing pointers
>to basic/built-in types, why bother; if you're storing pointers to
>classes either for efficiency/space or for polymorphism, use a wrapper.
>See http://www.xraylith.wisc.edu/~khan/software/stl/STL.newbie.html for
>some rationale and examples of this.
>

I guess you're right (do you wrap _all_ your pointers ?).


>With that out of the way, how about the following: (off the top of my
>head, so not tested yet)
>
[...]
Yes, I have something along these lines, it works ok.

>
>or you could always do what ObjectSpace does -- define new global fcts
>(eg., less_p which deferences the args before using them).
>
>
[...]
>
>The real question is whether these "convenience" functions/algos should
>actually be (have been) part of the standard? My personal belief is that
>these are way too specific to programming style and the particular
>application, and should be left to the user to add to the STL base,
>preferably in a different namespace.
>

But don't you think that commercial versions of the STL will
ship with these convenience functions? I guess they will, because
these functions _are_ useful. And people will use those functions
(at least I will). What does that mean? It means that if I want
to try another implementation, I will have to port my code to that
other implementation, because the convenience functions will be
slightly different.

I'd like to be able to choose a commercial version of the STL
based on the quality of its implementation, not its interface.

Don't get me wrong, I don't think that a standard should attempt
to solve all the problems programmers stumble across. Believe me,
I am fighting that kind of behavior every day (you know: "please
add this to this library, and add this as well because that would
be useful to me, etc"). I just wish that in the case of STL they
had gone a bit further...


>
>regards,
>mumit -- khan@xraylith.wisc.edu
>http://www.xraylith.wisc.edu/~khan/
>

Fabrice
clerc@gla.ecoledoc.ibp.fr
f_clerc@effix.fr

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1995/09/30
Raw View
f_clerc@effix.effix.fr (Fabrice Clerc) wrote:

>>>I will agree on this one -- it IS going to be frequent, and:
>>>       find(container)
>>>IS a distinct enhancement over:
>>>       find(container.begin(), container.end())
>>
>>    I DON'T agree.

>>
>>    There is nothing wrong with writing convenience
>>functions like "find(container, value)" --- the very ability to
>>do so in a few lines of code --- in several different ways ---
>>is exactly why this should NOT be standardised.
>>Put such a function in your own namespace, to avoid clashes.
>>
>
>so, you agree :-)

   I agree it is fine to write your own functions -- but not that
such functions ought to be standardised.

>
>ok, suppose bob writes a small library which uses the STL.
>no problem, bob's convenience functions are shipped with the
>library and are placed in a namespace. tom also writes a
>library which also uses the STL and also has convenience
>functions in a namespace.
>
>now, what happens if I want to build an application using both
>libraries? I end up with at least two set of convenience functions
>(three if I have mine) doing basically the same thing.

   Yes. You have a choice to use them, or even write some more :-)

>imho, if a lot of people end up writing the 'same' convenience
>functions, these convenience functions should be placed in a
>standard. the question is, what is "a lot of people" ?

   "number of people" isn't a good criteria by itself.
I haven't written this particular function, because it
doesn't do much for me: I'm getting used to supplying both
begin() and end() off of containers and I find it quite
readable -- personal taste, mind you. There are enough cases
where I am NOT using a range of a whole container that
I like seeing the same form of arguments used everywhere:

   copy(x.begin(), x.end(), out);
   copy(it1, it2, out);

rather than two forms:

   copy(x, out);
   copy(it1, it2, out);

The former COULD have been adopted as standard:

   copy(range(it1, it2), out);

.. but note that "copy()" returns an iterator separate from its
argument "out". So it seems better to pass the beginning and
ending iterators separately everywhere rather than sometimes
a range object and sometimes not. (to me, anyhow)



--
John Max Skaller               voice: 61-2-566-2189
81 Glebe Point Rd              fax:   61-2-660-0850
GLEBE NSW 2037                 email: maxtal@suphys.physics.oz.au
AUSTRALIA                      email: skaller@maxtal.com.au
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: khan@xraylith.wisc.edu (Mumit Khan)
Date: 1995/09/14
Raw View
This is a rather interesting thread started by Tony Cook in c.std.c++
on whether STL is error-prone or not. Ajay Kamdar seems to agree, and
he also points out something that is quite troublesome to me as well --
commercial vendors adding extensions "before the ink is dry on DWP".
John Max Skaller, on the other hand, claims that STL is designed to be
a low-level toolkit that app programmers would build on and it does not
necessarily have to be "user-friendly". Since I have a vested interest
in the welfare of STL, I'd like to take this discussion further and
find out what people really want out of STL and possibly come up with a
feasible solution that builds on STL (I personally include stl includes
via "stl/include", where the include file wraps the appropriate stl
include file and adds my own simplified algorithms -- such as deleting
a sequence/map and such).

1. What are the various things that you have find to be error-prone?

2. What kind of changes would you like to see in the interface to the
   STL algorithms? The thread started with Tony Cook using the begin/
   end iterator in find algorithm (which I consider essential since I
   do partial searches all the time) as an example if I remember
   correctly. Cook suggests find() return a bool and modify the iterator
   argument ...

3. Container member functions -- any new members that you think "ought"
   to be added? When it comes to STL, I tend to favor not touching the
   container classes and rather favor adding global fcts ala algo.h.

4. Member functions that are also available as algorithms: eg., list<T>
   provides a sort member (uses '<' relation), but STL also provides
   a generalized sort() algorithm where you provide your custom
   comparator. Is the list<T>::sort() necessary? If so, why?

5. Verbosity: There was a thread few months ago about verbosity in using
   STL containers and iterators. I have cases where each iterator type
   has to be typedef'd just so I can fit the decl in a reasonable width
   line. I remember someone** proposing some typeof() member to return
   the correct type of iterator so we don't have to do the following:

   map<string, map<string, list<list<point> > >::const_iterator =
       polygon_map.begin();

   And this is one of the short ones in my CAD library.

   ** I wish I'd saved the article or remembered who it was.

6. Understanding how STL manages storage: In the past 6 months or so,
   I've received (and answered quite a few!) about 150 mail messages
   asking about STL storage management. Is it really that hard to
   understand? What kind of examples would make it clear?

7. What kind of documentation would make using STL easy (easier?)?

8. your turn ...


Ajay Kamdar <ajay@lehman.com> wrote:
>[Note: Check out the "C++ Tradeoffs" article by Ellis and Carroll in
>  the September '95 issue of the C++ Report for a good discussion of
>  a topic some what related to whether to use a STL container directly.]
>

OK article, but I didn't see a whole lot beyond common sense :-)

>Container classes are needed both in the interface and implementation
>of C++ programs at all levels. If STL is being positioned to NOT fill
>the need for a standard general purpose container library that can be
>used at all levels of C++ programming, then IMHO STL shouldn't even be
>in the standard.

No high level toolkit designed by a committee is going to make everybody
(or even a reasonable population) happy! I would much rather have the
higher level toolkit built on top of STL as a separate package, and if
there is enough interest (and input), I might actually do one of these
(it'll be free of course).

regards
mumit -- khan@xraylith.wisc.edu
http://www.xraylith.wisc.edu/~khan/
http://www.xraylith.wisc.edu/~khan/software/stl/STL.newbie.html

ps: Apologies to moderators about botched attempts in posting this, esp
about the misattribution. At least now I know that you guys actually read
these :-)
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: admin@rzaix53.uni-hamburg.de (Bernd Eggink)
Date: 1995/09/14
Raw View
Mumit Khan (khan@xraylith.wisc.edu) wrote:

[ ... ]

>  1. What are the various things that you have find to be error-prone?

My main concern is runtime checking. As iterators don't hold any information
about the respective container, it is impossible to check whether
an iterator is dereferencable or whether two iterators point to the same
container.

Of course you can write wrappers which bind iterator and container together,
but this will result in rewriting quite a portion of STL, and again
several different and incompatible solutions will emerge, which is exactly
what a standard should prevent.

And there are other nasty traps. Recently I fell into this one:

class str
{
  public:
 str(const string &s): st(s), i(0) { }
 int operator<(const str &x) const { return i < x.i; }
 // ...

  private:
 string  st;
 int   i;
};

main()
{
 typedef set<string, less<str> > strset; // Oops, should be less<string>

 strset a;

 a.insert(string("one"));
 a.insert(string("two"));
 // ...
}

This program compiles and runs well, but gives wrong results. Errors like
this (I suspect there are a lot more of them lurking around) are difficult
to catch. Although STL is not to blame for it - it's a general problem with
templates - I'd prefer an interface which prevents the user from providing
inconsistent parameters.

--
+----------------------------------+
|          Bernd Eggink            |
|    Rechenzentrum Uni Hamburg     |
| admin@rzaix13.rrz.uni-hamburg.de |
+----------------------------------+

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: clerc@gla.ecoledoc.ibp.fr (Fabrice Clerc)
Date: 1995/09/15
Raw View
In article <4378m2$1n86@news.doit.wisc.edu>, khan@xraylith.wisc.edu (Mumit Khan) writes:
|> This is a rather interesting thread started by Tony Cook in c.std.c++
[...]
|>
|>
|> 3. Container member functions -- any new members that you think "ought"
|>    to be added? When it comes to STL, I tend to favor not touching the
|>    container classes and rather favor adding global fcts ala algo.h.
|>

template <class Container>
void clear(Container& c) {

  c.erase(c.begin(),c.end());
}

|>
|> 8. your turn ...
|>

maybe make containers of pointers a bit easier to use:

o allow "standard" comparison functions to be used with
  containers of pointers:

  set<int*,indirect_apply<less<int> > > spi;

  (boy, that sure is synctatic sugar :-)

o also, I have found

  template <class Iterator>
  void delete_range(Iterator first,Iterator last) {

    while (first!=last) {
      delete *first++;
    }
  }

  useful.


Fabrice
---
Fabrice Clerc
clerc@gla.ecoledoc.ibp.fr




[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]






Author: Tony Cook <tony@online.tmx.com.au>
Date: 1995/09/21
Raw View
Mumit Khan (khan@xraylith.wisc.edu) wrote:
: This is a rather interesting thread started by Tony Cook in c.std.c++
: on whether STL is error-prone or not.

Thanks <grin>.

: 1. What are the various things that you have find to be error-prone?

Take the find example again:
  vector<int> myContainer;
  vector<int>::iterator found =
 find(myContainer.begin(), myContainer.end());
  if (found == myContainer.end())
 ....

Here I must supply the container name 3 times in a to perform a
fairly simple (and common) operation - this is what I find
error-prone.

: 2. What kind of changes would you like to see in the interface to the
:    STL algorithms? The thread started with Tony Cook using the begin/
:    end iterator in find algorithm (which I consider essential since I
:    do partial searches all the time) as an example if I remember
:    correctly. Cook suggests find() return a bool and modify the iterator
:    argument ...

I don't see any need to _remove_ the current begin/end idiom - as
you say it's useful - but to simply supply extra interfaces to
perform the most common functions in a more compact way -
specifically, operating on the whole container, and possibly
operating from the start of the container to a specific point and
from a specific point to the end of the container.

Note that even with a subset of a container, we still have multiple
redundant references, eg:

  vector<int>::iterator searchStart, searchEnd;
  // ... searchStart, searchEnd set somehow
  vector<int>::iterator searchResult =
 find(searchStart, searchEnd);
  // we use searchEnd twice
  if (searchResult == searchEnd)
 ...

or, possibly more realisticly:
  vector<int>::iterator searchStart, searchEnd;
  // ... searchStart, searchEnd set somehow
  vector<int>::iterator searchPos = searchStart;
  while (searchPos = find(searchPos, searchEnd)) != searchEnd)
  {
    //...process it
    ++searchPos; // prepare for next one
  }

The test here should be 'did we find it' - not 'are we at this point
yet' - find 'knows' where the end of the range is - why can't it
report whether or not it was successful without us having to supply
one of it's parameters again?

--
        Tony Cook - tony@online.tmx.com.au
                    100237.3425@compuserve.com


---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: khan@xraylith.wisc.edu (Mumit Khan)
Date: 1995/09/22
Raw View
In article <MATT.95Sep14103019@physics10.berkeley.edu>,
Bernd Eggink <admin@rzaix53.uni-hamburg.de> wrote:
>
>Mumit Khan (khan@xraylith.wisc.edu) wrote:
>
>[ ... ]
>
>>  1. What are the various things that you have find to be error-prone?
>
>My main concern is runtime checking. As iterators don't hold any information
>about the respective container, it is impossible to check whether
>an iterator is dereferencable or whether two iterators point to the same
>container.

This is a sticky point, and just about *everybody* I've corresponded
with has brought up this point. I at one point had added some code to
some of the iterators in ObjectSpace STL<ToolKit> to throw an exception
when you dereference an invalid iterator, but I haven't had the
"opportunity" to use it yet (so the code isn't really even tested).
What I don't like about my hack, and that's what you mention as well,
is that I've had to couple the iterator and container and I rather like
the (current) decoupled view of the two.

I'll see if I can easily retrofit the changes to the free implementation
(either HP reference or libg++) and then I'll post it. It's pretty simple
minded, basically built like the Fallible<T> in "Scientific and Engr C++"
by Barton and Nackman. I can't remember exactly how much I had actually
completed, but believe I only implemented the checked iterators for the
sequential containers (list<T>, vector<T> and deque<T>), not for the
associative ones.

the easiest implementation of a checked-iterator is of course is that
for the vector<T> class ...

As for a test if two iterators point to the same iterators, the coupling
would provide that as well.

Has anybody implemented this in a clean way??

>
>And there are other nasty traps. Recently I fell into this one:
>
>class str
>{
>  public:
> str(const string &s): st(s), i(0) { }
> int operator<(const str &x) const { return i < x.i; }
> // ...
>
>  private:
> string  st;
> int   i;
>};
>
>main()
>{
> typedef set<string, less<str> > strset; // Oops, should be less<string>
>
> strset a;
>
> a.insert(string("one"));
> a.insert(string("two"));
> // ...
>}
>


But what would you like the compiler/implementations to do in these
cases? This is of course a design feature that you can pass custom
comparator function. Now if the compilers supported the draft, then
at least your problem will go away;

    template <class Key, class Compare = less<Key> > class set;

so you'd never have to write code like you have to now unless you *want*
to supply a custom comparator (eg., when the key is a pointer to some
object). Your problem is solved.

Of course, if C++ provided constraints checking (see the D&E for a
discussion on this), you could do much more ...

Complain to compiler vendors to catch up to the standard. But wait, there
is no C++ standard. Aaargghhhh!

regards
mumit -- khan@xraylith.wisc.edu
http://www.xraylith.wisc.edu/~khan/

Cc: comp.std.c++, Bernd Eggink <admin@rzaix53.uni-hamburg.de>


---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: martelli@cadlab.cadlab.it (Alex Martelli)
Date: 1995/09/23
Raw View
Tony Cook <tony@online.tmx.com.au> writes:
 ...
>I don't see any need to _remove_ the current begin/end idiom - as
>you say it's useful - but to simply supply extra interfaces to
>perform the most common functions in a more compact way -
>specifically, operating on the whole container, and possibly

I will agree on this one -- it IS going to be frequent, and:
 find(container)
IS a distinct enhancement over:
 find(container.begin(), container.end())

>operating from the start of the container to a specific point and
>from a specific point to the end of the container.

But how could you do better here than
 find(container.begin(), specificPoint)
or
 find(specificPoint, container.end())
???


>  vector<int>::iterator searchStart, searchEnd;
>  // ... searchStart, searchEnd set somehow
>  vector<int>::iterator searchPos = searchStart;
>  while (searchPos = find(searchPos, searchEnd)) != searchEnd)
>  {
>    //...process it
>    ++searchPos; // prepare for next one
>  }

>The test here should be 'did we find it' - not 'are we at this point
>yet' - find 'knows' where the end of the range is - why can't it
>report whether or not it was successful without us having to supply
>one of it's parameters again?

Because, I guess, there is no "null" value for every sort of
iterator -- one that could only be used for automatic conversion
to bool returning false, while every other iterator would convert
to true.  This is a distinct convenience with pointers and one
of the most common C idioms, of course.  If there was a "null",
then it would seem to me to be the natural value to return from
an iterator-returning function that "fails" (in a "soft" sense,
not in an exception-raising one).  "Null" would also be a natural
for initialization of iterators, etc, again in analogy with
pointers.  I assume there were solid reasons not to guarantee
the existence of "null iterators" for every kind...?


Alex
--
DISCLAIMER: these are TOTALLY personal opinions and viewpoints, NOT connected
in any way with my employer, nor any other organization or individual!
Email: martelli@cadlab.it                            Phone: +39 (51) 597313
CAD.LAB s.p.a., v. Ronzani 7/29, Casalecchio, Italia   Fax: +39 (51) 597120


---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1995/09/25
Raw View
martelli@cadlab.cadlab.it (Alex Martelli) wrote:
>
>Tony Cook <tony@online.tmx.com.au> writes:
> ...
>>I don't see any need to _remove_ the current begin/end idiom - as
>>you say it's useful - but to simply supply extra interfaces to
>>perform the most common functions in a more compact way -
>>specifically, operating on the whole container, and possibly
>
>I will agree on this one -- it IS going to be frequent, and:
> find(container)
>IS a distinct enhancement over:
> find(container.begin(), container.end())

    I DON'T agree.

    It is crucial for orthogonality that the algorithm interfaces
are isolated from any notion of containers -- which is
what the STL iterator architecture is for.

             ITERATORS
               ^    \-------------\------\---------...
             /       \             \      \
            /         V             V      V
      ALGORITHMS     CONTAINERS DEVICES PROCESSES

    Providing standard algorithms taking container arguments
would destroy the essential independence of the notions
by introducing direct coupling. The _indirection_ provided
by iterators is what makes STL work so well.

    What makes STL such an excellent library is the
way iterators are used to interface containers and algorithms
by contracts on the intermediaries that service both --- iterators.
This leads to predictable orthogonal combination -- where
predictable refers not only to correctness, but also performance.

    There is nothing wrong with writing convenience
functions like "find(container, value)" --- the very ability to
do so in a few lines of code --- in several different ways ---
is exactly why this should NOT be standardised.
Put such a function in your own namespace, to avoid clashes.

--
John Max Skaller               voice: 61-2-566-2189
81 Glebe Point Rd              fax:   61-2-660-0850
GLEBE NSW 2037                 email: maxtal@suphys.physics.oz.au
AUSTRALIA                      email: skaller@maxtal.com.au
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]