OOP_(re)

Mon, 2 Jun 1997 12:23:55 +0100 (BST)


On Sat, 31 May 1997 22:02:45 +0200 Frank Heckenbach  
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



The African Chief (laa12@cc.keele.ac.uk)

HTML conversion by Lluís de Yzaguirre i Maura
Institut de Lingüística Aplicada - Universitat "Pompeu Fabra"
e-mail: de_yza@upf.es