Jump to content
Zakaria

tag as String

Recommended Posts

8 minutes ago, Zakaria said:

how add property tagString to all delphi Compememet

If you want to be able to edit this new property in the IDE Object Inspector I don't see a way to do this since it would require not only a change of the source code of the TComponent class but also a rebuild of all design and run-time packages that use TComponent, and you do not have the source code for all of them, as far as I know.

 

If using this property in code would be enough there may be a way to fake it using a class helper for TComponent that uses the existing Tag property to store an index of the actual string held in some global container, like a TStringlist.

Share this post


Link to post

@PeterBelow It is even possible to inject new properties to the Object Inspector. In Cmon.DataSense.Design.pas I use this technique to add a DataSource and DataField property to supported controls. The additional data is stored in a special component (TDataSense) using a dictionary internally:

  • Like 4

Share this post


Link to post
3 hours ago, Zakaria said:

how add property tagString to all delphi Compememet

The first step is to re-think whatever series of choices led to this abysmal state.

  • Like 3

Share this post


Link to post
49 minutes ago, Brandon Staggs said:

whatever series of choices led to this abysmal state. 

*zing* 🔥

Share this post


Link to post
7 hours ago, Zakaria said:

how add property tagString to all delphi Compememet

You can't add a new property to all components.  But, the existing Tag property is a NativeInt, so it can hold a String (which is just a pointer under the hood), eg:

var tagStr: NativeInt := 0;
String(Pointer(tagStr)) := SomeString;
SomeComponent.Tag := tagStr;

...

var tagStr := SomeComponent.Tag;
SomeString := String(Pointer(tagStr));

Just make sure you release the String properly before the Component is destroyed, eg:

var tagStr := SomeComponent.Tag;
String(Pointer(tagStr)) := '';

 

  • Like 2

Share this post


Link to post
17 minutes ago, Remy Lebeau said:

You can't add a new property to all components.  But, the existing Tag property is a NativeInt, so it can hold a String (which is just a pointer under the hood), eg:


var tagStr: NativeInt := 0;
String(Pointer(tagStr)) := SomeString;
SomeComponent.Tag := tagStr;

...

var tagStr := SomeComponent.Tag;
SomeString := String(Pointer(tagStr));

Just make sure you release the String properly before the Component is destroyed, eg:


var tagStr := SomeComponent.Tag;
String(Pointer(tagStr)) := '';

 

What could possibly go wrong

  • Haha 8

Share this post


Link to post

This might be an even worse idea.... 

{$IFDEF Win64}
type TBadIdea = array [0..7] of AnsiChar;
{$ENDIF}
{$IFDEF Win32}
type TBadIdea = array [0..3] of AnsiChar;
{$ENDIF}

function ReadTagString(Tag: NativeInt): String;
var 
  BadIdea: TBadIdea absolute Tag;
begin
  Result := string(BadIdea);
end;

function WriteTagString(Tag: String): NativeInt;
var 
  BadIdea: TBadIdea absolute Result;
begin
  for var i := 1 to SizeOf(NativeInt) do
    if i <= Length(Tag) then
      BadIdea[i-1] := AnsiChar(Tag[i])
    else
      BadIdea[i-1] := #0;
end;

procedure TFormEvenWorse.btnSetClick(Sender: TObject);
begin
  Tag := WriteTagString(edtValue.Text);
  edtValue.Text := '';
end;

procedure TFormEvenWorse.btnReadClick(Sender: TObject);
begin
  edtValue.Text := string(ReadTagString(Tag));
end;

1540062048_Thinkiftheyshould.thumb.jpeg.9b7911d94da75798589a2633de4160d4.jpeg

 

There should be better handling of Ansi vs Unicode strings. Technically if you are only going to store characters you could make more efficient use of the bytes. 

Edited by Jim McKeeth

Share this post


Link to post
17 hours ago, Uwe Raabe said:

@PeterBelow It is even possible to inject new properties to the Object Inspector. In Cmon.DataSense.Design.pas I use this technique to add a DataSource and DataField property to supported controls. The additional data is stored in a special component (TDataSense) using a dictionary internally:

This is the right answer. You can see an example of this when you put a component on a TRelativePanel. Notice how there are additional properties after Width in the Object Inspector...

 

image.thumb.png.7b4958d0dd64bef014ca4b1f03498cea.png

Share this post


Link to post

If the OP would explain the original motivation behind this, then we could propose proper solutions to the actual problem. 

 

This is a canonical XY question. 

  • Like 1

Share this post


Link to post
42 minutes ago, Jim McKeeth said:

This might be an even worse idea.

Very bad idea. ReadTagString() is not so bad, but IDK what WriteTagString() is trying to accomplish. It is converting characters into bytes, completely ignoring the pointer that is inside the string. It is not the inverse of ReadTagString().

Edited by Remy Lebeau

Share this post


Link to post
33 minutes ago, Remy Lebeau said:

IDK what WriteTagString() is trying to accomplish. It is converting characters into bytes, completely ignoring the pointer that is inside the string.

 

739930060_BadIdea.gif.3b23d5e08180c7f82c68117920830100.gif

 

Convert the string into an array of AnsiChar, which is just an array of bytes, which is absolute to a NativeInt....

type TBadIdea = array [0..7] of AnsiChar;

So there is no pointer....

 

image.png.1322a8a1c1ba60c49dbe6215856c16dc.png

 

It is bytes all the way down....

Edited by Jim McKeeth

Share this post


Link to post

I think the best way is to have a singleton, that initialized very early in the project (preferably right after MM and before forms), this singleton will hook TComponent.Create and Destroy, grabbing its address (value/pointer) and add it to internally managed list, this list have the component associated with an extra data, extra data could be a string, integer, record.. and even classes.

 

This will solve/remove the need to manually managing this extra data (eg. string), also ensure memory integrity for this extra data without interfering with the associated component in anyway, on top of that helper(s) for specific components can be used for easier and simplified use.

Share this post


Link to post

I sometimes need to use the "Tag" property to store a string, and I've noticed that the number of instances and changes are minimal so I could store them in memory during the application lifetime. That's why I've opted with a global singleton which:

  1. Returns a string object when asked, and keeps the instance in memory while the application is running
  2. Releases every instances once the application is closed

Even if this is not an optimal solution, this is the "cleanest" way I could think of at the time. Something like:

 

uses Spring.Collections;

// ...

type TStrObj = class(TObject)
  private
    FValue: string;
  public
    constructor Create(const aValue: string = '');
    property Value: string read FValue write FValue;
  end;
  
type FStrObjects: IList<TStrObj>;

// ...

constructor TSomeSingleton.Create();
begin
  FStrObjects := TCollections.CreateObjectList<THndStrObj>(True);
end;

function TSomeSingleton.GetStringObject(const aValue: string): TStrObj;
begin
  // Create the object
  Result := TStrObj.Create(aValue);
  // Add it to the list
  FStrObjects.Add(Result);
end;

// ...

// Usage:
SomeComponent.Tag := NativeInt(Pointer(TSomeSingleton.GetStringObject('Some String Value')));

 

Share this post


Link to post
On 10/22/2024 at 12:52 PM, Uwe Raabe said:

@PeterBelow It is even possible to inject new properties to the Object Inspector. In Cmon.DataSense.Design.pas I use this technique to add a DataSource and DataField property to supported controls. The additional data is stored in a special component (TDataSense) using a dictionary internally:

Good to know one can learn something new even at my age :)...

  • Like 2

Share this post


Link to post
12 hours ago, Jim McKeeth said:

Convert the string into an array of AnsiChar, which is just an array of bytes, which is absolute to a NativeInt....

So there is no pointer....

True.  Now I see what the code is doing.  It's stuffing the first 4 (32bit) or 8 (64bit) characters of the string directly into the NativeInt's bytes.  So, if the string is longer than the NativeInt can hold, the remaining characters get lost.  Storing the string's pointer into the NativeInt would preserve the entire string, but at the cost of having to manage its reference count properly.

Edited by Remy Lebeau

Share this post


Link to post

I've gone even deeper down the rabbit whole and referenced object instances through the tag... I am not proud of it.

Share this post


Link to post
19 minutes ago, Lars Fosdal said:

I've gone even deeper down the rabbit whole and referenced object instances through the tag... I am not proud of it.

That is actually quite common, and is why the Tag is a NativeInt to begin with.

Share this post


Link to post

"rabbit whole" ... /sigh

 

Quote

That is actually quite common, and is why the Tag is a NativeInt to begin with.

Yeah, I guess it is... still, it is a far cry from clean code.

Share this post


Link to post
36 minutes ago, Remy Lebeau said:

why the Tag is a NativeInt to begin with

Except it wasn't... Prior to XE2 it was an integer.

Share this post


Link to post
47 minutes ago, Anders Melander said:

Except it wasn't... Prior to XE2 it was an integer.

Probably because at that time pointers were still 32 bit only.

Share this post


Link to post
1 minute ago, Uwe Raabe said:

Probably because at that time pointers were still 32 bit only.

The point was that since it was originally (refs "to begin with") declared as an integer, not a nativeint, the original intent obviously wasn't that it could be used for pointers. It just so happened that it could.

Share this post


Link to post
5 hours ago, Anders Melander said:

The point was that since it was originally (refs "to begin with") declared as an integer, not a nativeint, the original intent obviously wasn't that it could be used for pointers. It just so happened that it could.

I'll rephrase.

Prior to the introduction of 64bit, the Tag was an Integer and pointers fit in an Integer, so it was common to store pointers in a Tag. When 64bit was added, the Tag became a NativeInt to preserve existing code in common use. 

  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×