On Sat, 31 May 1997 22:02:45 +0200 Frank Heckenbach heckenb@mi.uni-erlangen.de wrote: [...]
The African Chief wrote:
Nothing stopping you from having a different name for your handle. Also, that is one reason for having the source code. You can change anything that doesn't suit you.
No, no, no! (This is, you can, but you really shouldn't!)
We're talking about a class library for widespread use, aren't we? Imagine if everybody changed the base objects: No units of different sources would fit together!
No, you shouldn't - IF you are going to distribute your own sources for public consumption. If it is just for your own private use, I don't see the problem.
The OOP way to do this is: if something doesn't suit you, derive a new class, and apply all modifications you want to the new class.
That is still based on the assumption that you are happy to live with the original class as it was. What if you are not? It is like having a bad ancestor with bad genes or no good genes at all. I can see the point in the "pure OOP" theory, and I would do that if I was presented with a ready-made ancestor (you can't choose your ancestors, or the genes which you will inherit or not inherit from them). However, if I were the one to decide what that ancestor should be, I would still do it exactly like I did it.
Apart from that, in other places, I would declare the handle as a THandle or HWnd - the meaning of which varies depending on the platform. In Win16 it is a Word. In Win32 it is a long integer (which is the same as an integer in GPC).
So don't declare it at all in TObject, but declare it where necessary with the appropriate type.
I may have other uses for it in TObject - and now I do. AFAICS when you dispose of an object and/or call its destructor, GPC does not set it to NIL. This has caused me a number of problems in trying to ascertain which instance has been destroyed (you can't just check by finding if its value is NIL). A temporary hack (until I can see a better alternative) has been to set the handle to a specific negative number in the destructor (the handle should normally never be negative).
You wouldn't. And that is the crux of the matter. In CHIEFAPP define a GetObjectCount function which returns the number of currently active objects. I can loop through these and do any number of things with the information - here is a trivial example;
for i := 1 to GetObjectCount do begin p := InstanceFromSelfID ( i );
So the ID can be a pointer! Then InstanceFromSelfID would be a simple lookup in an array (or whatever structure is used to hold the IDs).
It is not an array. Even if it were, you are still missing the point. The point is that you can locate that pointer even if you didn't know whether the object instance actually exists or not.
If Assigned ( p ) then begin If p^.Name = 'CHIEFDIALOG' then { blah blah }
better: If Typeof(p^) = Typeof(TChiefDialog) then { blah blah }
Perhaps.
Removes the need for Name field, also pointer comparisons are (usually) faster than string comparisons.
Can you write "TypeOf" to a file or to a Stream?
else If p^.Name = 'CHIEFCONTROL' then {blah blah}
Or better yet (depending on the situation): declare a virtual method in a common ancestor of ChiefDialog and ChiefControl (or an interface implemented by both of them), and just call this method without any if...then's.
ChiefObject?
And what kind of things would you do with an unique integer ID?
Inter-process communication and message passing for one.
No problem with pointers!
Big problem with pointers. Try passing a pObject or whatever from one application to another, with a call to PostMessage (I haven't, but I personally wouldn't try it).
(Assuming the objects reside in some kind of shared memory, but otherwise an integer ID would be quite useless as well.)
They don't need to reside in any kind of shared memory. I have passed messages from Win32 applications and DLLs to other Win32 applications (and even Win16 applications, and a CMD.EXE command prompt), all of which live in separate address spaces, with a call to PostMessage.
Actually, the Load constructor in TObject does nothing at all.
Yes, you're right. With Load being a constructor, and explicitly declaring the StreamRecs, this works. BTW: Does TObject need a Load constructor at all?
Probably. I seem to remember needing it there to implement my Streams.
SelfID can be accessed at random - e.g., through a loop. You can't do that with addresses.
Why not?
See below.
I suppose you're looping through an array of IDs, right?
No. You are dealing with data held in a list object (a TObject descendant). Have you actually looked at OBJECTS.PAS? I wouldn't want to be explaining its implementation here.
You can loop through an array of pointers as well.
Of course.
Where do you get the SelfIDs from? Perhaps a list of IDs stored in a parent object? You could put the addresses there instead, couldn't you?
There is a local instance in the OBJECTS unit which allocates an ID each time INIT or CREATE is called. There is a function : "InstanceFromSelfID" which returns a pObject for any given SelfID or NIL if there isn't any active object with that SelfID.
What do you mean by "allocate"? Do you have a global "collection" of all "active" (=existing?) objects, with their addresses and IDs?
An object declared in the implementation section, with a linked list of existing object instances.
(Side note: I don't think that's a good idea. In OOP, there's hardly a need for global data. If this collection is needed at all, it might be better put into a "parent object".)
I still don't see any problem. Can't you just remove any "ID" fields (that contain the ID of the object itself), and change any references to other obejcts' IDs into pointers to them? The "collection" above would then contain all the addresses of active objects, and you could easily loop through them.
You could walk the list, yes. However, not everybody is comfortable with linked lists - but AFAIK, everybody can write a "for loop".
So, I could really just do something randomly like this;
p := InstanceFromSelfID ( 5 );
This means "give me a pointer to the object with ID 5", or what?
Yes - when you don't even know whether such an object exists.
With the changes above, you would already have the pointer instead of the ID 5, so this function call would even get superfluous.
You would only have the pointer by walking the list and incrementing a counter until it got to 5, and then retrieving whatever was being pointed to when you get to 5. Doesn't save you any coding at all.
[...]
A lot of that one was implemented from the perspective of the Windows API and communications between all sorts of things. It will take too much time to explain it here.
I think Windoze is a bad example! It does pretty much anything with numerical handles, but this is not how it should be (IMHO) in a typed language as Pascal!
Windows is probably a bad example - I don't know. However, the mechanism that it presents for inter and intra process communication is simple enough to grasp, and lots of programmers know it - and, more importantly, it is the only one that I know. So everything I do with my classlib will be influenced by that mentality, especially since it was written solely for use with the Win16 and Win32 APIs. I presume that CYGWIN provides these facilities as well (which it would need to do to support the Win32 API). I also believe that the OS/2 PM APIs provide a similar messaging system. Perhaps being too influenced by this scheme is a handicap. However, I do not see that we can ignore it completely. As someone who has tried to write a Windows class library that makes no call to any other class library, my method of implementation was what worked best for me. I cannot say that it is the only, or "the best" or "the purest OOP" method. In fact, I was not concerned with such issues at all. However, I took a pragmatic approach, it worked, and it worked beautifully. Perhaps I should have done my GPC objects unit in a different way - but I doubt it. The only reason I did it at all was because I already had much of the code written elsewhere. I only had to amend it to compile under GPC. Like I said, BPCOMPAT.1.0 is just a beginning. The code is now out there. These discusssions have been interesting - and it shows that the old saying about lawyers ("two lawyers, three opinions") is also true for programmers :-). However, if anyone is going to take objects.pas to greater heights, it won't be me. I have already done the best I can do.
So, guys, please take it on! When Pierre finishes his work on TStream, we will release BPCOMPAT.1.0.1 - then others can take it further!
Pierre, I guees people are starting to think we're the same person writing from two different accounts... ;-)
I was beginning to wonder ! ;-)=
Now, back to Law ...
Best regards, The Chief Dr Abimbola A. Olowofoyeku (The African Chief, and the Great Elephant) Author of: Chief's Installer Pro v3.50 for Win16 and Win32. Homepage: http://ourworld.compuserve.com/homepages/African_Chief/ E-mail: laa12@cc.keele.ac.uk