No i dont know haha, im just trying to open up a discussion about this topic not to do really any suggestions to replace it lol
(internal)Vectorization, MxM mulitplications, MMXor other opts too (like register alloc, eval during compile / replace by constant, dead code detection, ....)
like take C++ in that list, the algorithm was reworked like 3-4 times by ppl and everytime got a good piece of boost to it
IOW, it is not the language itself.I strongly disagree, the language can emphasize more or less efficient programming. For example in C++ you can distinguish between move and copy semantic on assignments. For example if you have a struct containing dynamically allocated memory. When you copy the struct, to have a unique copy you need to deep copy, i.e. copy the dynamic memory. If you move the struct, you know the struct you get your data from will not be touched afterwards, and therefore you can just grab the pointer and don't have to copy the data.
@Warfley: Are you sure your example is correct?Yes, it's about the copy operator that is overloaded. This is used when assigning variables of the same record type:
A lot of other data also works via pointer in Pascal:But this is the thing, with the management operators you can define your own assignment semantics to records. This is useful for having managed datatypes inside your records. There are multiple reasons why these are very useful, most importantly, it let's you write local datatypes that do not have the overhead of classes, and support things like operator overloading for example.
- Objects (instances of classes, not old style object).
- Dyn Array
On the other hand records are passed by value. But you can specify "var" or "constref" depending on what you need.
@Warfley
I think Delphi 10.3 supports those as in C++
https://blogs.embarcadero.com/custom-managed-records-coming-in-delphi-10-3/
Not sure for now if FPC 3.2 could achrive this need to look..
I strongly disagree, the language can emphasize more or less efficient programming. For example in C++ you can distinguish between move and copy semantic on assignments. For example ifNonsense. FreePascal allows the same constructs, but with a slightly more complex syntax.
IOW, it is not the language itself.I strongly disagree, the language can emphasize more or less efficient programming. For example in C++ you can distinguish between move and copy semantic on assignments.
Sure, but IMHO that is in stuff in the fringes and does not justify the over-broad statement that you make. You also don't really specify any numbers or scenarios where this matters.See a few posts above, the gmp example. In general, when overloading operators that return complex types. Other example, I implemented a set type where union, complement, etc. can be expressed via arithmetic operators. Here the result of the operator functions always need to be copied.
Nonsense. FreePascal allows the same constructs, but with a slightly more complex syntax.But this is all I said. In C++ simple code is often very efficient, see the GMP example from above, using the GMP classes with arithmetic operators is not less efficient than using the gmp low level api. But in pascal, to get a good performance you need to use the low level API, because move semantic is simply not part of the pascal language design.
@Warfley: Are you sure your example is correct?Yes, it's about the copy operator that is overloaded. This is used when assigning variables of the same record type:
The whole contents of the string is copied twicethrew me off.
* unions* Variant record fields
* general Pointer-arithmetic
* embeded Assembler code
Shpend, if you want so much C/C++ things, maybe better to use C/C++ compiler and do not try transform Pascal to C++ ?
Btw, you only wrote what i already mentioned, @thaddy :D My argument stated that I love that FPC has already those things and would highly benefit the other constructs C++ offer, honestlyQuote* unions* Variant record fields
* general Pointer-arithmetic
* embeded Assembler code
* {$pointermath on}
* inline assembler is fully supported on many targets.
Also note Pascal is older than C (1970 vs 1972 )
I write speed dependent (Vision) applications, but don't use (or have an use case) for the examples that Warfley gives at all
am I wrongthinking that Object Pascal was made to compete vs C++ and Plain Pascal was about to compete with C?
Im actually really abit wondered why these "move" semantics are not part of Pascal since Pascal was the Idea to replace (even if not succesfully done so) C/C++ family by providing exactly those kind of native/low-lvl intrinsics/API's but expose them in a cleaner and more maintainable way.
So actually this would be really nice to have FPC support also the Move-keyword ("std::Move")
and possibility to overload the "+= ", "-="
would you actually think that my mentioned things to add to FPC, like the "std::move" and "+=/-=" would make it to FPC?
This is not an arguement mate, if this would be the case, a C# 2D engine i recently saw its src-code (was written fully in .NET 4.0, even having acess to core 3.1 LTS) would mean for .NET theyy dont have to do any optimizations or offer any language feature because they already are not apparently of much importance due to the existence of that 2D engine, cuz they didnt need apparently any higher language feature, I m not a big fan of those type of arguments tbh..
am I wrongthinking that Object Pascal was made to compete vs C++ and Plain Pascal was about to compete with C?I don't think object pascal is made to compete with C++, at least not what was implemented in Delphi over the time.
I wonder how many of these are already solvable using management operators though.Since the release of FPC 3.2 I am actually working on several projects where I try to use them for creating more efficient alternatives to things commonly done using classes, and personally I think that they are a great feature that allow for a lot of new high level expressions that where impossible beforehand.
I would really love to see how is the current state of the FPC, what could be done better and are ppl interessted in doing so)
These operators are simply syntactic sugar and nothing more.Sure, this *is* the case, but the question is *should* that be the case?
but I really still think thats it doesnt hurt the language if some effective C++ possibilities (like for instance this entire managed records stuff and move semantics..) to allow for kind of efficient code
Warfley, didn't you noticed what your code example with strings are different on your "level" only. but still almost the same on lowlevel? when you will change size of string, it will combine new string allocation and copying inside memorymanager anyway.It only does so if it can not extend the string, as I said
In the worst case, the latter code is equivalent to the former (if the memory manager can't simply append enough space, or the refcount is > 1) But in a lot of cases the latter one is massively more efficient, as it avoids a complete string copy.If the refcount of the string is 1, and there is free memory behind that block, the MM will simply extend this block. Also, I don't know if the FPC MM does this, but many memory managers, to avoid fragmentation, overalloc memory so it fits a certain size (e.g. a multiple of 16 bytes). If that is the case, adding less bytes than are overallocated is actually completely free.
So actually this would be really nice to have FPC support also the Move-keyword ("std::Move")
I just agree based on own practice that the Delphi/FPC dialect has some weaknesses in efficient valuetype handling in some constructs. Also it is not just about final code efficiency, but also syntax related oddities like most container types having an iterator type that is defined by value, making it impossible to mutate additional fields in a for..in loop, since the loop/iterator var is a copy
These classes and the subsequent implementation of the RTL an VCL using classes, results more in Dephi getting much more similar to Java than to C++.
From a performance programming perspective this makes absolutely no sense. For example using a TStringList to split a string requires the additional allocation of the class on the heap. With an old style object, the required variables that the stringlist uses internally would be placed on the stack and no overhead would be gained than if no OOP was used.
Classes always bring additional overhead, and due to manual memory management.
Another thing I found about C++ is the heavy usage of templates (generics in pascal) for class configuration. For example, to implement a custom sorting algorithm for a TStringList, you set a function pointer in the TStringList instance. In C++ you configure such things via template parameters. This has the consequence, that in Pascal this code can not be optimized, as the compiler does not know at compiletime what function will be used, while in C++ this is a complete runtime decision.
Since the release of FPC 3.2 I am actually working on several projects where I try to use them for creating more efficient alternatives to things commonly done using classes, and personally I think that they are a great feature that allow for a lot of new high level expressions that where impossible beforehand.
But sadly, as I already mentioned before, due to this bug (https://bugs.freepascal.org/view.php?id=37164) they are simply not usable currently, at least not in situations where you rely on the finalization, as when functions return a managed record it get's freed while still being active. This can result in double free or use after free, etc. and basically means that for anything that needs to be managed, the management operators are not usable. So all these projects of mine are currently on hold.
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.
From a performance programming perspective this makes absolutely no sense. For example using a TStringList to split a string requires the additional allocation of the class on the heap. With an old style object, the required variables that the stringlist uses internally would be placed on the stack and no overhead would be gained than if no OOP was used.
Classes always bring additional overhead, and due to manual memory management. Also the standard libraries heavily make use of abstract base classes and inheritance with virtual methods and stuff. In fact some methods like the destructor must be always virtual. If you look at C++ this is not the case. Sure the C++ standard library implementations also makes heavy use of inheritance for reducing the code complexity, but in most of the classes like vector, set, etc. you won't find any virtual methods. More often than not virtual methods are avoided using the CRTP (https://www.wikiwand.com/en/Curiously_recurring_template_pattern) idiom which allows for static or bounded polymorphism (https://www.wikiwand.com/en/Template_metaprogramming#/Static_polymorphism) using templates. This limits or completely avoids virtual call chains and makes a lot of the code inlinable and getting rid of virtual table jumps.
These operators are simply syntactic sugar and nothing more.Sure, this *is* the case, but the question is *should* that be the case?
But sadly, as I already mentioned before, due to this bug (https://bugs.freepascal.org/view.php?id=37164) they are simply not usable currently, at least not in situations where you rely on the finalization, as when functions return a managed record it get's freed while still being active. This can result in double free or use after free, etc. and basically means that for anything that needs to be managed, the management operators are not usable. So all these projects of mine are currently on hold.
6 months old? They really should have fixed it by now
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?
And @Warfley can u do this taken from ur example:The as operator does not work for objects, as this is syntacic sugar added to classes. I don't even know if RTTI works for objects, but if it does, it should be possible to manually implement this with a function or possibly even by overloading the as operator (if that is possible, I don't know right now).
why dont they just not overthrow compelty objects and allow FULLY everything a class can, only by the difference of being on the stack rather than the heap?
Hmm, why dont they just not overthrow compelty objects and allow FULLY everything a class can, only by the difference of being on the stack rather than the heap? And even better, if you , for some reason dont want advanced record to function as stack-classes, well just do not include a custom-macro (the name is mine now, but u get the idea..)
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:
No. You are trolling. Except 5I dont know guys.. everyone is telling smth else..
1,2,3,4,6 are supported.
Because objects have not been updated in a long time, for example you can not use class operators, which means if you want to have operators you better not use generics. Management operators also are not implemented for objects.
Records are in everything but inheritance much better than objects. Personally I came up with some alternatives to implement polymorphism
"Polymorphism" is partly supported.
You can pass TFoo when TFooBase is expected. TFoo the inherited class is accepted.
But it only works, if either
- Both have the same memory size.
- You add your own memory management.
For all your other points, I would have to check (docs, or trial and error) what the latest 3.2 (or even trunk) does support.
Anyway limitations to most of the points you have, are a questions as to whether they are implemented in FPC or not.
The above polymorphism limit is within the very design of (old style) objects. It will always exist. It can not be overcome within the definition of what those objects are.
And what is the case for operators?As I said, I would need to check myself.
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. Example (same types as used above):
1) Object support fully inheritance
2) Object is placed fully on stack and thus dont need any alloc/dealloc
3) Object CANNOT overload operators
4) Object CANNOT use Generics
5) Object CANNOT use Management operators
6) Object CANNOT use Polymorphism
Yes, but then (your code example) you allocate the memory on the HEAP.
Then please get the following to work (without heap)
object TBase // SizeOf = 8 +vmt if present a, b: longint; // any methods you like end; object TAdvanced(TBase) sizeof = 24 +vmt if present e1,e2,e3,e4: Int64 end; Function Bar: TAdvanced; begin // result should be a TAdvanced or pointer to it => it needs 24 bytes (+vmt) end; procedure Foo; var base: TBase; // or pointer begin base := Bar(); // enter some deep recursion code, to use the stack // access base end;
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.
Then please get the following to work (without heap)I clearly stated:
Inheritance works in all cases as expected, but to make use of it, you need to access it via pointers.But in your example you do:
Despite that "object" by nature is copy by value....What should this even mean? You can copy objects by reference as well as by value. It's up to the programmer to decide which of these two are required in what situation, and use different syntacitcal constructs. If you copy where you should have referenced you might loose data and waste performance, if you reference where you should have copied, you might get a dangling pointer.
All data must be on the stack (no heap). Therefore all pointers point to a location on the stack.What is your point? Because what you are saying has nothing to do with inheritance at all. This is about lifetime of different data storages. Sure this is a nice discussion to be had, but what does this have to do with inheritance?
That is important, since the main benefit was that "objects" do not need to be explicitly freed (as was mentioned).
And just in advance, this is about objects. It does not matter if there are means to get around the need to explicitly free classes. You said you can do it with objects. Please let me know how.
What is your point? Because what you are saying has nothing to do with inheritance at all. This is about lifetime of different data storages. Sure this is a nice discussion to be had, but what does this have to do with inheritance?See below.
By using a global variable. If you want to have an object be stored permanently, you need to use permanent storage. The stack is not permanent storage and the objects get invalidated when a function returns. So if you want to return a pointer, it can not point to the stack. What does this have to do with inheritance?Meaning there can only be one instance at a time. Even if the caller wants more than one. The caller could have several local vars (pointer or otherwise) to hold results from more than one call.
Whats your point? You can use inheritance as long as you can use pointers. And you can use a pointer as long as it's memory location it points to is valid.
The point is, that I said: Objects support polymorphism. But I also said this does not work in certain cases, when passing to/from function.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.
- You said, that this could be solved with pointers.
- I asked about a specific scenario where I do not believe pointers to solve the restriction.
Meaning there can only be one instance at a time. Even if the caller wants more than one. The caller could have several local vars (pointer or otherwise) to hold results from more than one call.I could use an array. Sure this will at some time also run out, but you can also get out of memory on the heap. The thing I want to address is, you can store objects in any data you want (as you can with classes, as shown above). 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.
The question is not about returning a pointer. That was your suggestion to solve the limitation in old style objects.I just assumed that you would understand that to access memory, that memory must exist. As long as the memory exists, the ability to do polymorphism is not restricted by where the data is located
The point is, that I said: Objects support polymorphism. But I also said this does not work in certain cases, when passing to/from function.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.
- You said, that this could be solved with pointers.
- I asked about a specific scenario where I do not believe pointers to solve the restriction.
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:
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.That is my opening statement. What I said to begin with (and repeated).
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.
The answer to that is you can't.
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.
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.
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.
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.
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.
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 setYes it is.
But new style classes make it possible to keep the memory available in the given case.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.
Old style objects do not allow for that.
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).
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.
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 matterHow 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.
1) Object support fully inheritance
2) Object is placed fully on stack and thus dont need any alloc/dealloc
3) Object CANNOT overload operators
4) Object CANNOT use Generics
5) Object CANNOT use Management operators
6) Object CANNOT use Polymorphism
But new style classes make it possible to keep the memory available in the given case.But this is completely and utterly wrong.
Old style objects do not allow for that.
If you take my statements out of the context in which I gave them, then yes the remaining partial statement will be wrong.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.
Please re-read my post, and make sure to apply the entire context that I stated.
But new style classes make it possible to keep the memory available in the given case.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
Old style objects do not allow for that.
But this inability stems from the way objects are designed [1]. It is part of old style objects.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
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.
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
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.
When there is a discussion about what can be done with objects if they should only be used on the stackand
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 necessaryI 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:
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.
Really? Just my previous response:If you take my statements out of the context in which I gave them, then yes the remaining partial statement will be wrong.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.
Please re-read my post, and make sure to apply the entire context that I stated.
- 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.
I don't say that this limitation does not exist, my complete argument revolves around that this has literally nothing to do with objects.But classes do not go on the stack. At least not by design. Not in FPC.
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:Only if you missed that it was previously mentioned that it was about the choice to have objects on the stack.QuoteBut new style classes make it possible to keep the memory available in the given case.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
Old style objects do not allow for that.
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.
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 objectsBecause by design, it cannot be applied to classes.
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.
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
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.
In the usecase of your example indeed it works.
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: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.
There is no limitations objects have when placed on the stack that is inherent to the design of objects.
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
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 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:
To which you gave an example to a case in which this happens to work.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.
Yes, but then (your code example) you allocate the memory on the HEAP.
Then please get the following to work (without heap)
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.
They do exist for the case described. What happens outside that described case => not the point.Then tell me, how should I undertstand this:
But this inability stems from the way objects are designed [1]. It is part of old style objects.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.
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.
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.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.
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:
I then ask for how to solve the case of returning an inherited (larger) object.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:
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)
they cannot inherit from interfaces while records can[/li][/list]Ough! Am i missing something? Records inherits from interfaces? give me example please!
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.
- 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 do exist for the case described. What happens outside that described case => not the point.Then tell me, how should I undertstand this:QuoteBut this inability stems from the way objects are designed [1]. It is part of old style objects.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.
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.
I think you mean that slightly different (but too complex for me to bother to put in into words).I then ask for how to solve the case of returning an inherited (larger) object.Yes, and my whole argument here is that this has nothing to do with polymorphism.
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)
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:In essence, yes like that. But, for polymorphism to work (without restriction as the one starting this argument) the memory model chosen must then have certain properties.
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.But only if you know the size of the biggest possible derived object.
Ahh okay okay, dont feel botherd pls, I m only wanting to get maximum understanding how fpc does it and what MAYBE can be improved, if not its fine thats what discussions are about, right?
So: Just for my understanding, why cannot object-types inherit then (advanced)record-types? they should kind of share the same memory-layout in Stack, dont they?
"New style classed to not impose that limit on such abilities." => To be read as, new style classes force usage of the heap (this is part of the design of new style classes). Therefore they can not experience that limit.Now I understand what you mean. You don't compare classes and objects, you compare heap allocation vs stack allocation and just use new style classes as standin for heap allocated instances and old style classes as standin for stack allocated instances.
Contrary, old style objects to not force usage on the heap. (Never mind that they could. It is not the point of the overall context that they could. It was said that they should be an the stack)
So I am "comparing"
- new style classes, particularly the fact that their design forces heap usage
versus
- Old style classes, with regards to their design does not force them to be on the heap. And that in this case are chosen to be on the stack
I think you mean that slightly different (but too complex for me to bother to put in into words).As I said, to me, polymorphism only describes the access not the allocation of objects. Therefore I have never seen that as a polymorphism issue. As an example, polymorphism was used prior to OOP by having structs/records with a comon prefix. This is very often employed in the linux kernel, where structs are used and the pointer they are accessed by, only refers to a type covering the first few fields while the rest was ommited and only used internally. These datatstructures of course always need to be passed by reference as their size is unknown to the outside.
Because actually it is (afaik) due to polymorphism that the inherited class can have a bigger mem footprint. And that is part of what causes the issue.
But it is not just polymorphism alone.
Then again, I don't think I ever claimed that.
As a general note "design of objects" is more broad than "design of polymorphism in objects".
But only if you know the size of the biggest possible derived object.Well as pascal is a statically typed language, so one could write a preprocessor that automatically generates a type where all derivates of the superclass fit in, as their sizes need to be known during compiletime. But this would be just a form of hack. So yes, if you want to use static memory like the stack, the size of the object needs to be static and known at compiletime, or to be more precise, needs to be known at the time the function is written.
And that size can not be known in all cases (examples given early on in the discussion)
@PascalDragonPretty simple, it's a seperation of concerns. You can put classes wherever you want, heap, stack, data, etc. But you can't simply put everything on the stack and expect it to work. You must choose the correct allocation method is for the data at hand.
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??
I am just simply saying they are more welcomed to the stack than anything else
@WarfleyTo that I wanna add, that internally, the objects just get what a record already have, its fields and methods, thats it, it doesnt need to bother5 for any VMT (from the record i mean which it inherits of) or anything else regarding Auto-Refcounting, since the record doesnt allow interfaces aswell(which makes sense) and so it shouldnt be much of a trouble to allow to just extend the record(s) its inheriting from.
And to which extent is FPC different to C++ in terms of MemoryModel? (no sarcastic comment!)
Like what are there more of limitations which cant be just added, like I have hard times really understanding that objects are not capable of ineriting records..
To that I wanna add, that internally, the objects just get what a record already have, its fields and methods, thats it, it doesnt need to bother5 for any VMT (from the record i mean which it inherits of) or anything else regarding Auto-Refcounting, since the record doesnt allow interfaces aswell(which makes sense) and so it shouldnt be much of a trouble to allow to just extend the record(s) its inheriting from.
And to which extent is FPC different to C++ in terms of MemoryModel? (no sarcastic comment!)
Like what are there more of limitations which cant be just added, like I have hard times really understanding that objects are not capable of ineriting records, where da hell is the difference, like they can both be placed on stack, both can have functions/procs, both can contain pointer to other data, both cannot implement interfaces the ONLY difference is that object can inherit other objects thats it, for some reason, im sry @PascalDragon i cannott believe that they are soooooo much different in terms of implementation.. if this is the only thing they are different of.
Like what are there more of limitations which cant be just added, like I have hard times really understanding that objects are not capable of ineriting records, where da hell is the difference, like they can both be placed on stack, both can have functions/procs, both can contain pointer to other data, both cannot implement interfaces the ONLY difference is that object can inherit other objects thats it, for some reason, im sry @PascalDragon i cannott believe that they are soooooo much different in terms of implementation.. if this is the only thing they are different of.
- type helper
Believe what you want. I know the compiler's code, you don't.Well, this is true but as I stated multiple times, there is nothing wrong considering addition to the compiler which would make sense, obviously you have to check for yourself how hard it would be to add them but its far away from being an "inappropiate/unlogical Addition".
the thing only is it could be really complete the niche of old-style-objects, when it would have :I would not say this, because C++ has a lot of differences that not necessarily are in the memory model itself, but allow for a lot of things in C++ that makes C++ much more convinient for high performance programming.
I generally notice the executables compiled by Free Pascal are over twice as big and run about half the speed compared with the same code compiled by Delphi7.
But i think we hace several issues related to the class/object model.
Could you give your opinion on advanced records
Clearly this is direction that Delphi/Embarcadero went, so trying to understand your direction a bit better.
Also, I'm curious if you are coming from a language that was Class-based OOP centric, so feel compelled to continue similar usage in Object Pascal versus the present possible options that it gives.
If you spend half your time thinking about how to optimize your code, you could be twice as productive simply by stopping to do so. It is very rare that speed is an issue, and in almost all cases you can speed up the bottleneck by changing the underlying way you do it. Which has nothing whatsoever to do with the compiler.И да, и нет. Это зависит от разрабатываемых приложений. Если конечный результат программа которой будут только пользоваться, и не для дальнейшей разработки с её помощью, то тут можно просто не задумываясь делать приложение и выдавать результат. Учитывая критичные места.
Simple example: if you have a huge dataset that you filter in many places to extract the relevant information, you could speed that up a lot by using multiple, different queries and/or datatsets. And you could speed those up by using stored procs instead. That has a far greater impact on the speed of your application than any compiler optimization.
In short: always ignore speed unless it becomes a problem, at which point you should search for the bottleneck and fix only that.
Yes, I know that is the opposite philosophy of C++, where speed is always the most important consideration.
For me, the most important thing is always the readability.
I tested the programs (and will continue to test), looked at certain places of the compiler. Optimization of the FPC compiler itself stalled about 10 years ago. Apparently, no one was engaged in it and everyone relies on computer resources.
In some cases, you can abandon Pascal's "String" and work with the text yourself. The compiler often (if you want to perform some actions with the text yourself) adds code that incurs additional costs (this is correct for most! Rarely, but some people want to control the process of working with the text themselves).
The StrToInt function is outdated (probably StrToFloat too, I didn't check it). By means of Pascal (not even assembler), it can be accelerated at least twice, if not more. You can't speed up the IntToStr function with pascal, text overhead is added, and in this case the compiler copes with the text quite well.
Я ни кого не хотел задеть своими словами! Я просто слишком прямолинеен, это моя достаточно плохая черта.I tested the programs (and will continue to test), looked at certain places of the compiler. Optimization of the FPC compiler itself stalled about 10 years ago. Apparently, no one was engaged in it and everyone relies on computer resources.
I really wonder how you come to that conclusion, cause if you look at the history of e.g. the x86 specific optimizations (https://gitlab.com/freepascal.org/fpc/source/-/commits/main/compiler/x86/aoptx86.pas) you can see that it's actively worked on, same for other platforms. Only because what you consider a good optimization is not done does not mean that no optimizations are done, because it all depends on what the devs priortize.
Основной смыл моих слов был в том, что я написал слово: Паскаль. :) И я прекрасно выразился, что сам паскаль меня устраивает! Оптимизация, как я считаю, не достаточная. И больше банальная оптимизация. Где явно видно, что ни какого изменения в коде не будет в процессе работы программы, но компилятор не преобразует такие моменты. Или преобразует, но не всегда или частично. Или вообще только для одной платформы - Windows.In some cases, you can abandon Pascal's "String" and work with the text yourself. The compiler often (if you want to perform some actions with the text yourself) adds code that incurs additional costs (this is correct for most! Rarely, but some people want to control the process of working with the text themselves).
The StrToInt function is outdated (probably StrToFloat too, I didn't check it). By means of Pascal (not even assembler), it can be accelerated at least twice, if not more. You can't speed up the IntToStr function with pascal, text overhead is added, and in this case the compiler copes with the text quite well.
The point of Pascal is ease of use and not to squench the last bit of performance out of the code. If you need that you either drop to a lower level and use e.g. PChar instead of AnsiString or simply use a different language that generates code as you like it (e.g. C++). Also this ease of use also includes maintainability of both the compiler and the RTL. Sure you can write StrToInt in assembly for every platform, but then this means a higher maintainance burden.
function geStrToInt(Str: String; Size: LongWord = isInteger): Boolean;
I didn't quite understand the question, but as I understood about "Str".It is more efficient to preface parameters of managed types with a const modifier.
The Armv8 architecture supports single-precision (32-bit) and double-precision (64-bit) floating-point data types and arithmetic as defined by the IEEE 754 floating-point standard. It also supports the half-precision (16-bit) floating-point data type for data storage, by supporting conversions between single-precision and half-precision data types and double-precision and half-precision data types. When Armv8.2-FP16 is implemented, it also supports the half-precision floating-point data type for data-processing operations.
Not to mention that D7 targeted Windows only. Big difference.I generally notice the executables compiled by Free Pascal are over twice as big and run about half the speed compared with the same code compiled by Delphi7.
Just like more modern Delphi. D7 is minimalist and has no deep support for unicode, or anything else after 2003 or so. If you need to compare to a Delphi, use a recent one, not something ancient.
Also, binary size minimization is no core target at the moment, nobody wants to do complex work on it, like improving smartlinking (except Pascaldragon, occasionally)
Did anyone notice the post dates jumped back after reply #110?Does it?
LOL, you're right.Did anyone notice the post dates jumped back after reply #110?Does it?
110: 2020-Dec-26
111: 2021-Sep-21
112: 2021-Sep-23
<snip> no function without the `inline` modifer will ever be inlined, no matter what).I just wanted to say that is a good thing. It makes the resulting code completely predictable which is often useful when debugging.
<snip> no function without the `inline` modifer will ever be inlined, no matter what).I just wanted to say that is a good thing. It makes the resulting code completely predictable which is often useful when debugging.
IOW, a function should never be inlined unless the "inline" modifier is present (and, if present, the compiler has to find it "acceptable")
Obviously it'd be nice if FPC had a more advanced form of the "AutoInline" switch turned on at all times so that you could just rely on it to do the right thing, but as it stands in reality it does the opposite (which is to say, no function without the `inline` modifer will ever be inlined, no matter what).
Yeah, that's what I was referring to in my comment. It works ok-ish as-is, but does not have any impact on pre-compiled units of course.Well yes.
Yeah, that's what I was referring to in my comment. It works ok-ish as-is, but does not have any impact on pre-compiled units of course.
Yeah, that's what I was referring to in my comment. It works ok-ish as-is, but does not have any impact on pre-compiled units of course.
Of course it does not, because it needs to have the node tree available in the PPU which is only the case if the routine has the inline directive or was compiled while the AutoInline optimization was enabled.
If for example some commercially sold code is released as ppu/o only, then they may not want to distribute such extra info (which could be used to gain insight into their unpublished code).