Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2006-09

Propose: change TComponent.Tag from longint to PtrInt

23 messages · 14 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Luiz Americo

I'd like to propose change TComponent.Tag type from longint to PtrInt. I know it's not a good programing practice, but sometimes this property can be used to hold pointers.
In 32bit there's not many problems since pointer and longint have the same size, but the thing changes in 64 bit.
The choice of PtrInt has the benefit of not modifying nothing in 32bit, while in 64bit you getting a integer of the same size of pointers.

What do you think?

Luiz

Reply by email

peter green
to Luiz Americo
Show quoted text 9 lines
I'd like to propose change TComponent.Tag type from longint to PtrInt. I know it's not a good programing practice, but sometimes this property can be used to hold pointers. In 32bit there's not many problems since pointer and longint have the same size, but the thing changes in 64 bit. The choice of PtrInt has the benefit of not modifying nothing in 32bit, while in 64bit you getting a integer of the same size of pointers. What do you think?

do it, this was a very common use of tag in lots of delphi code and i can't see any problems with having it as a 64 bit type.

Reply by email

Martin Schreiber
to Luiz Americo: I'd like to propose change TComponent.Tag type from longint to PtrInt. I know it's not a…
Luiz Americo wrote 10 lines
I'd like to propose change TComponent.Tag type from longint to PtrInt. I know it's not a good programing practice, but sometimes this property can be used to hold pointers. In 32bit there's not many problems since pointer and longint have the same size, but the thing changes in 64 bit. The choice of PtrInt has the benefit of not modifying nothing in 32bit, while in 64bit you getting a integer of the same size of pointers. What do you think?

Agreed.

Martin

Reply by email

Mattias Gaertner
to Martin Schreiber
Show quoted text 14 lines
On Monday 04 September 2006 02.55, Luiz Americo wrote: > I'd like to propose change TComponent.Tag type from longint to > PtrInt. I know it's not a good programing practice, but sometimes > this property can be used to hold pointers. > In 32bit there's not many problems since pointer and longint have > the same size, but the thing changes in 64 bit. > The choice of PtrInt has the benefit of not modifying nothing in > 32bit, while in 64bit you getting a integer of the same size of > pointers. > > What do you think? > Agreed.

Me2.

Just one disadvantage:
The Tag is published.
If someone stores a 64bit value and the component is stored, the stream can not be read under 32bit anymore.

Mattias

Reply by email

Daniël Mantione
to Mattias Gaertner

Op Mon, 4 Sep 2006, schreef Mattias Gaertner:

Show quoted text 6 lines
Me2. Just one disadvantage: The Tag is published. If someone stores a 64bit value and the component is stored, the stream can not be read under 32bit anymore.

...which makes it impossible to use streams for anything derived from Tcomponent, which is really a lot. I don't think we should break this feature to allow broken code to work.

Daniël

Reply by email

Vincent Snijders
to Daniël Mantione
Daniël Mantione wrote 13 lines
Op Mon, 4 Sep 2006, schreef Mattias Gaertner: > Me2. > > Just one disadvantage: > The Tag is published. > If someone stores a 64bit value and the component is stored, > the stream can not be read under 32bit anymore. ...which makes it impossible to use streams for anything derived from Tcomponent, which is really a lot. I don't think we should break this feature to allow broken code to work.

Really a lot? Can you give examples where numbers > maxint are streamed?

Vincent

Reply by email

Mattias Gaertner
to Vincent Snijders
Show quoted text 17 lines
Daniël Mantione schreef: > > Op Mon, 4 Sep 2006, schreef Mattias Gaertner: > >> Me2. >> >> Just one disadvantage: >> The Tag is published. >> If someone stores a 64bit value and the component is stored, >> the stream can not be read under 32bit anymore. > > ...which makes it impossible to use streams for anything derived > from Tcomponent, which is really a lot. I don't think we should > break this feature to allow broken code to work. Really a lot? Can you give examples where numbers > maxint are streamed?

I agree with Luiz, that there is a lot more code using tag as storage for a pointer, than code streaming big numbers. So for compatibility it should be PtrInt.

Mattias

Reply by email

peter green
to Daniël Mantione: Op Mon, 4 Sep 2006, schreef Mattias Gaertner: ...which makes it impossible to use streams…
Show quoted text 11 lines
> Me2. > > Just one disadvantage: > The Tag is published. > If someone stores a 64bit value and the component is stored, > the stream can not be read under 32bit anymore. ...which makes it impossible to use streams for anything derived from Tcomponent, which is really a lot. I don't think we should break this feature to allow broken code to work.

ahh the joys of conflicting requirements,

I don't consider storing a pointer in the tag is broken code, it was considered the accepted way to maintain a pointer to your custom data from a standard control and there is often no simple alternative.

how does streaming of components work? would it be possible to make tag able to accomodate a pointer while only writing the least significant 32 bits to a stream?

Reply by email

Daniël Mantione
to peter green

Op Mon, 4 Sep 2006, schreef peter green:

Show quoted text 17 lines
> > > Me2. > > > > Just one disadvantage: > > The Tag is published. > > If someone stores a 64bit value and the component is stored, > > the stream can not be read under 32bit anymore. > > ...which makes it impossible to use streams for anything derived from > Tcomponent, which is really a lot. I don't think we should break this > feature to allow broken code to work. ahh the joys of conflicting requirements, I don't consider storing a pointer in the tag is broken code, it was considered the accepted way to maintain a pointer to your custom data from a standard control and there is often no simple alternative.

In 16 bit days it was considered acceptable to typecast pointers into records and just increase their offset. However, it was not portable. Making your code portable requires some effort. This kind of code is just one example of things you should not do in portable code. If Borland had intended this as a pointer , they would have declared it as a pointer.

Of course there is a simple solution, derive a class and add fields to taste.

how does streaming of components work? would it be possible to make tag able to accomodate a pointer while only writing the least significant 32 bits to a stream?

Yes, this could be an acceptable solution, since you can never stream pointers anyway.

However, I am still worried about a slippery slope. There exists many data types with longints in them and we simply cannot declare them all longint just because someone might store a pointer in it.

Daniël

Reply by email

Daniël Mantione
to Vincent Snijders: Really a lot? Can you give examples where numbers > maxint are streamed? Vincent

Op Mon, 4 Sep 2006, schreef Vincent Snijders:

Show quoted text 16 lines
Daniël Mantione schreef: > > Op Mon, 4 Sep 2006, schreef Mattias Gaertner: > > > Me2. > > > > Just one disadvantage: > > The Tag is published. > > If someone stores a 64bit value and the component is stored, > > the stream can not be read under 32bit anymore. > > ...which makes it impossible to use streams for anything derived from > Tcomponent, which is really a lot. I don't think we should break this > feature to allow broken code to work. Really a lot? Can you give examples where numbers > maxint are streamed?

Even if you stream a zero it breaks.

Daniël

Reply by email

Michael Van Canneyt
to peter green: ahh the joys of conflicting requirements, I don't consider storing a pointer in the tag…
peter green wrote 17 lines
> >> Me2. >> >> Just one disadvantage: >> The Tag is published. >> If someone stores a 64bit value and the component is stored, >> the stream can not be read under 32bit anymore. > > ...which makes it impossible to use streams for anything derived from > Tcomponent, which is really a lot. I don't think we should break this > feature to allow broken code to work. ahh the joys of conflicting requirements, I don't consider storing a pointer in the tag is broken code, it was considered the accepted way to maintain a pointer to your custom data from a standard control and there is often no simple alternative.

Accepted only because there was no other way.

The only correct way is to introduce an additional field 'UserData', of type Pointer.

A pointer cannot be stored anyway at design-time, so it does not need to be published.

Remains the decision where you introduce this pointer: TComponent, TControl.

IMHO, you should introduce TLCLComponent, add it there, and descend all LCL components from that.

Michael.

Reply by email

Vincent Snijders
to Daniël Mantione: Op Mon, 4 Sep 2006, schreef Vincent Snijders: Even if you stream a zero it breaks. Daniël
Daniël Mantione wrote 19 lines
Op Mon, 4 Sep 2006, schreef Vincent Snijders: > Daniël Mantione schreef: >> Op Mon, 4 Sep 2006, schreef Mattias Gaertner: >> >>> Me2. >>> >>> Just one disadvantage: >>> The Tag is published. >>> If someone stores a 64bit value and the component is stored, >>> the stream can not be read under 32bit anymore. >> ...which makes it impossible to use streams for anything derived from >> Tcomponent, which is really a lot. I don't think we should break this >> feature to allow broken code to work. > Really a lot? Can you give examples where numbers > maxint are streamed? Even if you stream a zero it breaks.

Then Lazarus uses an advanced streaming mechanism, which looks at the size of the value and decides to 1, 2, 4 or 8 bytes for an int64, when streaming a form.

I thought this was more or less standard behaviour.

Vincent

Reply by email

Marc Weustink
to Michael Van Canneyt: Accepted only because there was no other way. The only correct way is to introduce an…
Michael Van Canneyt wrote 36 lines
On Mon, 4 Sep 2006, peter green wrote: > >> >>> Me2. >>> >>> Just one disadvantage: >>> The Tag is published. >>> If someone stores a 64bit value and the component is stored, >>> the stream can not be read under 32bit anymore. >> >> ...which makes it impossible to use streams for anything derived from >> Tcomponent, which is really a lot. I don't think we should break this >> feature to allow broken code to work. > ahh the joys of conflicting requirements, > > I don't consider storing a pointer in the tag is broken code, it was > considered the accepted way to maintain a pointer to your custom data > from a > standard control and there is often no simple alternative. Accepted only because there was no other way. The only correct way is to introduce an additional field 'UserData', of type Pointer. A pointer cannot be stored anyway at design-time, so it does not need to be published. Remains the decision where you introduce this pointer: TComponent, TControl. IMHO, you should introduce TLCLComponent, add it there, and descend all LCL components from that.

TLCLComponent is already exists (it's only not committed yet. Adding a UserData to it is maybe an idea.
THis will work for most compoenents, except DB components for instance.

Marc

Reply by email

Marc Weustink
to Marc Weustink
Marc Weustink wrote 41 lines
Michael Van Canneyt wrote: > > > On Mon, 4 Sep 2006, peter green wrote: > >> >>> >>>> Me2. >>>> >>>> Just one disadvantage: >>>> The Tag is published. >>>> If someone stores a 64bit value and the component is stored, >>>> the stream can not be read under 32bit anymore. >>> >>> ...which makes it impossible to use streams for anything derived from >>> Tcomponent, which is really a lot. I don't think we should break this >>> feature to allow broken code to work. >> ahh the joys of conflicting requirements, >> >> I don't consider storing a pointer in the tag is broken code, it was >> considered the accepted way to maintain a pointer to your custom data >> from a >> standard control and there is often no simple alternative. > > Accepted only because there was no other way. > > The only correct way is to introduce an additional field 'UserData', > of type Pointer. > > A pointer cannot be stored anyway at design-time, so it does not need > to be published. > > Remains the decision where you introduce this pointer: TComponent, > TControl. > > IMHO, you should introduce TLCLComponent, add it there, and descend > all LCL components from that. TLCLComponent is already exists (it's only not committed yet. Adding a UserData to it is maybe an idea. THis will work for most compoenents, except DB components for instance.

Whoops, as Vincent reminded me, it is already there (I confused it with another uncommitted class)

Marc

Reply by email

Mattias Gaertner
to Vincent Snijders: Then Lazarus uses an advanced streaming mechanism, which looks at the size of the value…
Show quoted text 27 lines
Daniël Mantione schreef: > > Op Mon, 4 Sep 2006, schreef Vincent Snijders: > >> Daniël Mantione schreef: >>> Op Mon, 4 Sep 2006, schreef Mattias Gaertner: >>> >>>> Me2. >>>> >>>> Just one disadvantage: >>>> The Tag is published. >>>> If someone stores a 64bit value and the component is stored, >>>> the stream can not be read under 32bit anymore. >>> ...which makes it impossible to use streams for anything derived >>> from Tcomponent, which is really a lot. I don't think we should >>> break this feature to allow broken code to work. >> Really a lot? Can you give examples where numbers > maxint are >> streamed? > > Even if you stream a zero it breaks. > Then Lazarus uses an advanced streaming mechanism, which looks at the size of the value and decides to 1, 2, 4 or 8 bytes for an int64, when streaming a form. I thought this was more or less standard behaviour.

It is.

Mattias

Reply by email

peter green
to Daniël Mantione: Op Mon, 4 Sep 2006, schreef Mattias Gaertner: ...which makes it impossible to use streams…
...which makes it impossible to use streams for anything derived from Tcomponent, which is really a lot. I don't think we should break this feature to allow broken code to work.

BTW even borland themselves seemed to promote its use for storing pointers

from delphi 3 help:
"Tag has no predefined meaning to Delphi. The Tag property is provided for the convenience of storing additional integer value or pointer information for special needs in an application. For example, use the Tag property when implementing case statements with a component."

Reply by email

Luiz Americo Pereira Camara
to Daniël Mantione: Op Mon, 4 Sep 2006, schreef peter green: In 16 bit days it was considered acceptable to…
Daniël Mantione wrote 10 lines
In 16 bit days it was considered acceptable to typecast pointers into records and just increase their offset. However, it was not portable. Making your code portable requires some effort. This kind of code is just one example of things you should not do in portable code. If Borland had intended this as a pointer , they would have declared it as a pointer. Of course there is a simple solution, derive a class and add fields to taste.

For non visual components it's simple, but for LCL/Visual components it's a bit more complicated if you plan to use them with the GUI designer: you will have to write a package and then install it. In other cases are worse. Look at the TPage example (which in fact was the motivation to suggest this modification since i want a place to store a pointer in it): i would have to write a TPage descendant and also modify the Lazarus IDE to use it when designing the GUI.

Luiz

Reply by email

Jason P Sage

I have seen this issue come up with my own classes, and I get warnings about portability from the compiler - the whole 32 to 64 bit warnings.

The point being is that you can often do some pretty neat things with pointer math etc.

Concerning this particular thread - and this discussion about changing Lazarus just to support it... does seem particular over kill.

One work around you might consider is to make a collection of sorts in another class where this TAG you are referring to is a unique identifier, that when passed to this vaporware collection I'm talking about, would return the actual pointer to the object in question.

Personally - I have this set of classes I developed from the ground up with some help Michael Van Canneyt like 4 years ago. I was working on a double linked list that is a class that handles navigation through, crating, and destroying, classes (linked list items) that works very much like a recordset e.g. like navigating through ADO (Microsoft ODBC wrapper) in that you can movefirst, move last, move next, move previous, and various search things, sorting etc.

This set of classes has come a long way - but I'm always reminded of the 32/64 bit issue. However the port is simple because only the very core that does some pointer taboo wizardry is effected - and other objects I manage with these classes are done with Unique identifiers.

Example:

SFC: SFC_DL;
SFC:= SFC_DL.Create;
SFC.AddItem SFC_DLITEM.Create;
Writeln('Current Item Unique Identifeir:',SFC.Item.UID, ' or ');
Writeln('Direct: ',SFC_DLITEM(SFC.lpItem).UID);

That gives an integer unique identifier that is portable and managed like A DBMS autonumber.

Then to attach objects I have as another property of SFC_DLITEM, a pointer:

SFC_DLITEM(SFC.lpItem).PTR:= TMyCustomObject.Create;

Now I can search for its pointer using the UID. Additionally, these classes Have ansistring fields like saName, and saDesc, you can search on also:

If SFC_DL.bFoundItem_saName('MyClass Name', false) then ... (false=not case sensitive)

My point is not to tout my classes - but to suggest that keeping a list of portable unique identifiers, coupled with pointers, and a method to find them quickly, might allow you to use the TAG in TComponent as it is, with a little rework of how you use TAG - but still accomplishing the same thing.

Have a great day people!
Jason P Sage

Reply by email

Bram Kuijvenhoven
to Michael Van Canneyt: Accepted only because there was no other way. The only correct way is to introduce an…
Michael Van Canneyt wrote 9 lines
> I don't consider storing a pointer in the tag is broken code, it was > considered the accepted way to maintain a pointer to your custom data > from a > standard control and there is often no simple alternative. Accepted only because there was no other way. The only correct way is to introduce an additional field 'UserData', of type Pointer.

I think this is an excellent solution. Casting Tags to pointer is/was bad practice anyway.

The Tag can still be used for association of user data with a component, but then as an index to a lookup table which holds the actual pointer. That is a portable solution (though it requires a little more memory and code). So adding a UserData field will be helpful.

If we really want to save memory usage, we can even let a UserData (public) property and the Tag (published) property use the same field. Tag will then be an interface to the lowest 32 bits of a pointer field, which is simply stored/read in a whole by UserData. But this is of course a hack which is more likely to confuse programmers and cause problems in the future, so I wouldn't particularly recommend it...

Regards,

Bram Kuijvenhoven

Reply by email

Aleš Katona
to Bram Kuijvenhoven

The problem here is that if you introduce a changing type to Tag, you're going to break streaming of the components between various platforms. However I think simplest would be to simply make the tag something huge for years to come, like Int64.

The idea with hidden union is nice tho. But you'll need to know how to stream it precisely and there's also endianness to take care of then.

Ales

Reply by email

Michael Van Canneyt
to Aleš Katona
Ale Katona wrote 7 lines
The problem here is that if you introduce a changing type to Tag, you're going to break streaming of the components between various platforms. However I think simplest would be to simply make the tag something huge for years to come, like Int64. The idea with hidden union is nice tho. But you'll need to know how to stream it precisely and there's also endianness to take care of then.

I've been following the discussion, and the tag property will remain what it is: a 32-bit integer. It was introduced by Delphi to mimic the Tag property which exists in VB, to allow it to be used in case statements. Changing it will probably cause more problems than it will solve.

In casu:

Typecasting 'Tag' to a pointer is a non-portable, ugly hack and we should not encourage such hacks.

If you want to store objects, use a TCollection, and store TCollectionItem.ID in the component tag. ID is a unique identifier, even when elements are added and removed from the collection.

Slightly more work, but clean and portable.

Michael.

Reply by email

Micha Nelissen
to Michael Van Canneyt
Michael Van Canneyt wrote 5 lines
If you want to store objects, use a TCollection, and store TCollectionItem.ID in the component tag. ID is a unique identifier, even when elements are added and removed from the collection. Slightly more work, but clean and portable.

And much slower.

Micha

Reply by email

Burkhard Carstens
to Michael Van Canneyt: I've been following the discussion, and the tag property will remain what it is: a 32-bit…
Michael Van Canneyt wrote 28 lines
On Wed, 6 Sep 2006, Ale Katona wrote: > The problem here is that if you introduce a changing type to Tag, > you're going to break streaming of the components between various > platforms. However I think simplest would be to simply make the tag > something huge for years to come, like Int64. > > The idea with hidden union is nice tho. But you'll need to know how > to stream it precisely and there's also endianness to take care of > then. I've been following the discussion, and the tag property will remain what it is: a 32-bit integer. It was introduced by Delphi to mimic the Tag property which exists in VB, to allow it to be used in case statements. Changing it will probably cause more problems than it will solve. In casu: Typecasting 'Tag' to a pointer is a non-portable, ugly hack and we should not encourage such hacks. If you want to store objects, use a TCollection, and store TCollectionItem.ID in the component tag. ID is a unique identifier, even when elements are added and removed from the collection. Slightly more work, but clean and portable. Michael.

In many situations, the collection would be overkill, so I'd also opt for the new public property "UserData : pointer" or better "TagPtr : pointer". This would make it harder to create code, that also compiles with delphi, but that doesn't bother me ;-)

Anyway, if one needs to compile with delphi, one could continue to use pointer(tag), as delphi is 32-bit only anyway ...
This way the user has the choice to write code that compiles with fpc32+delphi or fpc32+fpc64

regards
Burkhard

Reply by email