Lazarus

Free Pascal => Beginners => Topic started by: julkas on April 23, 2019, 08:43:35 pm

Title: [SOLVED] Using records inside the class
Post by: julkas on April 23, 2019, 08:43:35 pm
Reading fcl-stl gset implementation on GitHub I found that gset class contains pointer to the record. Why usage of records in the class? Why just not standard class fields?
Code: Pascal  [Select][+][-]
  1. generic TSet<T, TCompare>=class
  2.   public
  3.   type
  4.     PNode=^Node;
  5.     Node=record
  6.       Data:T;
  7.       Left,Right:PNode;
  8.       Parent:PNode;
  9.       Color:boolean;
  10.     end;
  11.     TIterator=specialize TSetIterator<T, Node>;
  12.   var
  13.   private
  14.     FBase:PNode;
  15.     FSize:SizeUInt;

BTW FSize is outside the record!

Thanks.
Title: Re: Using records inside the class
Post by: lucamar on April 23, 2019, 11:06:49 pm
My guess (and it's just a guess, i've not eye-grepped the code) is it's using a quite standard double-linked list and the implementor found easier to GetMem() for a whole record. I woul probably have the done the same if my surmise is correct.
Title: Re: Using records inside the class
Post by: ASerge on April 24, 2019, 12:00:18 am
I agree with @lucamar. For each element, a record is used, and class fields are used to manage the entire set.
Title: Re: Using records inside the class
Post by: julkas on April 24, 2019, 09:25:31 am
it's using a quite standard double-linked list and the implementor found easier to GetMem() for a whole record.

1. I think OO implementation of standard double-linked list is more readable, extendable and customizable.
2. There must be other reasons (memory, performance,...)
3. Using records in classes in wrong place and way breaks OO spirit and design.

Any idea, thoughts?

Thanks.
Title: Re: Using records inside the class
Post by: Thaddy on April 24, 2019, 09:35:41 am
Any idea, thoughts?
fcl-stl is not exactly the best implementation -its interface is! -. It tries to mimic a C++ style standard template library..
FPC/Lazarus has at least two standard libraries that are - implementation wise - much better:
- fgl
- rtl-generics.

Furthermore these are - but this is my opinion - much easier to use.

People coming from other languages often make the mistake to go for FCL-stl instead of RTL-generics (or for 3.0.4 and before: fgl.) because it contains a magic word: stl. But it should not contain that magic word.
That said: with some effort it could achieve what it advertises.

Quote
1. I think OO implementation of standard double-linked list is more readable, extendable and customizable.
A TList or a TList<T> are underneath linked lists, just like in C.
Quote
2. There must be other reasons (memory, performance,...)
No. The only reasons are OOP in the first place: A linked list isn't. It's a list of pointers, a.k.a. memory locations.
IOW very low-level. OOP is about high-level abstractions.
Quote
3. Using records in classes in wrong place and way breaks OO spirit and design.
You do not understand the encapsulation part of object orientation.
Structures or variables in general can be tightly coupled to a class they belong to.
That's an asset. Not a hindrance.
Title: Re: Using records inside the class
Post by: mas steindorff on April 24, 2019, 09:55:25 am
I like to use records inside my classes as well for 2 reasons. 

1st it shows a variable that may have a common name is truly the one I have defined in the class (like index or tag... my.index, my.tag) or simply to avoid function call vis var like fileExsit vis my.fileExsit

2nd I may wish to use a property field to limit access to a variable now that I will replace with a set and get function later.  Or I only wish to give it read only access outside the class. In my code I know I will have full access to the core variable so I can read and write it as needed.

Example
...tmyclass = class
... my = record
  Count :int64;
  Index :int64;
...
End ; //of record
Public

Property count :int64 read my.count; 
...
End; //of class
Title: Re: Using records inside the class
Post by: Thaddy on April 24, 2019, 10:01:17 am
I like to teach whatever my computer science knowledge can bring to other programmers.
That's not perfect, but usually correct.

So, Max you are correct.
Title: Re: Using records inside the class
Post by: julkas on April 24, 2019, 10:24:05 am
fcl-stl is not exactly the best implementation -its interface is! -. It tries to mimic a C++ style standard template library..
FPC/Lazarus has at least two standard libraries that are - implementation wise - much better:
- fgl
- rtl-generics.

OK. My question is not from the user side, but from learner and programmer. I try to understand pros and cons of gset implementation.

Why not simply -

Code: Pascal  [Select][+][-]
  1. generic TSet<T, TCompare>=class
  2.   public
  3.       Data:T;
  4.       Left,Right:TSet;
  5.       Parent:TSet;
  6.       Color:boolean;
  7.       FSize:SizeUInt;
Title: Re: Using records inside the class
Post by: Thaddy on April 24, 2019, 11:50:43 am
Well, the public part is not immediately correct, if you mean that.
Records declared inside of a class should not have the possibility to be accessed outside of that class or (when protected) outside of inherited classes.

There are cases, though, that warrant it. These are rare.

You are asking the right questions...

Scope is essentially what prevents you from making mistakes.
Title: Re: Using records inside the class
Post by: creaothceann on April 24, 2019, 06:48:19 pm
OO spirit and design
OO is just one tool. Tools are specific to their use cases, and you have to know when to switch tools. There is no silver bullet (https://en.wikipedia.org/wiki/No_Silver_Bullet).

(OO is frequently not used for example in high-performance game programming because OO often groups data unrelated to the current job together, i.e. the fields of an object are all bundled in one memory location, wasting the cache space of modern CPUs.)
Title: Re: Using records inside the class
Post by: mas steindorff on April 24, 2019, 08:09:06 pm
OK. My question is not from the user side, but from learner and programmer. I try to understand pros and cons of gset implementation.
well, one "pro" to this is by by placing all of my variables in a record, I can use a call to fillchar(my,sizeof(my),0) => "fill memory with 0" in my Clear() procedure. My warning anyone using this trick is you can only have simple variables in your record. Strings, classes, and even arrays defined within the record should not be cleared in this manor so I keep them outside the record.

this trick lets me add more variables and not worry about re-init each one in either the class.create or .clear procedures.

it's also simpler to create a class.Assign().  I can copy all of the variables with a one liner self.my := src.my;  and then go in and modify the ones I need to.   

Title: Re: Using records inside the class
Post by: marcov on April 24, 2019, 10:56:44 pm
A TList or a TList<T> are underneath linked lists, just like in C.

Nope, basically just array wrappers.
Title: Re: [SOLVED] Using records inside the class
Post by: PascalDragon on April 25, 2019, 12:11:33 pm
Reading fcl-stl gset implementation on GitHub I found that gset class contains pointer to the record. Why usage of records in the class? Why just not standard class fields?
Code: Pascal  [Select][+][-]
  1. generic TSet<T, TCompare>=class
  2.   public
  3.   type
  4.     PNode=^Node;
  5.     Node=record
  6.       Data:T;
  7.       Left,Right:PNode;
  8.       Parent:PNode;
  9.       Color:boolean;
  10.     end;
  11.     TIterator=specialize TSetIterator<T, Node>;
  12.   var
  13.   private
  14.     FBase:PNode;
  15.     FSize:SizeUInt;

BTW FSize is outside the record!

Thanks.
It seems that you misunderstand what is happening here. Node is not a field, it's a type. It would logically be equal to declare Node outside of TSet<,>. This way TSet<,> can contain multiple Node entries by using a linked list which is much less expensive than doing a linked list for the whole class. And the FSize is simply there to keep track of the amount of items without the need to iterate the whole linked list.
Title: Re: [SOLVED] Using records inside the class
Post by: julkas on April 25, 2019, 05:43:59 pm
It seems that you misunderstand what is happening here. Node is not a field, it's a type. It would logically be equal to declare Node outside of TSet<,>. This way TSet<,> can contain multiple Node entries by using a linked list which is much less expensive than doing a linked list for the whole class. And the FSize is simply there to keep track of the amount of items without the need to iterate the whole linked list.
Ok. Thanks. I got it. I am writing implementation of similar data structure without records. Node is instance of class in my code and all operations are class methods. This approach has overheads, but works good and fast. Gset instance holds only  pointer to the root node.  But my question remains.
Title: Re: Using records inside the class
Post by: garlar27 on April 25, 2019, 05:52:04 pm
OK. My question is not from the user side, but from learner and programmer. I try to understand pros and cons of gset implementation.
well, one "pro" to this is by by placing all of my variables in a record, I can use a call to fillchar(my,sizeof(my),0) => "fill memory with 0" in my Clear() procedure. My warning anyone using this trick is you can only have simple variables in your record. Strings, classes, and even arrays defined within the record should not be cleared in this manor so I keep them outside the record.

@mas steindorff:
Look at this thread "Weird Memory Leak!! (https://forum.lazarus.freepascal.org/index.php/topic,16527.msg90059.html#msg90059)". Martin_fr suggested to use this code:
Code: Pascal  [Select][+][-]
  1. procedure FillerByte(out x; count: SizeInt; value: byte);
  2. begin
  3.    FillByte(x, count, value);
  4. end;
  5.  
And now I can't live without that code (by the way this month was the birthday of such a great procedure, now it has 7 years and 20 days old  :D )
Title: Re: [SOLVED] Using records inside the class
Post by: lucamar on April 25, 2019, 07:13:35 pm
This approach has overheads, but works good and fast. Gset instance holds only  pointer to the root node.  But my question remains.

You're responding to yourself there: It has overheads. When it's for yourself it may not matter (much) but when writing general code that  may be used by thousands of programmers for thousands of different applications it matters quite a lot.

For example, I know people that use an advanced record implementing the minimum imprescindible (for their use) for a list of strings because their applications can't cope with the overhead of using TStringList.
Title: Re: [SOLVED] Using records inside the class
Post by: julkas on April 25, 2019, 07:41:56 pm
When it's for yourself it may not matter (much) but when writing general code that  may be used by thousands of programmers for thousands of different applications it matters quite a lot.
That's right. Assume for different reasons we want augment, extend Node in gset class. Can we do this? Can we subclass gset class and add other fields to the inner record. If yes , how?
Title: Re: Using records inside the class
Post by: mas steindorff on April 25, 2019, 08:15:20 pm
OK. My question is not from the user side, but from learner and programmer. I try to understand pros and cons of gset implementation.
well, one "pro" to this is by by placing all of my variables in a record, I can use a call to fillchar(my,sizeof(my),0) => "fill memory with 0" in my Clear() procedure. My warning anyone using this trick is you can only have simple variables in your record. Strings, classes, and even arrays defined within the record should not be cleared in this manor so I keep them outside the record.

@mas steindorff:
Look at this thread "Weird Memory Leak!! (https://forum.lazarus.freepascal.org/index.php/topic,16527.msg90059.html#msg90059)". Martin_fr suggested to use this code:
Code: Pascal  [Select][+][-]
  1. procedure FillerByte(out x; count: SizeInt; value: byte);
  2. begin
  3.    FillByte(x, count, value);
  4. end;
  5.  
And now I can't live without that code (by the way this month was the birthday of such a great procedure, now it has 7 years and 20 days old  :D )
thank you for this tidbit. if I read this correctly, that thread says you are using fillchar on strings?  from what I understand, a string may be broken across 2 areas of memory and is likely to live outside it's record's memory block. I can see how it may work on a string at startup or if it's fixed size but have you tried it on one that has changed 10 to 20 times with different lengths?    (your code in the other thread look like you used setlength() to fix it's size and thus it's location)
Title: Re: [SOLVED] Using records inside the class
Post by: garlar27 on April 25, 2019, 11:18:38 pm
@mas steindorff:
... but have you tried it on one that has changed 10 to 20 times with different lengths? ...

I use it everywhere in my code inside functions, methods, and loops of every flavor, and with ever changing string lengths and never had a single problem. It also works fine with dynamic arrays.

(your code in the other thread look like you used setlength() to fix it's size and thus it's location)

I use it the same way as I used FillByte:
Code: Pascal  [Select][+][-]
  1. FillerByte(variable, SizeOf(variable), 0);

I even have a code template for it:
Code: INI  [Select][+][-]
  1. [filleb0 | FillerByte(x, SizeOf(x), 0);]
  2. $(AttributesStart)
  3. EnableMakros=true
  4. $(AttributesEnd)
  5. FillerByte($param(variable), SizeOf($param(variable,sync=1)), |0);
  6.  

thank you for this tidbit. if I read this correctly, that thread says you are using fillchar on strings?  from what I understand, a string may be broken across 2 areas of memory and is likely to live outside it's record's memory block. I can see how it may work on a string at startup or if it's fixed size but have you tried it on one that has changed 10 to 20 times with different lengths?    (your code in the other thread look like you used setlength() to fix it's size and thus it's location)

I think the assign count is cleaned safely because x is an out parameter. Then the FillByte works as it where the first time cleaning that record.

If you think it is possible to fail in some circumstances, then it would be a good idea to do a test to confirm if it behaves correctly or not.
TinyPortal © 2005-2018