Board index » delphi » TSortedCollections = memory leaks?

TSortedCollections = memory leaks?

I am wondering exactly how one prevents memory leaks when using
TSortedCollections.

I understand that the collections themselves store pointers to unique
objects located via the Compare virtual method called by
"TSortedCollection.Insert(p)"; where p is a pointer to a descendent of
TObject.

However, if p is not inserted into the collection because there is an
existing object that Compares identically , what happens to the
pointer?  Does the TSortedCollection dispose of it cleanly, or does
the calling routine have to dispose of it?

If the calling routine must dispose of the pointer, is there some way
to know if it has been inserted or not?  Or is the proper approach to
Search the collection first to see the object's location is already
taken (which results in two searches per insertion)?

It seems wasteful to Search twice, and even more wasteful to leave an
undisposed pointer.  The best scenario would be for the
SortedCollection to dispose of the pointer, but I can't imagine a
general-purpose object would presume to do that.

 Any suggestions?

 

Re:TSortedCollections = memory leaks?


Quote
> I understand that the collections themselves store pointers to unique
> objects located via the Compare virtual method called by
> "TSortedCollection.Insert(p)"; where p is a pointer to a descendent of
> TObject.

> However, if p is not inserted into the collection because there is an
> existing object that Compares identically , what happens to the
> pointer?  Does the TSortedCollection dispose of it cleanly, or does
> the calling routine have to dispose of it?

> If the calling routine must dispose of the pointer, is there some way
> to know if it has been inserted or not?  Or is the proper approach to
> Search the collection first to see the object's location is already
> taken (which results in two searches per insertion)?

> It seems wasteful to Search twice, and even more wasteful to leave an
> undisposed pointer.  The best scenario would be for the
> SortedCollection to dispose of the pointer, but I can't imagine a
> general-purpose object would presume to do that.

>  Any suggestions?

The best way was: Insert is virtual, override, check yourself if the
item exists already, and dispose, no changes to the existing source.

An uglier way is to save the number of items before inserting, and a
pointer to the item, and check after inserting if the number of items
has changed.

I believe this was done to allow non tobject based tcollections, but am
not sure.

I'll dig up the old msgs about this topic

--
Marco van de Voort (Mar...@stack.nl)

Pascal page on www.stack.nl/~marcov/xtdfpc.htm
Fortuneclone (written in Pascal with humor archive ) on
    www.stack.nl/~marcov/cookie.htm

Re:TSortedCollections = memory leaks?


On 15 Mar 2000 00:32:38 GMT, wso...@attglobal.net (William Sonna)
wrote:

Quote
> I am wondering exactly how one prevents memory leaks when using
> TSortedCollections.

> I understand that the collections themselves store pointers to unique
> objects located via the Compare virtual method called by
> "TSortedCollection.Insert(p)"; where p is a pointer to a descendent of
> TObject.

> However, if p is not inserted into the collection because there is an
> existing object that Compares identically , what happens to the
> pointer?  Does the TSortedCollection dispose of it cleanly, or does
> the calling routine have to dispose of it?

Of course TSortedCollection.Insert may not dispose a parameter.
You could want to insert a pointer to an object into several
collections.

A simple way (if Duplicates=false) is to keep track of
Collection.Count. If it increased after Insert then the pointer was
inserted, otherwise it wasn't.

Another way is to create a boolean function yourself which contains
the implementation of Insert and provides the additional information.

type
  TMySortedCollection=object(TSortedCollection)
    function isInserted(Item:Pointer):boolean;virtual;
  end;

  function TMySortedCollection.isInserted;
  var
    i:integer;
  begin
    isInserted:=true;
    if not Search(KeyOf(Item,i)) or Duplicates then
      AtInsert(i,Item)
    else
      isInserted:=false
  end;

Now you would use

        if isInserted(p) then ...

 instead of

        Insert(p);

Regards
Horst

Re:TSortedCollections = memory leaks?


On Sun, 15 Mar 3900 07:38:43, horst.krae...@t-online.de (Horst

Quote
Kraemer) wrote:
> On 15 Mar 2000 00:32:38 GMT, wso...@attglobal.net (William Sonna)
> wrote:

> > I am wondering exactly how one prevents memory leaks when using
> > TSortedCollections.

> > I understand that the collections themselves store pointers to unique
> > objects located via the Compare virtual method called by
> > "TSortedCollection.Insert(p)"; where p is a pointer to a descendent of
> > TObject.

> > However, if p is not inserted into the collection because there is an
> > existing object that Compares identically , what happens to the
> > pointer?  Does the TSortedCollection dispose of it cleanly, or does
> > the calling routine have to dispose of it?

> Of course TSortedCollection.Insert may not dispose a parameter.
> You could want to insert a pointer to an object into several
> collections.

> A simple way (if Duplicates=false) is to keep track of
> Collection.Count. If it increased after Insert then the pointer was
> inserted, otherwise it wasn't.

> Another way is to create a boolean function yourself which contains
> the implementation of Insert and provides the additional information.

> type
>   TMySortedCollection=object(TSortedCollection)
>     function isInserted(Item:Pointer):boolean;virtual;
>   end;

>   function TMySortedCollection.isInserted;
>   var
>     i:integer;
>   begin
>     isInserted:=true;
>     if not Search(KeyOf(Item,i)) or Duplicates then
>       AtInsert(i,Item)
>     else
>       isInserted:=false
>   end;

> Now you would use

>    if isInserted(p) then ...

>  instead of

>    Insert(p);

> Regards
> Horst

Thanks for the tip.

But if I may ask one followup question:

After determining that the object has not been inserted, will
dispose(Item) completely dispose the object, or must Item be typecast
to the object type being disposed?

Are Pascal pointers that smart?

Re:TSortedCollections = memory leaks?


On 15 Mar 2000 12:23:28 GMT, wso...@attglobal.net (William Sonna)
wrote:

Quote
> > Another way is to create a boolean function yourself which contains
> > the implementation of Insert and provides the additional information.
> > type
> >   TMySortedCollection=object(TSortedCollection)
> >     function isInserted(Item:Pointer):boolean;virtual;
> >   end;
> >   function TMySortedCollection.isInserted;
> >   var
> >     i:integer;
> >   begin
> >     isInserted:=true;
> >     if not Search(KeyOf(Item,i)) or Duplicates then
> >       AtInsert(i,Item)
> >     else
> >       isInserted:=false
> >   end;

> > Now you would use

> >       if isInserted(p) then ...

> >  instead of

> >       Insert(p);

> > Regards
> > Horst

> Thanks for the tip.

> But if I may ask one followup question:

> After determining that the object has not been inserted, will
> dispose(Item) completely dispose the object, or must Item be typecast
> to the object type being disposed?
> Are Pascal pointers that smart?

I don't quite understand your question.

You are using a pointer to your data type as you probably always do.
i.e.

var
  p:PMyobj;
...
new(p,init);
...
MyCollection.Insert(p);

or

if not MyCollection.isInserted(p) then dispose(p,done);

[ or simply new(p)/dispose(p) if you data aren't 'objects' ]

You are disposing your pointer as you always do. Where do you see a
problem ?

Regards
Horst

Re:TSortedCollections = memory leaks?


Quote
William Sonna <wso...@attglobal.net> wrote in message

news:05C6FUhLDNUU-pn2-S2dLyQWSHLod@bill...

Quote
> I am wondering exactly how one prevents memory leaks when using
> TSortedCollections.

> I understand that the collections themselves store pointers to unique
> objects located via the Compare virtual method called by
> "TSortedCollection.Insert(p)"; where p is a pointer to a descendent of
> TObject.

> However, if p is not inserted into the collection because there is an
> existing object that Compares identically , what happens to the
> pointer?  Does the TSortedCollection dispose of it cleanly,

No.

Quote
> or does the calling routine have to dispose of it?

Yes. This is a design error. If you have the TV source code you might want
to change the TCollection.Insert and AtInsert virtual methods (and therefore
all overridend descendant methods) into a boolean function that returns TRUE
if the object is inserted. TSortedCollection.Insert then becomes:

function TSortedCollection.Insert(Item: Pointer): Boolean;
var
  I: Integer;
begin
  Insert := False;
  if not Search(KeyOf(Item), I) or Duplicates
   then Insert := AtInsert(I, Item);
end;

function TCollection.AtInsert(Index: Integer; Item: Pointer): Boolean;
assembler;
asm
  [Same a original down to the @@2: label, then ]
@@2:    MOV     AX,WORD PTR [Item+2]
        STOSW
        MOV     AX,WORD PTR [Item]
        STOSW
        CLD
        MOV     AL,1
        JMP     @@6

@@3:    MOV     AX,coIndexError
        JMP     @@5
@@4:    MOV     AX,coOverflow
        MOV     BX,CX
@@5:    CALL    CollectionError
        XOR     AX,AX
@@6:
end;

Now you can have code like the following:

if not MyCollection.Insert(P)
 then Dispose(P);

--
Jay

Jason Burgon - Author of Graphic Vision
New version 2 now available from:
http://www.jayman.demon.co.uk

Re:TSortedCollections = memory leaks?


On Sun, 15 Mar 3900 16:41:47, horst.krae...@t-online.de (Horst

Quote
Kraemer) wrote:
> On 15 Mar 2000 12:23:28 GMT, wso...@attglobal.net (William Sonna)
> wrote:

> > > Another way is to create a boolean function yourself which contains
> > > the implementation of Insert and provides the additional information.

> > > type
> > >   TMySortedCollection=object(TSortedCollection)
> > >     function isInserted(Item:Pointer):boolean;virtual;
> > >   end;

> > >   function TMySortedCollection.isInserted;
> > >   var
> > >     i:integer;
> > >   begin
> > >     isInserted:=true;
> > >     if not Search(KeyOf(Item,i)) or Duplicates then
> > >       AtInsert(i,Item)
> > >     else
> > >       isInserted:=false
> > >   end;

> > > Now you would use

> > >  if isInserted(p) then ...

> > >  instead of

> > >  Insert(p);

> > > Regards
> > > Horst

> > Thanks for the tip.

> > But if I may ask one followup question:

> > After determining that the object has not been inserted, will
> > dispose(Item) completely dispose the object, or must Item be typecast
> > to the object type being disposed?

> > Are Pascal pointers that smart?

> I don't quite understand your question.

> You are using a pointer to your data type as you probably always do.
> i.e.

> var
>   p:PMyobj;
> ....
> new(p,init);
> ....
> MyCollection.Insert(p);

> or

> if not MyCollection.isInserted(p) then dispose(p,done);

> [ or simply new(p)/dispose(p) if you data aren't 'objects' ]

> You are disposing your pointer as you always do. Where do you see a
> problem ?

This (bad) example should illustrate the question:

procedure MyTable.MyInsert(Item:pointer);
var i:integer;

begin
i:=self.count;
self.insert(Item);          {the problem is that Item is a pointer;
not necessarily an object}
if self.count=i then                            {insert failed;
dispose pointer}
  dispose(PObject(Item),done)    { DANGEROUS typecast - Item need not
point to an                                     object descended from TObject}
                or
  dispose(Item)                                  {even worse - any
memory allocated by Item is lost}
end;

This may seem a bit contrived, but it at least illustrates the point
that the creator of the object, not the collection, appears as if it
should be disposing of the objects that are not inserted into the
collection.  Only the creator knows the actual type, and it is easily
accomlplished by the method you showed me.

But when it comes time to dispose the collection, the collection (not
the creator) MUST do the disposing via some variation of the
dispose(pObject(Item),done) mechanism shown above, which is inherently
unsafe, and is undefined with anything not descended from TObject.

Unless there's an alternative - has any variation of the STL been
ported to Pascal?

Re:TSortedCollections = memory leaks?


On 16 Mar 2000 02:36:01 GMT, wso...@attglobal.net (William Sonna)
wrote:

Quote
> This (bad) example should illustrate the question:
> procedure MyTable.MyInsert(Item:pointer);
> var i:integer;
> begin
> i:=self.count;
> self.insert(Item);          {the problem is that Item is a pointer;
> not necessarily an object}
> if self.count=i then                            {insert failed;
> dispose pointer}
>   dispose(PObject(Item),done)    { DANGEROUS typecast - Item need not
>                                    point to an object descended from TObject}
>                 or
>   dispose(Item)      {even worse - any memory  allocated by Itemis lost}
> end;
> This may seem a bit contrived, but it at least illustrates the point
> that the creator of the object, not the collection, appears as if it
> should be disposing of the objects that are not inserted into the
> collection.  Only the creator knows the actual type, and it is easily
> accomlplished by the method you showed me.
> But when it comes time to dispose the collection, the collection (not
> the creator) MUST do the disposing via some variation of the
> dispose(pObject(Item),done) mechanism shown above, which is inherently
> unsafe, and is undefined with anything not descended from TObject.

Of course. If you load a standard TCollection with pointers which are
not pointing to descendants of TObject then

        dispose (pMyCollection,Done);

or

        MyCollection.Done;

is a programming error which will usually crash your machine because
the standard TCollection is *designed* to work only for descendants of
TObject. In fact the standard TCollection *does* free blindly each
pointer via dispose(PObject(p),Done).

But this does not mean that the TCollection family is limited to
TObject desendants only. You may adapt it to any object family of your
own or to any data type you want - with a little more work than with
the C++ STL 'vector' template. Instead of just defining

        vector <mytype> x[100];

in C++ you have to define a new collection type derived from
TCollection and to override the virtual FreeItem method:

The default FreeItem is defined as:

        procedure.TCollection.FreeItem(Item:Pointer);
        begin
          if Item<>NIL then dispose(PObject(Item),Done)
        end;

If you want to have a collection for an object family (with base type
TBase) or to a specific non-object type TMyType you have to define a
new collection type.

type
  TMyCollection = object(TCollection)
    procedure FreeItem(Item:Pointer);virtual;
  end;

  procedure TMyCollection.FreeItem(Item:Pointer);
  begin
    if Item<>NIL then dispose(PBase(Item),Done)
  end;

or

  procedure TMyCollection.FreeItem(Item:Pointer);
  begin
    if Item<>NIL then dispose(PMyType(Item))
  end;

in case of a non-object type.

Now the Collection has the necessary "knowledge" and

        FreeItem(Item);

does what should be done provided that Item is actually pointing to a
data object of the type(s) the collection has been adapted to. In your
example it is still your reponsability that Item does point to a data
object this specific Collection is made for.

In real life you would never do this for an object family because you
usually would derive your Base object from TObject and then it *is* a
TObject family and you may use the default TCollection for it.

Regards
Horst

Re:TSortedCollections = memory leaks?


On Sun, 15 Mar 3900 20:44:22, "Jason Burgon"

Quote
<ja...@jayman.demon.co.uk> wrote:
> William Sonna <wso...@attglobal.net> wrote in message
> news:05C6FUhLDNUU-pn2-S2dLyQWSHLod@bill...
> > I am wondering exactly how one prevents memory leaks when using
> > TSortedCollections.

> > I understand that the collections themselves store pointers to unique
> > objects located via the Compare virtual method called by
> > "TSortedCollection.Insert(p)"; where p is a pointer to a descendent of
> > TObject.

> > However, if p is not inserted into the collection because there is an
> > existing object that Compares identically , what happens to the
> > pointer?  Does the TSortedCollection dispose of it cleanly,

> No.

> > or does the calling routine have to dispose of it?

> Yes. This is a design error. If you have the TV source code you might want
> to change the TCollection.Insert and AtInsert virtual methods (and therefore
> all overridend descendant methods) into a boolean function that returns TRUE
> if the object is inserted. TSortedCollection.Insert then becomes:

> function TSortedCollection.Insert(Item: Pointer): Boolean;
> var
>   I: Integer;
> begin
>   Insert := False;
>   if not Search(KeyOf(Item), I) or Duplicates
>    then Insert := AtInsert(I, Item);
> end;

> function TCollection.AtInsert(Index: Integer; Item: Pointer): Boolean;
> assembler;
> asm
>   [Same a original down to the @@2: label, then ]
> @@2:    MOV     AX,WORD PTR [Item+2]
>         STOSW
>         MOV     AX,WORD PTR [Item]
>         STOSW
>         CLD
>         MOV     AL,1
>         JMP     @@6

> @@3:    MOV     AX,coIndexError
>         JMP     @@5
> @@4:    MOV     AX,coOverflow
>         MOV     BX,CX
> @@5:    CALL    CollectionError
>         XOR     AX,AX
> @@6:
> end;

> Now you can have code like the following:

> if not MyCollection.Insert(P)
>  then Dispose(P);

Three questions:

1.  Is there an advantage to recoding AtInsert to return a boolean ?
Since I don't have the code to TurboVision I don't know if this change
to AtInsert would break anything else.

2.  I am using a 32-bit compiler (Virtual Pascal) - is the assembler
AtInsert going to work correctly?

3.  I am aware that there is a FreeVision library which replicates
some or all of the TurboVision library.  Are you using / have you used
/ have you heard anything about the reliability of / the collections
it implements?

Re:TSortedCollections = memory leaks?


Quote
William Sonna <wso...@attglobal.net> wrote in message

news:05C6FUhLDNUU-pn2-khdPIcodM0UC@bill...

Quote
> But when it comes time to dispose the collection, the collection (not
> the creator) MUST do the disposing via some variation of the
> dispose(pObject(Item),done) mechanism shown above, which is inherently
> unsafe, and is undefined with anything not descended from TObject.

You're miissing one of the points about a TCollection. The
TCollection.FreeItem virtual method can correctly dispose of any TObject
descendant. Therefore, if your collection contains TObject descendants, you
don't need to override FreeItem, GetItem or PutItem. If your collection does
not contain TObject descendants then you must create (in your case) a
TSortedCollection derivative that contains a FreeItem (and GetItem and
PutItem if you're loading/saving to a stream) method that is appropriate for
the items contained in your collection.

So to sum up: TCollections assume they contain TObject derivatives, but can
be made to handle other item types by overriding the TCollection.XxxxItem
methods. Take a look at TStringCollection for an example of a sorted
collection that does not hold TObjects.

--
Jay

Jason Burgon - Author of Graphic Vision
New version 2 now available from:
http://www.jayman.demon.co.uk

Re:TSortedCollections = memory leaks?


On Sun, 16 Mar 3900 05:45:48, "Jason Burgon"

Quote
<ja...@jayman.demon.co.uk> wrote:
> William Sonna <wso...@attglobal.net> wrote in message
> news:05C6FUhLDNUU-pn2-khdPIcodM0UC@bill...

> > But when it comes time to dispose the collection, the collection (not
> > the creator) MUST do the disposing via some variation of the
> > dispose(pObject(Item),done) mechanism shown above, which is inherently
> > unsafe, and is undefined with anything not descended from TObject.

> You're miissing one of the points about a TCollection. The
> TCollection.FreeItem virtual method can correctly dispose of any TObject
> descendant. Therefore, if your collection contains TObject descendants, you
> don't need to override FreeItem, GetItem or PutItem. If your collection does
> not contain TObject descendants then you must create (in your case) a
> TSortedCollection derivative that contains a FreeItem (and GetItem and
> PutItem if you're loading/saving to a stream) method that is appropriate for
> the items contained in your collection.

Absolutely true.  And their use will still result in a memory leak if
any object not inserted into the collection is not disposed elsewhere.

Since Insert is a procedure rather than a function returning type
boolean, the only clean verification of insertion results in a double
Search per insertion, which I find annoyingly wasteful.  So I am going
to cheat and see if Count changed.

Add this to the fact that TCollections are hopelessly unsafe and pass
untyped pointers as parameters, and I'll think you'll agree that they
break just about every rule in the object-oriented book.

Nonetheless, they do work (however clumsily), so give 'em a C (not
even a C++).

Quote
> So to sum up: TCollections assume they contain TObject derivatives, but can
> be made to handle other item types by overriding the TCollection.XxxxItem
> methods. Take a look at TStringCollection for an example of a sorted
> collection that does not hold TObjects.

Its a shame we don't have any nice clean containers to go with our
nice clean programming language.

Re:TSortedCollections = memory leaks?


On 18 Mar 2000 03:01:45 GMT, wso...@attglobal.net (William Sonna)
wrote:

Quote

> Since Insert is a procedure rather than a function returning type
> boolean, the only clean verification of insertion results in a double
> Search per insertion, which I find annoyingly wasteful.  So I am going
> to cheat and see if Count changed.

Why don't you use the function I proposed ?

Quote
> Add this to the fact that TCollections are hopelessly unsafe and pass
> untyped pointers as parameters, and I'll think you'll agree that they
> break just about every rule in the object-oriented book.
> Nonetheless, they do work (however clumsily), so give 'em a C (not
> even a C++).
> Its a shame we don't have any nice clean containers to go with our
> nice clean programming language.

Sorry, partner. This rant makes me a little bit angry.

If you would have the slightest idea of OO programming you would know
that it is impossible to write a fully working container which adapts
itself "automatically" to the type you want it to use for if the
programming language doesn't support templates, i.e. "type
parameters".

Then you could write something like

type
  MyType = record ..... end;
var
  C : TCollection <MyType>;

and everything would be plain sailing. If this device would be
available then every interface function of TCollection having a plain
"pointer" as parameter or return value would turn itself automagically
into a function having a "^MyType" as a parameter or return value.

So the "shame" that we have this "clumsy" and unsafe interface is the
fault of "our nice clean programming language" and not the fault of
the "ignorant" implementors.

If this template mechanism is not available it is very easy to create
a clean interface with a minimum of work.

Just overload the FreeItem procedure and define _your_ interface to
the collection:

procedure foo( p : PMyType);

Inside this procedure you call Coll.Insert(p) and FreeItem(p) if you
need it.

But please don't complain. This would be only an "optical" device. You
could still call foo with a plain type "pointer" as a parameter and
the whole thing would be as "unsafe" as before.

But why for heavens sake would you ever call Insert with a

        var p:pointer

??? The plain "pointer" is the only variable type which would break
_every_ interface because by definition of Borland Pascal the generic
'pointer' type is the only type which you may assign to _any_ pointer
parameter. Otherwise there is no pointer type with which you could
call foo except ^MyType or a pointer to a type derived from MyType if
MyType is an object type. Isn't this enough ? Never create pointers of
type 'pointer' if you work with MyType and a Collection adapted to
MyType and everything is plain sailing.

Regards
Horst

Re:TSortedCollections = memory leaks?


On Sun, 18 Mar 3900 08:14:13, horst.krae...@t-online.de (Horst

Quote
Kraemer) wrote:
> On 18 Mar 2000 03:01:45 GMT, wso...@attglobal.net (William Sonna)
> wrote:

> > Since Insert is a procedure rather than a function returning type
> > boolean, the only clean verification of insertion results in a double
> > Search per insertion, which I find annoyingly wasteful.  So I am going
> > to cheat and see if Count changed.

> Why don't you use the function I proposed ?

> > Add this to the fact that TCollections are hopelessly unsafe and pass
> > untyped pointers as parameters, and I'll think you'll agree that they
> > break just about every rule in the object-oriented book.

> > Nonetheless, they do work (however clumsily), so give 'em a C (not
> > even a C++).

> > Its a shame we don't have any nice clean containers to go with our
> > nice clean programming language.

> Sorry, partner. This rant makes me a little bit angry.

> If you would have the slightest idea of OO programming you would know.......

If you had the slightest idea of OO programming you would know that
what you have proposed is so obvious and simplistic that it does not
merit further discussion.

You would also know that it is a classic "workaround" of a design
mistake.

Now buzz off.

Re:TSortedCollections = memory leaks?


On 18 Mar 2000 14:14:53 GMT, wso...@attglobal.net (William Sonna)
wrote:

Quote
> You would also know that it is a classic "workaround" of a design
> mistake.

It is not a disign mistake but a limitation of the programming
language - as defined by Borlands.

Quote
> Now buzz off.

Sir, you happened to forget your education.

Regards
Horst

Other Threads