According to Pierre Phaneuf:
That's the problem (that you're not sure it will not change). BTW, the constructor *is* the logical place do to such a thing...
I agree, but not every constructor call does actually initialize an object. Since it is difficult to be sure whether an object already is initialized or not, we have to decide about this when the constructor is *called*. For this reason, it happened to be easier to do the initialization outside the constructor's body.
If we find a method to be *sure* if the object is initialized, I can change constructors to do initialization in the body. Anyway, this does not matter unless you do tricks like `GetMem'ming an object.
But how do you check if an object is initialized or not?
Look whether the VMT field contains a nonzero value. To be absolutely sure, we can even check whether the pointer really points to a VMT: The first Word pointed to must be a positive Integer (the size of the object), the second one its negative (which is included for purposes like this one). Problem: This will only work if the object was pre-initialized to have a zero VMT field. This can be achieved with `New', but not with `GetMem', so you have still to be careful when implementing streams.
I don't think this is the best way either, unless you can devise a *sure* way to check out if the object is initialized.
What else would you propose?
In BP it cannot. It *must* be a constructor, because this is relied upon by TStream.Get to initialize the object VMT link correctly and allocate the correct amount of memory.
It could be in BP too, if we changed the `tStream' object. Just do everything, the constructor would normally do, by hand.
I am doing a BP compatible StreamRec facility for the BPCOMPAT package.
Great! }:-=)= (* This shall be a smiling Gnu. :*)
Are we looking for BP compatibility or not?
We are in part. The primary goal of GPC is to produce the best Pascal compiler the world has ever seen. (See the Info documentation for details.) BP compatibility is desireable because (ii) BP is not too bad and (i) many BP programmers will try GPC only if they can use their existing programs without any change.
BTW, I *am* also developing a class library made just for GPC using all the new features and fixing many of the problems with the BP OBJECTS.PAS and the TurboVision libraries.
:-)
It would be nice to have RTL functions to get an object name and parent, a bit like Delphi can give you the class name of an object.
How does Delphi do this? (Sorry for the stupid question, but my experience with Delphi is very limited, primarily because I cannot stand the mouse-only "user interface".)
A separate function would be better (probably something accepting a VMT pointer (as returned by TypeOf() ) as a parameter), since it wouldn't "use up" a method name.
What about making the structure of the VMT record visible for the program? For instance a built-in
Type VmtRec = record Size, NegSize: Integer; VirtualMethod: array [ 0..1 ] of Pointer; end (* VmtRec *);
Where the `1' above stands for the unknown number of methods in a VMT.
(* I thought about changing the VMT to a schema type
Type VmtRec ( n: Integer ) = record Size, NegSize: Integer; VirtualMethod: array [ 1..n ] of Pointer; end (* VmtRec *);
which would place `n' as an additional field at the very beginning of each VMT. But it's probably better to let `Size' stay the first field and to save the additional storage for the number of methods in the object. *)
And also not put all the symbols in every programs, just those that use the "ClassName()" function,
Difficult.
optimizing those using constants as their parameter
Easier.
and so on...
Most difficult.
A unique number is a bad idea and shouldn't be used. They can change and invalidate a file containing streamed objects from compile to compile.
That's what I meant with the "how to choose" problem. When deriving it from the name in an unambiguous way, it would work. But how would that "unambiguous way" look like? A CRTC check sum? (I don't really know what that is ...;)
The way I see it, the best way is to make a legal way to directly call a specific class constructor. Like the "typed new()" function (passing an object type as the first parameter) does, but being able to put a variable there. Maybe a GPC-specific new() function?
`New' already is as GPC-specific as it could be ...
With another name of course to avoid incompatibilities...
I will think about that and let you know if I have an idea.
Greetings,
Peter
Dipl.-Phys. Peter Gerwinski, Essen, Germany, free physicist and programmer peter.gerwinski@uni-essen.de - http://home.pages.de/~peter.gerwinski/ [970201] maintainer GNU Pascal [970510] - http://home.pages.de/~gnu-pascal/ [970125]
On Mon, 26 May 1997, Peter Gerwinski wrote:
I agree, but not every constructor call does actually initialize an object. Since it is difficult to be sure whether an object already is initialized or not, we have to decide about this when the constructor is *called*. For this reason, it happened to be easier to do the initialization outside the constructor's body.
Yes, you're right, the constructor calls that comes from calling the inherited constructor must not initialize the object... In BP, the two hidden parameters to constructors are the instance pointer (the Self parameter) and the VMT offset of the object that is being constructed. If the VMT offset given is the same as the called constructor, then it is the first constructor called and the object is initialized. If the VMT parameter isn't the same as the called constructor, it is because it is being called from within another constructor and hence the object is already initialized. This is actually easy! ;-)
Look whether the VMT field contains a nonzero value. To be absolutely sure, we can even check whether the pointer really points to a VMT: The first Word pointed to must be a positive Integer (the size of the object), the second one its negative (which is included for purposes like this one). Problem: This will only work if the object was pre-initialized to have a zero VMT field. This can be achieved with `New', but not with `GetMem', so you have still to be careful when implementing streams.
Here. A check in the constructor would not be reliable, since the memory could have been allocated with GetMem (and we have *no* mean to check this out) and the VMT field is filled with random bytes in that case, which could very well be an actual valid VMT pointer (with *some* luck, if the area of memory has already been allocated to another object, the chances are high that there is a valid VMT pointer there!).
In BP it cannot. It *must* be a constructor, because this is relied upon by TStream.Get to initialize the object VMT link correctly and allocate the correct amount of memory.
It could be in BP too, if we changed the `tStream' object. Just do everything, the constructor would normally do, by hand.
We cannot change BP! I mean, the idea of the BPCOMPAT package is to be compatible with BP, not make BP GPC compatible! :-)
I am doing a BP compatible StreamRec facility for the BPCOMPAT package.
Great! }:-=)= (* This shall be a smiling Gnu. :*)
This one is going to be a cheap clone 100% compatible (well, compatible interface). But there will be a "good" one made just for GPC soon...
Are we looking for BP compatibility or not?
We are in part. The primary goal of GPC is to produce the best Pascal compiler the world has ever seen. (See the Info documentation for details.) BP compatibility is desireable because (ii) BP is not too bad and (i) many BP programmers will try GPC only if they can use their existing programs without any change.
I meant in the BPCOMPAT... ;-)
BTW, I *am* also developing a class library made just for GPC using all the new features and fixing many of the problems with the BP OBJECTS.PAS and the TurboVision libraries.
:-)
Hence, I am not putting a lot of effort to enhance BPCOMPAT units, only fixing them so they work correctly.
It would be nice to have RTL functions to get an object name and parent, a bit like Delphi can give you the class name of an object.
How does Delphi do this? (Sorry for the stupid question, but my experience with Delphi is very limited, primarily because I cannot stand the mouse-only "user interface".)
Delphi does this by having methods in *every* objects that are provided by the compiler that returns the name of the objects and their parent.
What about making the structure of the VMT record visible for the program? For instance a built-in
Type VmtRec = record Size, NegSize: Integer; VirtualMethod: array [ 0..1 ] of Pointer; end (* VmtRec *);
Where the `1' above stands for the unknown number of methods in a VMT.
(* I thought about changing the VMT to a schema type
Type VmtRec ( n: Integer ) = record Size, NegSize: Integer; VirtualMethod: array [ 1..n ] of Pointer; end (* VmtRec *);
which would place `n' as an additional field at the very beginning of each VMT. But it's probably better to let `Size' stay the first field and to save the additional storage for the number of methods in the object. *)
This doesn't give much information about the object apart from its size and the number of methods and where they are... What we'd need is something like from which class is another class derived from, its name, and so on...
And also not put all the symbols in every programs, just those that use the "ClassName()" function,
Difficult.
Hmm... Is it possible to have a function automatically generated by the compiler before going to the optimizing stage? It would contain a table translating VMT pointers into strings of the classes name and would be optimized out of the program by the optimizer if unused, right?
A unique number is a bad idea and shouldn't be used. They can change and invalidate a file containing streamed objects from compile to compile.
That's what I meant with the "how to choose" problem. When deriving it from the name in an unambiguous way, it would work. But how would that "unambiguous way" look like? A CRTC check sum? (I don't really know what that is ...;)
CRC (Cyclic Redundancy Check) you must mean. It is an error detection scheme too often used as a hash key generator (which it isn't, it collides a bit too often).
The way I see it, the best way is to make a legal way to directly call a specific class constructor. Like the "typed new()" function (passing an object type as the first parameter) does, but being able to put a variable there. Maybe a GPC-specific new() function?
`New' already is as GPC-specific as it could be ...
With another name of course to avoid incompatibilities...
I will think about that and let you know if I have an idea.
What I mean is (for example) a NewObject() function that would work just like New(), but instead of a typed pointer or an object type itself, it would take a VMT pointer as the first parameter. It would make the construction of an object from a stream as simple as calling "Get:=NewObject(TStreamRec.VMTLink, Load(@Self));" at the end of the TStream.Get method.
BTW, how does New() work when it is passed an object type (say, PObject) as the first parameter?
Pierre Phaneuf
"The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offense." - Edsger W. Dijkstra.