Frage

I am working on getting rid of shortstring. One of the many places shortstring is currently used within our programs is in records. Alot of these records are kept in AVL trees.

The AVL tree used is a generic one, holding a pointer to a number of bytes (ElemSize), which have worked well so far. The memory for each record in the AVL tree is allocated with GetMem, and copied with Move. However, with string being a pointer to a reference-counted structure, copying back the memory to a record no longer works, as the sting referenced is often freed (automatically by reference count). With only a pointer and a size of the "data block", I assume it is not possible to have the reference count of the strings increased.

I'm looking for a way to get the reference count of the stings to be taken into account when storing the record in a AVL tree.

Can I pass the record type to the tree constructor, then cast the pointer to this type and thus get the references increased? Or a similar fix, where I can isolate the changes to primarily be in the AVL unit and calls to it's constructor.

Current code for allocation of space to store the record in AVL; XData is a pointer to the record to be stored:

New(RootPtr); { create new memory space }
GetMem(RootPtr^.TreeData, ElemSize);
WITH RootPtr^ DO BEGIN
    { copy data }
    Move(XData^, RootPtr^.TreeData^, ElemSize);
War es hilfreich?

Lösung

In essence the question you are asking is:

How can I allocate, copy and deallocate a record when all I know about its type is its size?

The simple answer is that you can use GetMem, Move and FreeMem provided that the record does not contain managed types. You wish to work with records that contain Delphi strings, which are managed. And so your current approach using GetMem and Move does not suffice.

There are plenty of ways to solve this. You could write your own code to do reference counting, so long as you knew where in the record the managed types were. I don't recommend this. You could make your user data be a class and use polymorphism to help.

The option I'd like to discuss continues to support records and indeed allows the user to choose whatever type they like. The reasoning is as follows:

If the type contains managed types, then operating on it requires knowledge of the type. If the tree is to be generic, then it cannot have that knowledge. Ergo, the knowledge must be supplied by the user of the tree.

This leads you to events. Let the tree offer events that the user can supply handlers for. The types would look like this:

type
  PTreeNodeUserData = type Pointer;

  TTreeNodeCreateUserDataEvent = function: PTreeNodeUserData of object;
  TTreeNodeDestroyUserDataEvent = procedure(Data: PTreeNodeUserData) of object;
  TTreeNodeCopyUserDataEvent = procedure(Source, Dest: PTreeNodeUserData) of object;

Then you can arrange for your tree to publish events with these types that the user can subscribe to.

The point being that this allows the user of the tree to supply the missing knowledge about the user data type.

Andere Tipps

One of the main benefits of using records is the simplicity with which they can be copied (without using Move). So your best solution is to simply replace Move with a normal assignment operator :=. This will correctly consider the reference counts for all managed types involved.

Is there a particular reason you're not using the normal assignment operator?

PS: You need to ensure that the memory for all managed types (including long strings) is correctly initialised and finalised. I suggest you do some additional reading on the Initialize and Finalize routines.


The tree is general, it can hold a given lump of data. I hoped I could extend the functionality without making a new tree class per record.

In that case you need your "copy behaviour" to be variable depending on what it's working with. As couple of options:

  1. If your tree is wrapped in a class you can easily modify it to use a callback event to perform the copy operation. (This option might be easiest even if you first have to work on encapsulating the tree in a class.)

  2. Modify your nodes and/or data to be objects with polymorphic copy functionality. Then each subtype will know how to copy itself correctly, and you can write something along the lines of Root.TreeData := XData.CreateCopy;

If you are working at such a low level, and don't want compiler to help you, then you need to use PChar-strings instead of regular strings.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top