Lazarus

Programming => LCL => Topic started by: pascal111 on May 05, 2021, 01:48:27 pm

Title: Creating "TEdit" component dynamically
Post by: pascal111 on May 05, 2021, 01:48:27 pm
I'm trying to create "TEdit" component dynamically, but I see no result.


Code: Pascal  [Select][+][-]
  1.  
  2. var
  3.   Form1: TForm1;
  4.   e1:tedit;
  5.  
  6. procedure TForm1.FormCreate(Sender: TObject);
  7. begin
  8.  
  9.   e1:=tedit.Create(self);
  10.  
  11.   e1.Height:=28;
  12.   e1.Width:=127;
  13.   e1.Left:=95;
  14.   e1.Top:=56;
  15.  
  16.   e1.Text:='Hello World!';
  17.  
  18. end;
  19.  
  20. procedure TForm1.FormDestroy(Sender: TObject);
  21. begin
  22.   e1.Free;
  23. end;
  24.  
  25.  
Title: Re: Creating "TEdit" component dynamically
Post by: dseligo on May 05, 2021, 01:51:30 pm
You didn't set parent:
Code: Pascal  [Select][+][-]
  1. e1.Parent:=Self;
Title: Re: Creating "TEdit" component dynamically
Post by: pascal111 on May 05, 2021, 02:11:33 pm
It works!
Thanks!
Title: Re: Creating "TEdit" component dynamically
Post by: y.ivanov on May 05, 2021, 02:19:50 pm
One more thing, since you gave an Owner to the TEdit by:

Code: Pascal  [Select][+][-]
  1. e1:=tedit.Create(self);

You shall not free it at FormDestroy(). It will be freed by the Owner at the right time.
Title: Re: Creating "TEdit" component dynamically
Post by: pascal111 on May 05, 2021, 02:23:01 pm
I got it! thanks!
Title: Re: Creating "TEdit" component dynamically
Post by: pascal111 on May 05, 2021, 02:24:33 pm
By the way, is there a problem if I said:

Code: Pascal  [Select][+][-]
  1.  
  2. e1.create(self); // rather than     e1:=tedit.Create(self);
  3.  
  4.  
Title: Re: Creating "TEdit" component dynamically
Post by: y.ivanov on May 05, 2021, 02:39:15 pm
By the way, is there a problem if I said:

Code: Pascal  [Select][+][-]
  1. e1.create(self); // rather than     e1:=tedit.Create(self);
  2.  
It is not the same. The constructor is part of the class(TEdit), not the instance (e1). e1 is just a pointer internally.

Title: Re: Creating "TEdit" component dynamically
Post by: lucamar on May 05, 2021, 02:55:40 pm
By the way, is there a problem if I said:
Code: Pascal  [Select][+][-]
  1. e1.create(self); // rather than     e1:=tedit.Create(self);

When you do that what you're doing is basically just calling a method of the instance, rather than invoking the constructor of the class. Two very different concepts.
Title: Re: Creating "TEdit" component dynamically
Post by: pascal111 on May 05, 2021, 02:59:06 pm
I see now. I think the different between the two concepts can be used in advanced uses of components.
Title: Re: Creating "TEdit" component dynamically
Post by: lucamar on May 05, 2021, 03:08:25 pm
Beyond the instance creation "magic", the constructor is sometimes used to initialize some fields, set some default properties, etc. If you're going to call it as a method to "reset" it to the default state it's usually better to include all those initializations to their own method (SetDefaults() or whatever) and call that instead.

That way it's less confusing and there is no doubt about whether you're constructing the object or just "resetting" it.

IMHO, of course :-\
Title: Re: Creating "TEdit" component dynamically
Post by: pascal111 on May 05, 2021, 03:16:50 pm
It seems Lazarus freePascal is strong language in event driven programming, it cares in details, small things of OOP and Visual Programming and different techniques.
Title: Re: Creating "TEdit" component dynamically
Post by: y.ivanov on May 05, 2021, 06:38:00 pm
I see now. I think the different between the two concepts can be used in advanced uses of components.

It is quite simple actually, you may think of objects as of instances of a certain class, and for that class as of the only instance of some hypothetical meta-class.
Therefore, all that is valid for the object can be applied to the class - the class also have its methods and data fields (static ones).
So, the constructors are actually methods of the class - they construct an instance of the object and return them back as a reference:
Code: Pascal  [Select][+][-]
  1.   e1 := TEdit.Create(Self);
  2.  
Means "call the method Create on (sole) instance of Meta-TEdit named TEdit and assign the returned reference to the e1".  As lucamar said, the:
Code: Pascal  [Select][+][-]
  1.   e1.Create(Self);
  2.  
means "call the method Create on instance of TEdit named e1", but e1 doesn't exist yet and also does not have such a method.

It's simple.
Title: Re: Creating "TEdit" component dynamically
Post by: pascal111 on May 05, 2021, 06:42:52 pm
It has logical sense @y.ivanov. I understand.
Title: Re: Creating "TEdit" component dynamically
Post by: Remy Lebeau on May 06, 2021, 12:30:13 am
One more thing, since you gave an Owner to the TEdit by:

Code: Pascal  [Select][+][-]
  1. e1:=tedit.Create(self);

You shall not free it at FormDestroy(). It will be freed by the Owner at the right time.

While it is true that an Owner will free the component automatically, it should be noted that it is perfectly safe to manually Free() a component that has an Owner assigned.  The component will simply remove itself from the Owner's internal list so that the Owner will not try to free the component again when the Owner is freed.
Title: Re: Creating "TEdit" component dynamically
Post by: y.ivanov on May 06, 2021, 02:43:28 am
*snip*
While it is true that an Owner will free the component automatically, it should be noted that it is perfectly safe to manually Free() a component that has an Owner assigned.  The component will simply remove itself from the Owner's internal list so that the Owner will not try to free the component again when the Owner is freed.

Formally true. My wording must be read "You may not free it" instead of "You shall not free it" then.

On the other hand, giving a non-nil owner to a component on its constructor means that we're transferring the life-cycle management to that owner. If we intend to manage the component life-cycle by ourselves, it is much better to create it without owner. Mixing the two approaches will result in UAF sooner or later.
Title: Re: Creating "TEdit" component dynamically
Post by: lucamar on May 06, 2021, 06:15:00 am
On the other hand, giving a non-nil owner to a component on its constructor means that we're transferring the life-cycle management to that owner.

Not always. For example imagine, if you will, you're creating a series of anonimous temporary controls and, for whatever reason, you don't want to create the tipical list to contain them. Giving them an Owner means that they will be locatable through the owner's Components "array" without necessarily meaning you want that owner to be responsible for them.
Title: Re: Creating "TEdit" component dynamically
Post by: y.ivanov on May 06, 2021, 01:04:31 pm
Formally true. My wording must be read "You may not free it" instead of "You shall not free it" then.

I have already admitted my incorrect statement. In practice, there can be many different cases that an experienced programmer knows how to handle. Generally, the life cycle management is something that must be taken most responsibly. I'm still thinking that mixing approaches is the sloped road to UAF. After all, even experienced programmers are not immune to it.
Title: Re: Creating "TEdit" component dynamically
Post by: PascalDragon on May 06, 2021, 03:37:08 pm
As lucamar said, the:
Code: Pascal  [Select][+][-]
  1.   e1.Create(Self);
  2.  
means "call the method Create on instance of TEdit named e1", but e1 doesn't exist yet and also does not have such a method.

To be fair it's perfectly fine to call a constructor as a normal method. The creation “magic” only exists when called using a class type.

This also means if you have a constructor that doesn't access any of the fields, then that will work without problems on an invalid instance (as with any method) and the problems will only occur later, because the instance wasn't created (been there, done that :P ).
Title: Re: Creating "TEdit" component dynamically
Post by: y.ivanov on May 06, 2021, 05:15:23 pm
To be honest, I suspected something like that (but how else to present this to a rookie). I'm curious about the realization, because, unlike C++, here the instances are purely dynamic and must be created explicitly. From what you say, I conclude that "magic" is the allocation on the heap and it only happens if it is called through the class. Otherwise, the constructor is called with an invalid Self as a regular method, is that correct? Isn't it forbidden from the compiler? I have never tried it...
Title: Re: Creating "TEdit" component dynamically
Post by: lucamar on May 06, 2021, 06:54:46 pm
Otherwise, the constructor is called with an invalid Self as a regular method, is that correct? Isn't it forbidden from the compiler? I have never tried it...

Yes, if the object hasn't been created normally then it's called with an invalid Self. It's not forbidden because there might be legitimate reasons to call it through an instance as a normal method (e.g. to "reset" the object to its default state).

(been there, done that :P ).

As, I guess, most of us when coding in a hurry and not thinking clearly: "Oh, I forgot to create that string list! Let's see: MyStrings.Create; ..." :-[ :D
Title: Re: Creating "TEdit" component dynamically
Post by: y.ivanov on May 06, 2021, 07:53:44 pm
*snip*
Yes, if the object hasn't been created normally then it's called with an invalid Self. It's not forbidden because there might be legitimate reasons to call it through an instance as a normal method (e.g. to "reset" the object to its default state).
*snip*

After reading few articles now I see it clearly! We use it all the time in the constructors (inherited Create;).

Makes me think that the usual heap allocation can be skipped (e.g. for tiny embedded) and have a 'static' instances, something like:

Code: Pascal  [Select][+][-]
  1. var
  2.   Buf: array [0..MaxBuf-1] of Byte;
  3.   Mark: Integer;
  4.   O1, O2: TMyObject;
  5. begin
  6.    Mark := 0;
  7.    O1 := TMyObject(@Buf[Mark]); Mark := Mark + TMyObject.InstanceSize;
  8.    O1.Create;
  9.    O2 := TMyObject(@Buf[Mark]); Mark := Mark + TMyObject.InstanceSize;
  10.    O2.Create;
  11.    // etc.
  12.    MainProc;
  13. end.
  14.  

Of course that kind of objects should never be Free'd.

BTW, using obj.Create for resetting is considered a very dangerous practice.
Title: Re: Creating "TEdit" component dynamically
Post by: lucamar on May 06, 2021, 08:00:04 pm
BTW, using obj.Create for resetting is considered a very dangerous practice.

Thence my previous advice (https://forum.lazarus.freepascal.org/index.php/topic,54470.msg404620.html#msg404620) :D
Title: Re: Creating "TEdit" component dynamically
Post by: y.ivanov on May 06, 2021, 08:08:14 pm
Thence my previous advice (https://forum.lazarus.freepascal.org/index.php/topic,54470.msg404620.html#msg404620) :D
Absolutely right! Sorry, little dizzy of my (personal) findings.

BTW, Is there a way to know InstanceSize at compile time? Something like SizeOf()? 
Title: Re: Creating "TEdit" component dynamically
Post by: lucamar on May 06, 2021, 09:13:44 pm
BTW, Is there a way to know InstanceSize at compile time? Something like SizeOf()?

Not that I know of, sorry.

If I may ask, why do you want it? For most classes InstanceSize is only a "lower estimation".
Title: Re: Creating "TEdit" component dynamically
Post by: y.ivanov on May 06, 2021, 09:36:15 pm

Not that I know of, sorry.

If I may ask, why do you want it? For most classes InstanceSize is only a "lower estimation".

For specifying the upper bound of the pre-allocated buffer in
https://forum.lazarus.freepascal.org/index.php/topic,54470.msg404786.html#msg404786
TinyPortal © 2005-2018