Topic: STL Issue : How to store references vs. copies of objects?


Author: khan@xraylith.wisc.edu (Mumit Khan)
Date: 1995/05/29
Raw View
In article <3q41i6$i36@keystone.intergate.net>,
 <scherrey@proteus-tech.com> wrote:
>
> I took your code and and attempted to replace the int* with my class.
>The result was a few compile-time errors which were solved by the following
>explicit function declarations:
>
>inline void destroy( DeleteHole** pointer )
>{
> (*pointer)->~DeleteHole();
>}
>inline void construct( DeleteHole** p, const DeleteHole* value )
>{
> new (p) (const DeleteHole*)(value);
>}
>

Ben,
Does Borland OS/2 C++ compiler support explicitly calling a template
desctructor? If not, that's the reason why you need the specialization
above to deal with it. It does look like a compiler limitation as opposed
to HP's STL release. I don't have a copy of OS/2 Borland C++ (I don't have
access to OS/2 either), so can't be of any real help here. ObjectSpace
seems to use a different allocator depending on if the compiler can handle
explicitly calling template destructor or not. I strongly suspect Borland
is in the latter category and that you're stuck with explicitly
overloading destroy() and construct() (basically supplying a suitable
allocator) for each type of pointer object you're planning on storing in
STL containers.

As an alternative to this method, how about wrapping the pointer to class
X in another class, say XPtrWrapper, and then storing the wrapper object
which will let the compiler manage the construction and destruction
automatically? A bit of overhead, but much better than having to create
copies of these objects for each container and non-standard hacks like
what you had to do. You also get almost free/automatic reference counting
this way, removing the headache of having to remember who owns the
objects.

If you're going to use STL in "real code", I suggest looking to one of the
commercially supported STL implementations. I know of 2 so far that
support the Borland C++ compiler (don't know about OS/2 though) -- Modena
(1-800-MODENA1) and ObjectSpace (1-800-OBJECT1 ?, info@objectspace.com).
Both use HP implementation as the base, but isolate you from the bleeding
edge features that most compilers don't yet support (and possibly at the
expense of somewhat reduced functionality).

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

ps. does my example actually work when you add the allocator/deallocator?






Author: scherrey@proteus-tech.com
Date: 1995/05/26
Raw View
 I'm getting deep into the STL little by little. Documentation is very brief,
even for a reference. It's not that I don't like it so far, it's just that the thing is
do damn powerful and flexible that I can't help but believe I'm missing a "better"
way of going about doing something everytime I try something new.
 One apparent limitation, however, seems to be that the container
templates (specifically "set" in my case) want to store copies of object instances
and simply won't let me have a container of pointers, even when I create a
function class (similar to "less" found in function.h) that explicitly supports pointers.
I get all kinds of compile-time errors when trying to instantiate such a monster
and don't feel like digging through all that code to get something this "simple"
accomplished.
 My application problem is that I have a set of object instances that need
to be accessed in two different orders ( a Size attribute and Position attribute ).
One order requires a unique key ( Position ) whereas the other can be duplicated
so I'm attempting to store the same instances in two difference containers, a set
and multiset respecitively. I don't want two copies of each object instance so my
"obvious" solution to this is to create a special reference class that copies the
interface of the class but stores a pointer to my actual instance. I will then have
one instance of my actual Class and two instances of references to that class for
every Size/Position combination I'll need.
 While this will work, it's not as efficient as I'd like and with the power of
the STL, I've got to believe that I'm just missing a more elegant solution that's
already available. I had a hunch that the allocator class template might help
(it's got all those neat typedefs for reference, pointer, etc...) but couldn't figure
out how. Anyone care to give me a hint or point me to an elusive instruction
document on efficient usage of the STL?!?!?

 All help is much appreciated!
  Thanx & later,

   Ben Scherrey
   Proteus Technologies, Inc.





Author: khan@xraylith.wisc.edu (Mumit Khan)
Date: 1995/05/26
Raw View
Two things for future reference:
1. Please use an editor that wraps lines to <= 72 characters or so. I
   had to reformat yours which takes time away from useful stuff like
   answering your questions.

2. Please include compiler and STL info (eg, GNU C++ 2.6.3 and libg++
   2.6.2 STL or Borland 4.5 and HP STL, etc) next time.

   My code snippet below works with gcc-2.6.3 and ObjectSpace STL but
   NOT with libg++ STL because of a bug in GCC 2.6.3 (but does work
   when you use the latest snapshot of GCC). This bug has to do with
   GCC choking on the singleton set in STL and fortunately has easy
   work-arounds.

><scherrey@proteus-tech.com> wrote:
>
> One apparent limitation, however, seems to be that the
> container templates (specifically "set" in my case) want to
>store copies of object instances and simply won't let me have a
>container of pointers, even when I create a function class (similar to
>"less" found in function.h) that explicitly supports pointers.  I get
>all kinds of compile-time errors when trying to instantiate such a
>monster and don't feel like digging through all that code to get
>something this "simple" accomplished.

As for storing pointers, absolutely not true. You do have to be careful
however about 2 things:
    1 deleting the objected pointed to explicitly because STL wouldn't do
      it for you
    2 comparison functions wouldn't work since pointer comparison has
      no bearing on how the pointed objects would compare.

Here's a snippet to show storing pointers work: (I'm demonstrating what
I think you want -- allocated object stored in multiple containers)

==== cut here

#include <stl.h>  // or individual includes if you like
    // need <list.h>, <set.h> and <algo.h>
#include <iostream.h>

//
// THIS IS VERY IMPORTANT (see note 2 above). You have to tell the set
// or multiset or map to compare the objects pointed to rather than
// the pointers these containers are storing.
//
struct compare {
    bool operator() (const int* i1, const int* i2) const {
 return *i1 < *i2;
    }
};

void print(int* i) {
    cout << " " << *i;
}

int main(int, char*[]) {
    list<int*> list1;

    //
    // create a list of new'd integers.
    //
    for(int i = 0; i < 5; ++i) {
 list1.push_back(new int(i * i));
    }

    cout << "List of int*: (";
    for_each(list1.begin(), list1.end(), print);
    cout << ")" << endl;

    //
    // now put these integers into a set. Note that I'm using a
    // custom comparitor to compare the integers, not the pointers.
    //
    set<int*, compare> set1;
    copy(list1.begin(), list1.end(),
 insert_iterator<set<int*, compare> > (set1, set1.begin())
    );

    cout << "Set of int* : [";
    for_each(set1.begin(), set1.end(), print);
    cout << "]" << endl;

    return 0;
}

====

% ./testc++
List of int*: ( 0 1 4 9 16)
Set of int* : [ 0 1 4 9 16]

voila!

===

Now for cleaning up memory: You can designate ONE of the containers
and give it the responsibility to delete the storage and the rest of them
simply don't have to worry about it (I actually use a heap manager for
this type application, but that's a bit beyond the scoe of this example)

You can templatize that as well:

    template <class ForwardIterator, class ForwardIterator>
    void sequence_delete(ForwardIterator first, ForwardIterator last) {
       while (first != last)
    delete *first++;
    }

Now you can delete everything pointed to by the list<int*> object:

    sequence_delete(list1.begin(), list1.end());
    list1.erase(list1.begin(), list1.end()); // or let it go out of scope.

alternatively,

    sequence_delete(set1.begin(), set1.end());

If you want reference semantics in client code, you can always do the
following:

    list<int*>::const_iterator it = list1.begin();
    for(; it != list1.end(); ++it) {
 const int& val = *(*it); // parens for emphasis only.
 do_stuff(val);
    }

There's more in my STL newbie file available via WWW:
    http://www.xraylith.wisc.edu/~khan/ [and follow "STL Newbie" link]

enjoy STL
mumit -- khan@xraylith.wisc.edu





Author: gglass@objectSpace.com (Graham Glass)
Date: 1995/05/26
Raw View
In article <3q3hs4$fs5@keystone.intergate.net>, scherrey@proteus-tech.com says:
>
>
>        I'm getting deep into the STL little by little. Documentation is very brief,
>even for a reference. It's not that I don't like it so far, it's just that the thing is
>do damn powerful and flexible that I can't help but believe I'm missing a "better"
>way of going about doing something everytime I try something new.
>        One apparent limitation, however, seems to be that the container
>templates (specifically "set" in my case) want to store copies of object instances
>and simply won't let me have a container of pointers, even when I create a
>function class (similar to "less" found in function.h) that explicitly supports pointers.
>I get all kinds of compile-time errors when trying to instantiate such a monster
>and don't feel like digging through all that code to get something this "simple"
>accomplished.
...

Many of the current STL implementations don't deal with the compiler
limitations regarding templates, and therefore don't allow you to
declare STL containers of pointers. However, STL is perfectly capable
of allowing declarations like this:

vector< X* > x; // vector of pointers to X objects

which I presume is what you'd like to do. The ObjectSpace implementation
of STL, called STL<ToolKit>, allows this construct with no hassles + comes
with a 400+ page tutorial. For more information, send email to
info@objectspace.com

- Graham Glass





Author: scherrey@proteus-tech.com
Date: 1995/05/26
Raw View
In <3q3mt1$27lm@news.doit.wisc.edu>, khan@xraylith.wisc.edu (Mumit Khan) writes:
>There's more in my STL newbie file available via WWW:
>    http://www.xraylith.wisc.edu/~khan/ [and follow "STL Newbie" link]

 Thanx very much for the quick response! I'll try out your suggestion and
see what I get. Meanwhile, I'm glad to see that someone's put out some info on
STL usage. I'll definately take a look at it.
 later,

 Ben Scherrey
 Proteus Technologies, Inc.





Author: scherrey@proteus-tech.com
Date: 1995/05/26
Raw View
 I took your code and and attempted to replace the int* with my class.
The result was a few compile-time errors which were solved by the following
explicit function declarations:

inline void destroy( DeleteHole** pointer )
{
 (*pointer)->~DeleteHole();
}
inline void construct( DeleteHole** p, const DeleteHole* value )
{
 new (p) (const DeleteHole*)(value);
}

 The problem was that the templated functions in STL (I'm using HP's
released code with Borland's OS/2 C++ compiler v.2) were not able to deal
with the pointer types. Is this an STL code problem, my compiler's fault (I don't
think it's the case, the code's definately not ready for this construct), or just
a limitation that I'm going to have to get used to? Meanwhile, thanx very much
for your help as this solution is much better than what I had going!

   later,

 Ben Scherrey
 Proteus Technologies, Inc.

PS: I would have never figured this out if it wasn't for Borland's meaningful
     compiler error messages. Kudo's!