The other day, I compiled a unit I wrote several years ago under FPC and got the following warning:
queues.p(101,37) Warning: use of NEW or DISPOSE for untyped pointers is meaningless
The same unit compiles under GPC (20070904) without warnings. My question is: does GPC properly deallocate memory in such a case, or is the DISPOSE request ignored? The offending procedure is:
procedure killq(var Queue : QType ); var Item : pointer; begin while(not EmptyQ(Queue)) do begin Dequeue(Queue,Item); if Item<>nil then dispose(Item); end; end; { killq }
--------------------------| John L. Ries | Salford Systems | Phone: (619)543-8880 x107 | or (435)867-8885 | --------------------------|
John L. Ries wrote:
The other day, I compiled a unit I wrote several years ago under FPC and got the following warning:
queues.p(101,37) Warning: use of NEW or DISPOSE for untyped pointers is meaningless
The same unit compiles under GPC (20070904) without warnings. My question is: does GPC properly deallocate memory in such a case
Yes, it does.
More precisely, by default it uses libc's free() function which knows about the allocated size. So unless you override memory allocation functions, you should be safe.
Frank
On Mon, 5 Apr 2010, Frank Heckenbach wrote:
John L. Ries wrote:
The other day, I compiled a unit I wrote several years ago under FPC and got the following warning:
queues.p(101,37) Warning: use of NEW or DISPOSE for untyped pointers is meaningless
The same unit compiles under GPC (20070904) without warnings. My question is: does GPC properly deallocate memory in such a case
Yes, it does.
More precisely, by default it uses libc's free() function which knows about the allocated size. So unless you override memory allocation functions, you should be safe.
Frank
Good to know. Thanks.
--------------------------| John L. Ries | Salford Systems | Phone: (619)543-8880 x107 | or (435)867-8885 | --------------------------|
On 05 Apr 2010, at 19:13, John L. Ries wrote:
The other day, I compiled a unit I wrote several years ago under FPC and got the following warning:
queues.p(101,37) Warning: use of NEW or DISPOSE for untyped pointers is meaningless
The same unit compiles under GPC (20070904) without warnings. My question is: does GPC properly deallocate memory in such a case, or is the DISPOSE request ignored?
Note that FPC also frees memory in this case. It's just that new() and dispose() a) are supposed to be used in pairs, but since new(pointer) does not really make sense (FPC translates it into getmem(p,0) in TP and Delphi modes) FPC also consider dispose(pointer) to be inadvisable b) have extended functionality besides freeing memory (such as calling constructors/destructors with TP-style objects, and initialising/finalising structured types that contain reference-counted elements). This extra functionality can however only be activated by the compiler if it knows what the pointer points to, which is not the case when you dispose an untyped pointer.
FPC therefore considers calling freemem(untyped pointer) preferable to calling dispose(untyped pointer).
Jonas
On Mon, 5 Apr 2010, Jonas Maebe wrote:
On 05 Apr 2010, at 19:13, John L. Ries wrote:
The other day, I compiled a unit I wrote several years ago under FPC and got the following warning:
queues.p(101,37) Warning: use of NEW or DISPOSE for untyped pointers is meaningless
The same unit compiles under GPC (20070904) without warnings. My question is: does GPC properly deallocate memory in such a case, or is the DISPOSE request ignored?
Note that FPC also frees memory in this case. It's just that new() and dispose() a) are supposed to be used in pairs, but since new(pointer) does not really make sense (FPC translates it into getmem(p,0) in TP and Delphi modes) FPC also consider dispose(pointer) to be inadvisable b) have extended functionality besides freeing memory (such as calling constructors/destructors with TP-style objects, and initialising/finalising structured types that contain reference-counted elements). This extra functionality can however only be activated by the compiler if it knows what the pointer points to, which is not the case when you dispose an untyped pointer.
FPC therefore considers calling freemem(untyped pointer) preferable to calling dispose(untyped pointer).
Also useful to know... Thanks.
Since the unit in question is supposed to work with whatever data type is thrown at it (it is intended to be generic set of queuing routines), I have no immediate plans to change it to accomodate FPC, but I will keep this information in mind for future work. I'm just starting to work with FPC, but even though I'm a former Borland cultist, I think I like GPC better (though each compiler has its own strengths and weaknesses).
--------------------------| John L. Ries | Salford Systems | Phone: (619)543-8880 x107 | or (435)867-8885 | --------------------------|
On Tue, 6 Apr 2010, Adriaan van Os wrote:
John L. Ries wrote:
Since the unit in question is supposed to work with whatever data type is thrown at it (it is intended to be generic set of queuing routines),
But how are the queue elements allocated ?
By the calling program. All the queue unit ever sees are pointers and its own infrastructure.
--------------------------| John L. Ries | Salford Systems | Phone: (619)543-8880 x107 | or (435)867-8885 | --------------------------|
John L. Ries wrote:
On Tue, 6 Apr 2010, Adriaan van Os wrote:
John L. Ries wrote:
Since the unit in question is supposed to work with whatever data type is thrown at it (it is intended to be generic set of queuing routines),
But how are the queue elements allocated ?
By the calling program. All the queue unit ever sees are pointers and its own infrastructure.
But then, I think it would be good style to add a call to the unit that does the allocation - if you really want the unit to be generic, as you write. After all, it is possible that the calling program chooses an allocater that is incompatible with the deallocator in the unit.
Regards,
Adriaan van Os
On Tue, 6 Apr 2010, Adriaan van Os wrote:
John L. Ries wrote:
On Tue, 6 Apr 2010, Adriaan van Os wrote:
John L. Ries wrote:
Since the unit in question is supposed to work with whatever data type is thrown at it (it is intended to be generic set of queuing routines),
But how are the queue elements allocated ?
By the calling program. All the queue unit ever sees are pointers and its own infrastructure.
But then, I think it would be good style to add a call to the unit that does the allocation - if you really want the unit to be generic, as you write. After all, it is possible that the calling program chooses an allocater that is incompatible with the deallocator in the unit.
Perhaps. In general, it is the calling program that is expected to deallocate the elements (after dequeueing them). The procedure I sent in my initial message is intended to delallocate a queue that might not be empty, though normally it would be (but it is still a good idea to check). It is actually the only routine in the unit that deallocates elements. Possibly, for Borland compatibility, I will need to add an argument to the procedure that gives the element length, but that is a lot more C-ish than I like.
--------------------------| John L. Ries | Salford Systems | Phone: (619)543-8880 x107 | or (435)867-8885 | --------------------------|
Jonas Maebe wrote:
On 05 Apr 2010, at 19:13, John L. Ries wrote:
The other day, I compiled a unit I wrote several years ago under FPC and got the following warning:
queues.p(101,37) Warning: use of NEW or DISPOSE for untyped pointers is meaningless
The same unit compiles under GPC (20070904) without warnings. My question is: does GPC properly deallocate memory in such a case, or is the DISPOSE request ignored?
Note that FPC also frees memory in this case. It's just that new() and dispose() a) are supposed to be used in pairs, but since new(pointer) does not really make sense (FPC translates it into getmem(p,0) in TP and Delphi modes)
GPC rejects "New (untyped-pointer)". What does TP do?
b) have extended functionality besides freeing memory (such as calling constructors/destructors with TP-style objects, and initialising/finalising structured types that contain reference-counted elements).
Generally I agree that's a good idea (though it makes such an implementation of "untyped" containers impossible).
However, TP objects don't apply here, since their destructors are not called automatically (only explicitly with "Dispose (pointer, destructor (...))"), which I consider a major drawback of the TP object model, BTW.
Frank
On 06 Apr 2010, at 04:49, Frank Heckenbach wrote:
Jonas Maebe wrote:
Note that FPC also frees memory in this case. It's just that new() and dispose() a) are supposed to be used in pairs, but since new(pointer) does not really make sense (FPC translates it into getmem(p,0) in TP and Delphi modes)
GPC rejects "New (untyped-pointer)". What does TP do?
It allows it, which is why FPC allows it in TP and Delphi modes (albeit with a warning). FPC also rejects it in other modes.
Jonas
However, TP objects don't apply here, since their destructors are not called automatically (only explicitly with "Dispose (pointer, destructor (...))"), which I consider a major drawback of the TP object model, BTW.
What model do you think which allows automatic destructors. I have heard of three - The old TP objects declared on stack (and thus automatically freed at the exit of the procedure) with var o: tObject ... The drawback is that it does not allow inheritance and polymorphism (only encapsulation), because these need size changes. This makes them nearly useless.
- Garbage collector "a la Lisp". It has no sensible way to decide when to call it. Usually it was called "when memory is exhausted", a situation difficult to decide with nowadays shared memories systems. The program using it hanged for very long times when it started. A very bad compromise between lazziness and efficiency.
- reference count, a better compromise AFAIU. The count is incremented each time the object is affected to a new pointer (creation or affectation proper), decreased automatically at the exit of the procedure where the pointer is declared, and the object is freed when the count is zero. The size overhead is a word to store the count, the time overhead is the time to increment/decrement the count at affectation or exit of the procedure and check for zero.
Is one of the many object models of gpc doing this automatically ?
Maurice
Maurice Lombardi wrote:
However, TP objects don't apply here, since their destructors are not called automatically (only explicitly with "Dispose (pointer, destructor (...))"), which I consider a major drawback of the TP object model, BTW.
What model do you think which allows automatic destructors.
I think you're confusing two things: The automatic removal of objects (which you talk about) and the automatic calling of a destructor method when the object is removed, whether the removal itself is automatic or manual (which I meant).
I have heard of three
- The old TP objects declared on stack (and thus automatically freed at
the exit of the procedure) with var o: tObject ... The drawback is that it does not allow inheritance and polymorphism (only encapsulation), because these need size changes. This makes them nearly useless.
FWIW, I disagree here. It's not old -- languages such as C++ fully support this way. The drawback you mention only applies when you need polymorphism at creation time, e.g., you need to create objects of different (related) classes, depending on some actual parameters or runtime data. There are such situations, sure, but in many other situations the class is known at creation time; such objects will still be polymorphic when you call their virtual methods or pass them as reference parameters.
So I prefer a model (such as that of TP and C++) that allows both "static" objects (in global memory or on the stack) and dynamic ones. I always shudder when I see e.g. Java code that does "TFoo Foo = new Foo ();" compared to simply "TFoo Foo;" in C++ or "var Foo: TFoo;" with TP objects just to create a simple local object.
- Garbage collector "a la Lisp". It has no sensible way to decide when
to call it. Usually it was called "when memory is exhausted", a situation difficult to decide with nowadays shared memories systems. The program using it hanged for very long times when it started. A very bad compromise between lazziness and efficiency.
- reference count, a better compromise AFAIU. The count is incremented
each time the object is affected to a new pointer (creation or affectation proper), decreased automatically at the exit of the procedure where the pointer is declared, and the object is freed when the count is zero. The size overhead is a word to store the count, the time overhead is the time to increment/decrement the count at affectation or exit of the procedure and check for zero.
Which one of these is "better" seems to be a religious issue (FWIW, I also prefer reference counting most of the time, though I know that some progress has been made in the implementation of garbage collectors in recent years). GPC does not support either of them ATM; though you can plug in a garbage collector (such as the conservative Boehm GC) that simply overrides the low-level memory management routines.
But again, that wasn't my point. If you have a (say, TP style) object that declares a destructor "Done", in none of the cases it will be called automatically:
- If the object is on the stack and leaves scope, and you don't call the destructor manually ("Foo.Done").
- If the object is dynamic and you dispose of it simply with "Dispose (Foo)" rather than "Dispose (Foo, Done)".
- If the object is dynamic and its memory is reclaimed, e.g., by the Boehm GC (which doesn't know about objects at all, just memory blocks).
In contrast, in C++ in the first two cases the destructor is called automatically -- not in the last one which makes the Boehm GC somewhat less useful for C++; this is because it has to treat the memory as untyped, which goes back to the original question: Like there, if you only have an untyped pointer, the compiler cannot automatically call the destructor (if it would do so at all, i.e. not for TP objects anyway).
Frank
Frank Heckenbach a écrit :
Maurice Lombardi wrote:
But again, that wasn't my point. If you have a (say, TP style) object that declares a destructor "Done", in none of the cases it will be called automatically:
If the object is on the stack and leaves scope, and you don't call the destructor manually ("Foo.Done").
If the object is dynamic and you dispose of it simply with "Dispose (Foo)" rather than "Dispose (Foo, Done)".
If the object is dynamic and its memory is reclaimed, e.g., by the Boehm GC (which doesn't know about objects at all, just memory blocks).
In contrast, in C++ in the first two cases the destructor is called automatically
No plan to implement this in gpc, with an additional compiler option if you do not like to make this behavior the default for gpc ?
Many thanks for the clarifications anyway
Maurice
Maurice Lombardi wrote:
Frank Heckenbach a écrit :
Maurice Lombardi wrote:
But again, that wasn't my point. If you have a (say, TP style) object that declares a destructor "Done", in none of the cases it will be called automatically:
If the object is on the stack and leaves scope, and you don't call the destructor manually ("Foo.Done").
If the object is dynamic and you dispose of it simply with "Dispose (Foo)" rather than "Dispose (Foo, Done)".
If the object is dynamic and its memory is reclaimed, e.g., by the Boehm GC (which doesn't know about objects at all, just memory blocks).
In contrast, in C++ in the first two cases the destructor is called automatically
No plan to implement this in gpc, with an additional compiler option if you do not like to make this behavior the default for gpc ?
It would rather be a different object model: TP and OOE (and I think also Delphi) objects can have several destructors, and destructors with parameters. For automatic destructor calls, you need to have a single destructors without parameters (or at least a designated default destructor). IMHO, that's no big restriction (even with TP style objects, I almost always use a single parameterless destructor anyway), but it doesn't fit well with the existing models.
Frank