Recent

Author Topic: How optimized is the FPC compiler  (Read 27695 times)

Warfley

  • Hero Member
  • *****
  • Posts: 654
Re: How optimized is the FPC compiler
« Reply #75 on: December 24, 2020, 12:03:41 am »
But you don't have to use a pointer to use an integer.
It depends what you want to do, for example if I want to access the bytes of the integer individually, I need a pointer to do so:
Code: Pascal  [Select][+][-]
  1. b: Byte;
  2. ...
  3. b := PByte(GetIntPtrFunction())[2];
and if you now take that pointer from a stack frame that is not live anymore, it is not going to work.
This is the exact same problem without any use of polymorphic inheritance.

The thing I want to point out is that data lifetime and polymorphism are completely independent. Sure to do polymorphism the data must be live, but it is not a limitation of polymorphism if you choose a data storage that has a lifetime thats not long enough.
« Last Edit: December 24, 2020, 12:20:23 am by Warfley »

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7684
  • Debugger - SynEdit - and more
    • wiki
Re: How optimized is the FPC compiler
« Reply #76 on: December 24, 2020, 01:10:21 am »
After writing the response some of the important facts only came into light at the end.

I will put a copy of them upfront. All my replies are to be read with the following in mind.

- Yes old objects have polymorphism
- But its usability is limited as to where/when it can be used, if only stack memory should be used
- the stack limitation (and consequences) are an arbitrary condition.
  (This last point was not previously spelled out / never mind the limit was stated as requirement)

The limit was NOT created by me, but taken from previous posts.

All of the above where known.
With all of the above known:
- I stated: there are limits.... (not to polymorphism, but to where/when it can be used)
- You stated: pointers can overcome that limit

Then I challenged you.


Further more, I like and use objects. They are useful, versatile and good. All that is not the point. Neither is what else you can do with them. There was a specific statement about one thing that does not work with them. (and you response to it). THis discussion is about that very particular use-case only.
This use case was born out of a particular set of needs indicated in posts by another person on the thread.


But you are mixing up two completely different concepts. Memory lifetime and polymorphism. To do polymorphism you need to access the memory, and to do so it must be live. This is not a restriction to objects. You can't use a class after freeing it. You can't use a pointer to an integer after freeing it's memory.
The "problem" you pointed out was: how to reference an object located on a stack frame after the function that frame belongs to has returned. The answer to that is you can't. This is like if you would be asking how to use a class after calling Free.
That is my opening statement. What I said to begin with (and repeated).
- Yes it has polymorphism
- But its usability is limited

One of your posts (in reaction to this statement of mine) was that I was wrong, and it would work with pointers.

I am aware of the lifetime of memory pointed to. Using pointers had never been my idea (you even "scolded" me when my example for the issue was NOT using pointers).
So I really do not get, why you keep telling me, that I mix things, when you were the one who did bring in pointers.
And I do not get why you doubt my understanding of pointers, when I said (as reaction to your claim) that pointers would not solve it entirely, and gave you a task on which I believed this would be the case.

As you now confirm:
Quote
The answer to that is you can't.

I always thought so.

Again, yes it still has polymorphism. But again, there are limitations that restrict where/when/how you can use it.

Quote
If you want to use an object, polymorphic or not, the memory it is stored on must be life. You pretent like this is a limitation of old style objects but the fact that the memory must exist if you want to access it is a restriction for *ANY* data.
Yes, true.
But new style classes make it possible to keep the memory available in the given case.
Old style objects do not allow for that.

The limitation is NOT that the memory must be alive.
The limitation is that for old style objects (when using certain function calls) it is not possible to keep it alive.


Quote
The ability to do polymorphism is only restricted by your ability to access the data. As long as you store the data in a way it is accessible you can do polymorphism.
But this inability stems from the way objects are designed [1]. It is part of old style objects.
New style classed to not impose that limit on such abilities.

[1] Under the premise, that no heap is to be used. Which was a given on the original task, and known before you implied that pointers could overcome that limitations.

Quote
I could also use a stack allocator that allocates the memory on the previous stack frame and passes the pointer to that to the callee.
That one I do give to you. That is true and that will work.
And it will fulfil the condition that memory is freed when the frame is exited (the proc returns).

Mind you though: "previous stackframe" => The called procedure must find the correct caller ("previous" => could be several frames up), and move the data of all stackframes in between, and adjust the basepointers for all of them.
It will be a noticeable overhead (but that is fine, that does not violate the conditions of the exercise)
 
I am not sure, how easy that can be done in FPC, without changes to the compiler? It might be possible, but it will probably relay on implementation detail of the compiler, and therefore run the risk of breaking with updates.
It also requires that stackframes are generated for all relevant callers.

It may not work, if the call chain has nested procedures. They get passed the parent basepointer, and may keep a local copy of it. You will probably not be able to adjust that, and it would then be invalid.

Quote
Another option would be to map a file into memory and save the data in the filesystem.
That is really just another way to say "heap" (swap file). Though yes, hairs can be split on this one.

Quote
And whats your obsession with not using the heap?
I have none. But the entire premise that lead to this discussion was that they should be on the stack. So no memory alloc, and more important no de-alloc was necessary.
Data on the stack is freed when the procedure exits. (Yes interfaces can free heap. But that was not the original question either).

At the time where you claimed that "pointers" would lift the restrictions on how the (fully existing) polymorphism could be used, all of those conditions were known.

Putting old style objects on the heap, means a manual call to de-alloc is needed.

Quote
If you say that old school objects have a limitation if you limit them only to be used on the stack, this limitation is arbitrarily set
Yes it is.
But it was set so, before you claimed that pointers could lift that arbitrarily set limitation.

« Last Edit: December 24, 2020, 01:18:06 am by Martin_fr »

Warfley

  • Hero Member
  • *****
  • Posts: 654
Re: How optimized is the FPC compiler
« Reply #77 on: December 24, 2020, 03:29:49 pm »
But new style classes make it possible to keep the memory available in the given case.
Old style objects do not allow for that.
But this is completely and utterly wrong. You can put old style objects on the heap, the same way new style classes are on the heap. The ONLY difference is that classes hide this from the user with syntactic sugar. Semantically there is literally no difference from putting an object on the heap and creating a class.

If like you say objects don't allow to be allocated on the heap, better call the object police, because then I have broken that law many times.

Why do you limit objects to the stack, but happiely ignore that limitation for classes to then argue classes are less limited than objects, even though the limitation was specifically brought forward by you to only apply to objects and ignore it for classes.

Let's make a similar challange, we make a race with two cars, you get a ferrari and I get an opel corsa. Who makes it first on a race track wins. The only caveat is that you in your ferrari can't use gasoline. See how inferior the ferrari is to the opel corsa, it can't even beat it in a simple race

Quote
There was a specific statement about one thing that does not work with them. (and you response to it).
But your challange has nothing to do with objects. You challange was to make a dynamic allocation of memory without using dynamic memory allocation (i.e. the heap).


If this is an inherent limitation to objects that does not apply to classes, I've shown previously how to allocate classes on the stack. Use this to solve your problem with classes instead of objects under the same conditions (i.e. not using the heap). If you can, I agree this is a limitation of objects. If not, this has nothing to do with the datatype used.
You claim this is an inherent design problem with objects, so it should be a cakewalk to solve this with classes then... right?


Quote
But it was set so, before you claimed that pointers could lift that arbitrarily set limitation.
No I didn't I said you can use polymorphism if you use pointers. I never claimed that you can access memory after it been freed. Quote me where I claimed that you can lift that limitation by using pointers.

But I can state what I meant again. If you have your object or class stored anywhere and got a pointer to it, you can use polymorphism. The location it is stored in and the mechanism of how this storage was acquired is completely irrelevant for the ability to use polymorphism.

I can excatly quote what I claimed:
Quote
So in theory, neither classes are restricted to the heap nor are objects restricted to the stack. Inheritance works in all cases as expected, but to make use of it, you need to access it via pointers. Where it is stored does not matter
How does the statement "Where it is stored does not matter" imply that it works even if there is no storage at all. You claim that I claimed that polymorphism should work in your example when using pointers, i.e. in a situation where the object was already destroyed. How did you read that from this statement? IMHO the "Where it is stored" implies that it is stored somewhere.
But your example creates a situation where the object storage was destroyed before it should be accessed polymorphically.
« Last Edit: December 24, 2020, 04:08:41 pm by Warfley »

PascalDragon

  • Hero Member
  • *****
  • Posts: 3638
  • Compiler Developer
Re: How optimized is the FPC compiler
« Reply #78 on: December 24, 2020, 08:41:01 pm »
1) Object support fully inheritance

TP-style objects support inheritance in that they can inherit from a parent object.

2) Object is placed fully on stack and thus dont need any alloc/dealloc

If you use them directly (e.g. a TMyObject) then it's fully located where you declared (e.g. global variable or stack). You can however instantiate them using New as well, thus they'd reside on the heap.

3) Object CANNOT overload operators

You can use global operator overloads, however you can't use member operator overloads which are necessary to support custom operators inside generics.

4) Object CANNOT  use Generics

Yes, they can.

5) Object CANNOT use Management operators

Correct.

6) Object CANNOT use Polymorphism

You can access an object instance using a variable of the parent type.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7684
  • Debugger - SynEdit - and more
    • wiki
Re: How optimized is the FPC compiler
« Reply #79 on: December 24, 2020, 10:55:42 pm »
But new style classes make it possible to keep the memory available in the given case.
Old style objects do not allow for that.
But this is completely and utterly wrong.

If you take my statements out of the context in which I gave them, then yes the remaining partial statement will be wrong.
Please re-read my post, and make sure to apply the entire context that I stated.

And by applying it, limit your response to that what is relevant to that context.

When there is a discussion about what can be done with objects if they should only be used on the stack => how does it matter that you can also use them on the heap?
It was clearly said, that this was not wanted (no matter why / despite the why was actually explained too)

Thanks
« Last Edit: December 24, 2020, 10:58:58 pm by Martin_fr »

Warfley

  • Hero Member
  • *****
  • Posts: 654
Re: How optimized is the FPC compiler
« Reply #80 on: December 25, 2020, 12:13:34 am »
If you take my statements out of the context in which I gave them, then yes the remaining partial statement will be wrong.
Please re-read my post, and make sure to apply the entire context that I stated.
And you seem to not have read my post, because like my last 3 post are soly about that you claim that this is some limitation of inheritance in objects. I don't say that this limitation does not exist, my complete argument revolves around that this has literally nothing to do with objects. Classes have the exact same problem if you allocate them on the stack.

You did now multiple times claim that this is inherently a limiation of objects. Let me go through all of your claims:
Quote
But new style classes make it possible to keep the memory available in the given case.
Old style objects do not allow for that.
This statement clearly implies that objects have a limitation that classes don't. But if you restrict classes to the stack, this is simply not true

Quote
But this inability stems from the way objects are designed [1]. It is part of old style objects.
New style classed to not impose that limit on such abilities.

[1] Under the premise, that no heap is to be used. Which was a given on the original task, and known before you implied that pointers could overcome that limitations.
This is a complete farce. You say that objects are limited by design while classes don't impose a limit, while acknowledging that the limit is arbitrary set by you and only applied to objects. If we apply the same premise for classes, they are as limited as objects

Quote
That was your suggestion to solve the limitation in old style objects.
This statement again implies that this is a limitation inherent to objects, which as I stated multiple times is simply not true. Classes if you restrict them to not use the heap, face the exact same limitations

So you claimed 3 times clearly that there is some inherent limitation of the design of objects, in two of which you clearly mention classes as an alternative that do not have this limitation. The only thing I am claiming, the whole time, is that this limitation has absolutely nothing to do with the design of objects, as it is also present in classes if you compare them under the same set of limitations.

This is literally all I am arguing against. You make repeatedly the claim that objects are inherently limited in a way classes are not. But this is just factually wrong. You are comparing two different things here, objects which are placed on the stack, vs classes placed on the heap. This has nothing to do with the advantages and disadvantages of classes vs objects, but all with the advantages and disadvantages of heap vs stack.

Again with my car example from above, the fact that an opel corsa with gasoline is faster than a ferrari without gasoline does not imply in any way shape or form that the ferrary is by design slower than the opel corsa, but can be solkey explained by the usage of gasoline to power the car.

Similarly, your argument is a comparison between classes on the heap and objects on the stack. The differences between them are not representative of the differences in design between objects and classes, as you have not compared them equally. The argument you are actually bringing forth is about the advantages and disadvantages of the different lifetime and managment of different memory allocation methods. And this is a perfectly fine argument to have, but you pretend like the design of objects has anything to do with it, while objects are literally designed to make use of both, the heap and the stack. If you really think that this is about the way objects are designed, compare apples with apples, compare stack objects vs stack classes or heap objects to heap classes. I repeat my challange:
Quote
Use this to solve your problem with classes instead of objects under the same conditions (i.e. not using the heap). If you can, I agree this is a limitation of objects. If not, this has nothing to do with the datatype used.
If you don't think that this is about the way objects are designed, but rather about the advantages and disadvantages of the different memory allocation methods (stack vs heap), we have absolutely nothing to disagree on. Because I fully agree, the automatic memory management of the stack is great for convinience, but does limit the usability of the data.

PS:
About this:
Quote
When there is a discussion about what can be done with objects if they should only be used on the stack
and
Quote
I have none. But the entire premise that lead to this discussion was that they should be on the stack. So no memory alloc, and more important no de-alloc was necessary
I never was part of that discussion (that they should be on the stack), I just claimed that polymorphism can be fully used if the object lives on the stack. I did so with this example:
Code: Pascal  [Select][+][-]
  1.     procedure PrintObj(x: PBase);
  2.     begin
  3.       WriteLn(x.Print);
  4.     end;
  5.      
  6.     var
  7.       c: TChild;
  8.     begin
  9.       c.init(42, 32);
  10.       printObj(c);
  11.       c.Destroy;
  12.     end;
As you can see, polymorphism is used by calling an virtual overriden method from a base object pointer pointing to the child object located on the stack.

Your ability to use polymorphic properties of an object is only limited by the livelyness of an object. It does not matter if the object is allocated on the stack or on the heap.


I never made a claim if objects should only be used on the stack. Honestly you should always use what fits your situation best, this is true for data types and for their allocation methods. I just said, and I am repeating myself now pretty often: As long as you have a pointer to an object (which of course must exists in memory), you can use all the features of polymorphism. I never claimed anything about if you should use them, if you should place them only on the stack or whatever. I never took part in that discussion and I never argued for any position in that discussion.

The thing is, the question if objects should be placed on the stack is a completely different discussion as of what they are capable of if they are placed on the stack. There is no limitations objects have when placed on the stack that is inherent  to the design of objects. To the contrary, all the features of an object can be used regardless of the location address starts with $000000 (heap grows from above) or $FFFFF (stack grows from below).

How about an experiment where we controll for all other variables except for the allocation method.
Code: Pascal  [Select][+][-]
  1. type
  2.   PBase = ^TBase ;
  3.   TBase = object
  4.      // CHANGEME
  5.   end;
  6.   PChild = ^TChild;
  7.   TChild = object(TBase)
  8.     // CHANGEME
  9.   end;
  10.  
  11.   procedure DoSomething(obj: PBase);
  12.   begin
  13.     // CHANGEME
  14.   end;
  15.  
  16. var
  17.   c: TChild;
  18.   pc: PChild;
  19. begin
  20.   DoSomething(@c); // a
  21.   new(pc);
  22.   DoSomething(pc); // b
  23.   dispose(pc);
  24. end;
Fill out the CHANGEME in a way that a fails but b does not fail, simply for the way the memory was allocated.

If there is a feature of objects that is only possible if the object was allocated on the heap, but not if the object was allocated on the stack, this controlled experiment should be able to show this.
« Last Edit: December 25, 2020, 12:44:41 am by Warfley »

ASBzone

  • Hero Member
  • *****
  • Posts: 616
  • Automation leads to relaxation...
    • Free Console Utilities for Windows (and a few for Linux) from BrainWaveCC
Re: How optimized is the FPC compiler
« Reply #81 on: December 25, 2020, 01:17:04 am »

My observation over the last few years (earlier I don't know) is:
A lot of time is spent on level 3+ optimization. But the real question is how many actually use it.
Personally, in order to minimize the number of bugs (which is my top priority),
I don't dare to use more than level 2 and I'm happy with that approach.


I used level 3 for a couple years, but now I only use level 4, and it has not been a problem for me.


My applications are admittedly small, however.
-ASB: https://www.BrainWaveCC.com/

Lazarus v2.0.13 r64843 / FPC v3.2.1-r49055 (via FpcUpDeluxe) -- Windows 64-bit install w/Win32 and Linux/Arm cross-compiles
Primary System: Windows 10 Pro x64, Version 2009 (Build 19042)
Other Systems: Windows 10 Pro x64, Version 2009 (Build 19042) or greater

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7684
  • Debugger - SynEdit - and more
    • wiki
Re: How optimized is the FPC compiler
« Reply #82 on: December 25, 2020, 02:15:52 am »
On the "by design" => read bottom part first. Might save some arguments on the middle part.

If you take my statements out of the context in which I gave them, then yes the remaining partial statement will be wrong.
Please re-read my post, and make sure to apply the entire context that I stated.
And you seem to not have read my post, because like my last 3 post are soly about that you claim that this is some limitation of inheritance in objects.
Really? Just my previous response:
- Yes old objects have polymorphism
- But its usability is limited as to where/when it can be used, if only stack memory should be used
- the stack limitation (and consequences) are an arbitrary condition.
  (This last point was not previously spelled out / never mind the limit was stated as requirement)

The limit was NOT created by me, but taken from previous posts.

It clearly states it is not "some limitation of inheritance in objects" (as you claim I said).

I said "if only stack memory should be used"
It is a limitation that stems from the combination of "object" + "stack only".

And yes, I read your point that then it would apply to classes too, if they actually could be limited to stack only. But that is not the point.

The stack only limit was brought up in some other post, because it meant that no (absolutely no) effort would have to be made to release the memory. (not even some interface, not even managed operators).
Stack memory is freed when the procedure is left.

Back to classes => The "no effort to free memory" effect can not be reached with classes (interfaces and managed operators are an effort). Hence the desired effect can not be reached with classes.

Objects can (by choice) live on stack only.
But if that choice is made, then (and only then) the limits I stated do apply.

Since that choice was made, it does not matter in any way what ever you can do if you use heap (be that: objects + heap or classes). Really does not matter.

All I ever said was, that if you make the "stack only" choice, then certain limits apply.

Quote
I don't say that this limitation does not exist, my complete argument revolves around that this has literally nothing to do with objects.
Classes have the exact same problem if you allocate them on the stack.
But classes do not go on the stack. At least not by design. Not in FPC.

If classes were designed to allow you to use them "stack only" then classes would have the same issue. But classes are not designed that way.

On the other hand: Objects are designed to give you the choice to use them on the stack.
Yes of course there design allows other usages too. It allows a choice. And for reasons given, the particular "stack only" usage was chosen.

Quote
You did now multiple times claim that this is inherently a limiation of objects. Let me go through all of your claims:
Quote
But new style classes make it possible to keep the memory available in the given case.
Old style objects do not allow for that.
This statement clearly implies that objects have a limitation that classes don't. But if you restrict classes to the stack, this is simply not true
Only if you missed that it was previously mentioned that it was about the choice to have objects on the stack.

But what im really curious at, is , what are then "object" classes like, are they not supposed to be C++ art-classes, where they can live on the stack and still have polymophism, inheritance etc... available?
There may have been more references, I did not search to exhaustion.

And just to be clear. I did not say that objects did not have polymorphism in that case. I said that in that case certain use cases would not work. (I.e. using as function result, where a child class is returned that needs extra memory). It is about the combination.

Quote
This is a complete farce. You say that objects are limited by design while classes don't impose a limit, while acknowledging that the limit is arbitrary set by you and only applied to objects. If we apply the same premise for classes, they are as limited as objects
Because by design, it cannot be applied to classes.
If it could ... well yes... but it cannot.

... skipping some repetition of the same point.

Quote
So you claimed 3 times clearly that there is some inherent limitation of the design of objects, in two of which you clearly mention classes as an alternative that do not have this limitation. The only thing I am claiming, the whole time, is that this limitation has absolutely nothing to do with the design of objects, as it is also present in classes if you compare them under the same set of limitations.
Yes and No, depending on the exact interpretation of your above statement.

- Objects - by design - give you the choice between heap and stack (and mixing those)
- Classes - by design - do not give you that choice (if they did, it where different, but they do not).

If you take that choice (e.g. for the reasons given earlier), then objects will encounter problems, when and if (and only when and if) used as described.

Classes would, if that choice was available for them. But it is not. Therefore - by that design - you can not make classes suffer from the issue.

Quote
You are comparing two different things here, objects which are placed on the stack, vs classes placed on the heap. This has nothing to do with the advantages and disadvantages of classes vs objects, but all with the advantages and disadvantages of heap vs stack.
Yes it as heap vs stack

No, I am not making that comparison => Well I did not start it. I only talked about objects, because the entire idea was to find something that worked on the stack only. Classes do not do that. So I did not even bother to bring classes in.

After you forced classes into the discussion of a "stack only" issue (a discussion where classes have no place to be), I replied that classes do not have that problem.
That reply of mine can indeed be misleading (sorry about that).
- Classes do not have the problem, because classes can not be used in that case at all.

To use "cars".
Had we been talking about problem boats may have on the open ocean (e.g. needing compensation for drift when navigating), I may have said (when prompted) that cars do not have those problems. I would have meant cars can not be used on the open ocean, therefore they also are not affected by drift occurring on the open ocean. (But sure, if you had a floating car.....)


Quote
The argument you are actually bringing forth is about the advantages and disadvantages of the different lifetime and managment of different memory allocation methods. And this is a perfectly fine argument to have, but you pretend like the design of objects has anything to do with it, while objects are literally designed to make use of both, the heap and the stack.
It is not about how polymorphism is designed in either of them.

It is about that only one of them can by design be used for "stack only".

See section above this quote.

Quote

I never was part of that discussion (that they should be on the stack), I just claimed that polymorphism can be fully used if the object lives on the stack. I did so with this example:
Code: Pascal  [Select][+][-]
  1.    
As you can see, polymorphism is used by calling an virtual overriden method from a base object pointer pointing to the child object located on the stack.
In the usecase of your example indeed it works.

Question here is "can be fully used", did you mean:
- Can sometimes be fully used
- Can always be fully used
?
I assumed you meant the latter. (Sorry if I misunderstood)

- If you meant the former, then the discussion is void, as the former would actually mean that "stack only" implies limitations.
- If you meant the latter, then well I asked you for an example of the usecase I provided. (And that example is still outstanding)

Quote
There is no limitations objects have when placed on the stack that is inherent  to the design of objects.

Ok, going back I did use "the way objects are designed.  Under the premise, that no heap is to be used."
And I may have refereed to the design of objects without explicitly stating the 2nd part, assuming it was known already.

I did so because "no heap" would most usually be done by declaring a local variable to hold the data.
In this case, under the given premise, memory allocation is not done by the user. It is part of the overall experience of using on object on the stack.
I therefore attributed the memory allocation (when not done explicitly by the user) to be part of the object design.

I.e. that is to read:
By design an object can automatically take space on the stack frame, if declared as local (none pointer) variable.
By design an object allows other types of mem allocation, if the user explicitly allocates the memory.

At first, one might thing that is not something bound to objects, as it applies to all datatypes. But that is not true. Ansistring and dyn array do not offer storage on the stack.




Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7684
  • Debugger - SynEdit - and more
    • wiki
Re: How optimized is the FPC compiler
« Reply #83 on: December 25, 2020, 02:33:11 am »
Btw, on the whole "by design of whatever" => that really was not the point of what this started with.

The point (even if design was mentioned) was, that
- when using objects
- and when limiting to stack only
limitations do exist.

As to if they are caused by "some design" or "something else" => not the point.

As to if they only exists when doing so with objects, or if they would also exist when using classes (under the assumption that classes could actually be used stack only) => not the point.

They do exist for the case described. What happens outside that described case => not the point.


Would be nice, to take all the "not the points" of the discussion, and get back to what the actual essence of my statement was.

My original post on that issue (follow the link  for full version)

But what im really curious at, is , what are then "object" classes like, are they not supposed to be C++ art-classes, where they can live on the stack and still have polymophism, inheritance etc... available?

They are a "limited attempt"....

Just as records, they do not allocate heap memory, but rather live on the stack. And while they have some support for inheritance, that requires that the subclasses have no additional fields. (You can afaik do virtual / overwritten stuff)

Imagine
Code: Pascal  [Select][+][-]
  1.  

b only has space for the fields of TBase.
So in the above example the remainder gets cut off. (Even if you cast back to TAdvanced, it will not come back. It is lost forever / It may even crash if you try to access extra).
B.method() would still call TAdvanced.method, if that was virtual/overwritten.

That is why new classes are on the heap => The caller does not need to know how much extra mem may have been required.

This is referring to the desired use case of having them on the stack. Allocating them to the heap is possible, but not part of this discussion.

As for "they do not allocate heap memory": They do not on their own. The user can do that for them. But not point of that post...

Using pointers to stack location does as far as I can see not change the above.


Your direct reply was "That is not true ..." But you missed the "stack only" and gave a "with heap" example.
This is not true, with regards to the "basic" inheritance (i.e. no interfaces), objects have the same capabilities as classes. Classes just add a coat of syntactic sugar to it:

Some posts later:
Yes, but then (your code example) you allocate the memory on the HEAP.
The type of allocation does not matter, what matters is that you access via pointers.
To which you gave an example to a case in which this happens to work.

I then ask for how to solve the case of returning an inherited (larger) object.
Using stack only.
Pointer (to stack) as you like.
(Note that when I posted it, I did set the quote markers wrong... / Below code is shortened, follow link for full)
Yes, but then (your code example) you allocate the memory on the HEAP.

Then please get the following to work (without heap)
Code: Pascal  [Select][+][-]
  1.  

Now, if you allocate space on the stack (by having a local var) in Foo => then you only know the size of TBase (not enough space to hold TAdvanced, even if you pass a pointer)

But if you allocate space on the stack (by having a local var) in Bar => then you return a pointer into Bar's stackframe => and once you return from Bar that stackframe is fair game for being used by the next subroutine call.

So how to do it? (No heap / stack only / as many pointers as you wish)

-------
NOTE
TAdvanced and Bar could be defined in another unit. Even 3rd party package. It could be in the implementation (if it is returned as the baseclass, but with an instance of TAdvanced). You do not know their size.
« Last Edit: December 25, 2020, 02:47:15 am by Martin_fr »

Warfley

  • Hero Member
  • *****
  • Posts: 654
Re: How optimized is the FPC compiler
« Reply #84 on: December 25, 2020, 04:32:14 am »
They do exist for the case described. What happens outside that described case => not the point.
Then tell me, how should I undertstand this:
Quote
But this inability stems from the way objects are designed [1]. It is part of old style objects.
New style classed to not impose that limit on such abilities.

[1] Under the premise, that no heap is to be used. Which was a given on the original task, and known before you implied that pointers could overcome that limitations.
Because just by reading it looks to me like you are directly comparing the design of old style objects with new style classes. But if you are talking about the specific case described, this comparison makes absolutely no sense, because new style classes are not comparable to that situation at all.

Are you comparing the two types or are you comparing the different circumstances (heap vs stack). Your claims like the one quoted clearly say the former, but all your explainations say the latter.

Would be nice, to take all the "not the points" of the discussion, and get back to what the actual essence of my statement was.

My original post on that issue (follow the link  for full version)

[...]

Your direct reply was "That is not true ..." But you missed the "stack only" and gave a "with heap" example.
This is not true, with regards to the "basic" inheritance (i.e. no interfaces), objects have the same capabilities as classes. Classes just add a coat of syntactic sugar to it:
Yes I misread your post. Thats why when you pointed that out, I gave you an example on how polymorphism still works if the data is allocated on the stack.

I then ask for how to solve the case of returning an inherited (larger) object.
Using stack only.
Pointer (to stack) as you like.
(Note that when I posted it, I did set the quote markers wrong... / Below code is shortened, follow link for full)
Yes, and my whole argument here is that this has nothing to do with polymorphism. Maybe I am wrong here, but for my understanding the goal of polymorphism through inheritance as employed in pascal is to allow to access different datatypes through a common interface. The key is the word *accessing* here. Your example is the copying of data, i.e. the storing of data. Polymorphism makes no attempt to unify the storage of data in memory of different objects. And of course it doesn't because there are other concepts for doing exactly this:

Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   sysutils;
  7.  
  8. Type
  9.  
  10.   { TBase }
  11.  
  12.   TBase = object
  13.   public    
  14.     A: Integer;
  15.     constructor init(AValue: Integer);
  16.     destructor destroy; virtual;
  17.     function ToString: String; virtual;
  18.   end;
  19.  
  20.   { TChild }
  21.  
  22.   TChild = object(TBase)
  23.   public      
  24.     B: Integer;
  25.     constructor init(AValue: Integer; BValue: Integer);
  26.     function ToString: String; virtual;
  27.   end;
  28.  
  29.   TUnion = record
  30.   case Boolean of
  31.   True: (base: TBase);
  32.   False: (Child: TChild);
  33.   end;
  34.  
  35. { TBase }
  36.  
  37. constructor TBase.init(AValue: Integer);
  38. begin
  39.   A := AValue;
  40. end;
  41.  
  42. destructor TBase.destroy;
  43. begin
  44.  
  45. end;
  46.  
  47. function TBase.ToString: String;
  48. begin
  49.   Result := 'A: ' + A.ToString;
  50. end;
  51.  
  52. { TChild }
  53.  
  54. constructor TChild.init(AValue: Integer; BValue: Integer);
  55. begin
  56.   inherited init(AValue);
  57.   B := BValue;
  58. end;
  59.  
  60. function TChild.ToString: String;
  61. begin
  62.   Result := inherited ToString + ' B: ' + B.ToString;
  63. end;
  64.  
  65. function Child: TUnion;
  66. begin
  67.   Result.Child.init(42, 32);
  68. end;
  69.  
  70. function Base: TUnion;
  71. begin
  72.   Result.base.init(42);
  73. end;
  74.  
  75. var
  76.   u: TUnion;
  77. begin
  78.   u := Child;
  79.   WriteLn(u.base.ToString);
  80.   u := Base;
  81.   WriteLn(u.base.ToString);
  82.   ReadLn;
  83. end.
Using variant records one can unionize the storage for different objects, and due to the common prefix of  inherited objects this can be used to make use of polymorphism while simultaniously having a unified storage.

This btw also solves your "challange", but thats irrelevant, because this challange is not about polymorphism, it is about the underlying memory model. That’s why the solution to this problem has nothing to do with polymorphism or some object specific mechanisms but with variant records, a construct to manually map memory
« Last Edit: December 25, 2020, 04:45:42 am by Warfley »

Shpend

  • Full Member
  • ***
  • Posts: 162
Re: How optimized is the FPC compiler
« Reply #85 on: December 25, 2020, 12:28:52 pm »
    I also tested more of the inheritcance of objects and i saw that objects, to be a major feature

    • they cannot inherit from interfaces while records can
    • they can ONLY inherit other objects (kind of big limitation, would be nice to allow them aswell also atleast inheritance from other records, im not a compiler dude but I think they could theoretically also inherit from classes, if not classes, but best would be inheritance from all)
  • They should be able to have the "management-operators" as records(together with the move semantics)

A small disclaimer: Im only speaking here what would be nice, im not judging why is that and why that not, only pointing out what would make objects great again :D And of course you decide in the end if it makes it into FPC and WHEN but i like alot in this forum atleast ppl are not avoiding discussions and improvements as if one would insult  their mother, good job on this, i like it alot :D

« Last Edit: December 25, 2020, 12:39:25 pm by Shpend »

Awkward

  • Jr. Member
  • **
  • Posts: 96
Re: How optimized is the FPC compiler
« Reply #86 on: December 25, 2020, 12:54:10 pm »
they cannot inherit from interfaces while records can[/li][/list]
Ough! Am i missing something? Records inherits from interfaces? give me example please!

How they can inherits if Record must be just ordering data set?

Shpend

  • Full Member
  • ***
  • Posts: 162
Re: How optimized is the FPC compiler
« Reply #87 on: December 25, 2020, 01:05:12 pm »
@Awkward
upsi, u are right it doesnt work, i was still at C# by writing my post :P

i have also read this article why (for now atleast) it doesnt work:

Quote
Relevant to this question, there are two kinds of inheritance: interface inheritance and implementation inheritance.

Interface inheritance generally implies polymorphism. It means that if B is derived from A, then values of type B can be stored in locations of type A. This is problematic for value types (like records) as opposed to reference types, because of slicing. If B is bigger than A, then storing it in a location of type A will truncate the value - any fields that B added in its definition over and above those of A will be lost.

Implementation inheritance is less problematic from this perspective. If Delphi had record inheritance but only of the implementation, and not of the interface, things wouldn't be too bad. The only problem is that simply making a value of type A a field of type B does most of what you'd want out of implementation inheritance.

The other issue is virtual methods. Virtual method dispatch requires some kind of per-value tag to indicate the runtime type of the value, so that the correct overridden method can be discovered. But records don't have any place to store this type: the record's fields is all the fields it has. Objects (the old Turbo Pascal kind) can have virtual methods because they have a VMT: the first object in the hierarchy to define a virtual method implicitly adds a VMT to the end of the object definition, growing it. But Turbo Pascal objects have the same slicing issue described above, which makes them problematic. Virtual methods on value types effectively requires interface inheritance, which implies the slicing problem.

So in order to properly support record interface inheritance properly, we'd need some kind of solution to the slicing problem. Boxing would be one kind of solution, but it generally requires garbage collection to be usable, and it would introduce ambiguity into the language, where it may not be clear whether you're working with a value or a reference - a bit like Integer vs int in Java with autoboxing. At least in Java there are separate names for the boxed vs unboxed "kinds" of value types. Another way to do the boxing is like Google Go with its interfaces, which is a kind of interface inheritance without implementation inheritance, but requires the interfaces to be defined separately, and all interface locations are references. Value types (e.g. records) are boxed when referred to by an interface reference. And of course, Go also has garbage collection.

Could this something be (the boxing idea even without GC?) and IF yes, is there on the other side a "big" perdformance penalty to this, which then maybe would break the entire performance discussion here, when thry would be boxed, but actually they would be only boxed when need be, and maybe the  FPC could optimize this by storing a permanent pointer of the interface to the heap location so when using interface methods from records, it would just use this
Code: Pascal  [Select][+][-]
  1. record.InterfaceProc1
internally as
Code: Pascal  [Select][+][-]
  1. hiddenIntefaceInstance.InterfaceProc1
or smth along the lines..

But in gods name, how does C++ do it then, I know that structs are same as class in C++ only the visibility section is different, so when speaking analogically to C++, it would mean in this context, that a struct is inheriting from abstract-classes, and they live both as well on stack if instantiated, so there would be the same slicing issue, or not, how do they deal with that??
« Last Edit: December 25, 2020, 01:10:11 pm by Shpend »

PascalDragon

  • Hero Member
  • *****
  • Posts: 3638
  • Compiler Developer
Re: How optimized is the FPC compiler
« Reply #88 on: December 25, 2020, 01:08:28 pm »
  • they cannot inherit from interfaces while records can

No, they don't.

  • they can ONLY inherit other objects (kind of big limitation, would be nice to allow them aswell also atleast inheritance from other records, im not a compiler dude but I think they could theoretically also inherit from classes, if not classes, but best would be inheritance from all)

No. They follow different lowlevel principles, they can't be mixed.

Shpend

  • Full Member
  • ***
  • Posts: 162
Re: How optimized is the FPC compiler
« Reply #89 on: December 25, 2020, 01:11:52 pm »
@PascalDragon
Pls read my post again (I have eddited cpl of times) how does C++ do all that within stack-area and get away with it??

 

TinyPortal © 2005-2018