Board index » delphi » inheritance, overriding, polymorphism, etc.

inheritance, overriding, polymorphism, etc.

Hello, there!

I have just faced a problem, on which I can't see a common solution yet.
I declare such class:

TCompiler = class(TObject)
  <bla-bla>
  function Compile: TObject; virtual; abstract;
  <bla-bla>
end;

Now, there're descendants of it, let's say:

TExpressionCompiler = class(TCompiler)
  <bla-bla>
  function Compile: TExpression; virtual;
  <bla-bla>
end;

and

TOperatorCompiler = class(TCompiler)
  <bla-bla>
  function Compile: TOperator; virtual;
  <bla-bla>
end;

and such...

Now we compile it and recieve warning that TExpressionCompiler.Compile and
TOperatorCompiler.Compile hide virtual method of base type (TCompiler).
There's no such situation in Delphi's help, the only example has procedure
methods instead of function (thus, they have no differences in declaration
and I can't override mine).
Solutions I have in mind:

1) Derive TExpression and TOperator from common ancestor. I don't really
like this way, cause the functionality of this classes is completely
different and I would not like messing them (tell me that I'm wrong if so).
2) Remove inheritance (make each TxxxCompiler descendant from TObject).
There may be a situation, where all of them must have a common method, which
I could place into TCompiler, so I don't really like this one also.

So, has anybody any comments, suggestions or solutions?

--

.sau
alienvis...@mail.ru

 

Re:inheritance, overriding, polymorphism, etc.


Quote
On Tue, 17 Apr 2001 15:06:06 +0400, "Donov Aleksey" <m...@ic.ru> wrote:
>Hello, there!

>I have just faced a problem, on which I can't see a common solution yet.
>I declare such class:

>TCompiler = class(TObject)
>  <bla-bla>
>  function Compile: TObject; virtual; abstract;
>  <bla-bla>
>end;

>Now, there're descendants of it, let's say:

>TExpressionCompiler = class(TCompiler)
>  <bla-bla>
>  function Compile: TExpression; virtual;
>  <bla-bla>
>end;

>and

>TOperatorCompiler = class(TCompiler)
>  <bla-bla>
>  function Compile: TOperator; virtual;
>  <bla-bla>
>end;

>and such...

>Now we compile it and recieve warning that TExpressionCompiler.Compile and
>TOperatorCompiler.Compile hide virtual method of base type (TCompiler).
>There's no such situation in Delphi's help, the only example has procedure
>methods instead of function (thus, they have no differences in declaration
>and I can't override mine).
>Solutions I have in mind:

>1) Derive TExpression and TOperator from common ancestor. I don't really
>like this way, cause the functionality of this classes is completely
>different and I would not like messing them (tell me that I'm wrong if so).
>2) Remove inheritance (make each TxxxCompiler descendant from TObject).
>There may be a situation, where all of them must have a common method, which
>I could place into TCompiler, so I don't really like this one also.

>So, has anybody any comments, suggestions or solutions?

You don't say exactly what it is you're trying to accomplish.

I don't understand the objective: It seems likely that you want
to use these polymorphically:

procedure whatever(c: TCompiler);
begin
...
x:= c.Compile;
//or maybe
c.Compile.DoSomething;
...
end;

and you want c.Compile to call TExpressionCompiler.Compile
or TOperatorCompiler.Compile depending on the type
of c. If this is what you want then you _must_ _override_
the Compile method with something of the same signature.

It seems like you really haven't thought enough about what
you do intend to do with these things: If what you want is
as above but you want the two Compile functions to
return something different then _what_ _type_ is the
variable x above supposed to be? Or if you want to
say c.Compile.DoSomething the question is this:
If TExpression and TOperator both have a DoSomething
method then they do _not_ have totally different functionality.

If they _do_ have totally different functionality then
the notion of using the two polymorphically just doesn't
make any sense so there's no problem - if they're really
totally different then they should be just disjoint classes
with no connection. If you _do_ want to use them
polymorphically as above (and if you actually have
something _specific_ in mind) then they _do_ have
some functionality in common.

So the thing to do is figure out what parts of the
functionality they should all have in common.
_That_ functionality goes into the base class,
you descend and override, etc. The parts of
functionality that they do not have in common
are _not_ bits of functionality that you want
to use polymorphically - that can't be, makes
no sense.

What I _suspect_ is that you're worried about the
bits of functionality that are totally different but
you still want to use polymorphically, but you're
just worried about this in the abstract, with no
specific example in mind. If so the good news
is there's no such thing - the _actual_ bits of
functionality that you _do_ want to use
polymorphically are bits of functionality that
they _do_ have in common.

(If this doesn't answer your question then please
give a _very_ specific example of something
you want to do.)

- Show quoted text -

Quote
>.sau
>alienvis...@mail.ru

Re:inheritance, overriding, polymorphism, etc.


David C. Ullrich <ullr...@math.okstate.edu> wrote in message
news:3adc477e.4667545@nntp.sprynet.com...

Quote
> You don't say exactly what it is you're trying to accomplish.

The goal is to parse a text of a program. The syntax of a language is close
to Pascal's and two of most used "atoms" in it are operators (doing smth)
and expressions (returning values).

Quote
> I don't understand the objective: It seems likely that you want
> to use these polymorphically:

> procedure whatever(c: TCompiler);
> begin
> ...
> x:= c.Compile;
> file://or maybe
> c.Compile.DoSomething;
> ...
> end;

> and you want c.Compile to call TExpressionCompiler.Compile
> or TOperatorCompiler.Compile depending on the type
> of c. If this is what you want then you _must_ _override_
> the Compile method with something of the same signature.

One of object I use is TFunction, which should compile all of it's content
(between "begin" and "end"), into such classes as TDeclareVarOperator,
TWhileOperator, TSummExpression, TNotLessExpression (you see, what they're
derived from, right?) and so on. To do this I suppose to use a low-level
function (not a class method) with a declaration like this:

function GetCompiler(Text: String): TCompiler;

This function should decide what does given Text seem to be - an operator or
an expression, create appropriate TCompiler descdendant and return it. Then
the caller (the TFunction method, which manages compilation of function's
body) should use GetCompiler(Text).Compile method, which (you know this
already) returns TOperator or TExpression instance.
The whole idea of such solution is that desired program text's parsing _is_
actually compiling it into a hierarchical pack of class instances, such as
TIfOperator, TDivisionExpression, etc. (So what TxxxCompiler.Compile method
returns is not actually TOperator or TExpression, but they're descdendants).
And the program's execution is just a calling TFunction.Execute method,
which calles it's TOperators descendantses Execute method. Almost the same
situation is with TExpression's childs - they return results by reading
Value property.

Quote
> It seems like you really haven't thought enough about what
> you do intend to do with these things: If what you want is

You right, I have just stepped on a first step of this task, so now I
collect my thoughts (and not only mine) of how implements all this stuff.

- Show quoted text -

Quote
> So the thing to do is figure out what parts of the
> functionality they should all have in common.
> _That_ functionality goes into the base class,
> you descend and override, etc. The parts of
> functionality that they do not have in common
> are _not_ bits of functionality that you want
> to use polymorphically - that can't be, makes
> no sense.

> What I _suspect_ is that you're worried about the
> bits of functionality that are totally different but
> you still want to use polymorphically, but you're
> just worried about this in the abstract, with no
> specific example in mind. If so the good news
> is there's no such thing - the _actual_ bits of
> functionality that you _do_ want to use
> polymorphically are bits of functionality that
> they _do_ have in common.

Actually, I thought of deriving TOperator and TExpression from common
ancestor such as TSyntaxItem just for this particular TCompiler.Compile
method, but I think shouldn't I have more serious reasons to do this?

Quote
> (If this doesn't answer your question then please
> give a _very_ specific example of something
> you want to do.)

Done.

.sau, a bit confused
alienvis...@mail.ru

Re:inheritance, overriding, polymorphism, etc.


Quote
On Wed, 18 Apr 2001 12:51:30 +0400, "Donov Aleksey" <m...@ic.ru> wrote:
>David C. Ullrich <ullr...@math.okstate.edu> wrote in message
>news:3adc477e.4667545@nntp.sprynet.com...

>> You don't say exactly what it is you're trying to accomplish.

>The goal is to parse a text of a program. The syntax of a language is close
>to Pascal's and two of most used "atoms" in it are operators (doing smth)
>and expressions (returning values).

Well, I'm not sure whether that answers my question about what
you're doing or not. The real question is whether you are going to
need to use TExpressionCompiler and TOperatorCompiler
polymorphically or not - if yes then they need to descend from
a common ancestor while if no then there's no problem, you
just use them separately.

I've parsed a few expressions. When I do that I have a
TExpression, and then many subclasses TConstant,
TVariable, TSum, etc; a TVariable has a Name, a
TSum has fields Left and Right each of type TExpression,
etc, and TExpression has a _virtual_ Value method, which
gets overriden in descendants

function TConstant.Value;
begin
  result:= FValue;
end;

function TSum.Value;
begin
  result:= Left.Value + Right.Value;
end;

Here the polymorphism is important: for example
in TSum.Value we need for Left.Value to call
TConstant.Value if Left is a TConstant and call
TSum.Value if Left is a TSum, etc.

In this situation I don't see any point to having
different sorts of compilers - there's one Compile
_function_ that parses the text, and if the text is
LeftSomething+RightSomething it returns

TSum.Create(Compile(LeftSomething),
           Compile(RightSomething)),

if the text is a constant it returns TConstant.Create(theValue),
etc. Don't see the point to multiple Compiler classes:
I wouldn't know which compiler class to create until
I've determined whether the text is a sum or a product
or whatever, and once I've determined that there's
no need for a compiler object, I just create and
return the right thing.

But that's just expression parsing. The right way to
parse something like a Pascal unit depends on the
structure of the language. Might be a List of TFunction
objects instead of a tree - now what a TFunction
object should be depends on whether there are
nested functions in the language, whether there
are local variables, etc.

Re:inheritance, overriding, polymorphism, etc.


David C. Ullrich <ullr...@math.okstate.edu> wrote in message
news:3adda2fe.3360695@nntp.sprynet.com...

Quote
> Well, I'm not sure whether that answers my question about what
> you're doing or not. The real question is whether you are going to
> need to use TExpressionCompiler and TOperatorCompiler
> polymorphically or not - if yes then they need to descend from
> a common ancestor while if no then there's no problem, you
> just use them separately.

May be. I realized that TExpressionCompiler (if one will take place at all)
will be actually called only in TxxxOperator or TxxxExpression
constructors - there's no need in it during TFunction creating.

Quote
> I've parsed a few expressions. When I do that I have a
> TExpression, and then many subclasses TConstant,
> TVariable, TSum, etc; a TVariable has a Name, a
> TSum has fields Left and Right each of type TExpression,
> etc, and TExpression has a _virtual_ Value method, which
> gets overriden in descendants

> function TConstant.Value;
> begin
>   result:= FValue;
> end;

> function TSum.Value;
> begin
>   result:= Left.Value + Right.Value;
> end;

> Here the polymorphism is important: for example
> in TSum.Value we need for Left.Value to call
> TConstant.Value if Left is a TConstant and call
> TSum.Value if Left is a TSum, etc.

This thing was almost the first I've done.

Quote
> In this situation I don't see any point to having
> different sorts of compilers - there's one Compile
> _function_ that parses the text, and if the text is
> LeftSomething+RightSomething it returns

> TSum.Create(Compile(LeftSomething),
>            Compile(RightSomething)),

> if the text is a constant it returns TConstant.Create(theValue),
> etc. Don't see the point to multiple Compiler classes:
> I wouldn't know which compiler class to create until
> I've determined whether the text is a sum or a product
> or whatever, and once I've determined that there's
> no need for a compiler object, I just create and
> return the right thing.

I see.

Quote
> But that's just expression parsing. The right way to
> parse something like a Pascal unit depends on the
> structure of the language. Might be a List of TFunction
> objects instead of a tree - now what a TFunction
> object should be depends on whether there are
> nested functions in the language, whether there
> are local variables, etc.

There'll be nested functions, local variables and such.
Another requirement is that there must be a library of user functions. I
suppose I have a Global function, which contains declarations of them, so
they can call each other.
But even it is not the main reason for a tree - when the complex ariphmetic
expression is parsed, it is supposed to be broken into simple ones. You have
already mentioned it - the Left and Right properties of TSumExpression are
both TExpression, and I suppose creating their instances from TSumExpression
will be a right decision, because once a expression is calculated and it has
returned the value, we can immediately free it, and if that TExpression can
manage its child TExpressions, it will free them, and they will free... etc.
In another case, we will seek through some TList of TExpression, looking for
expired ones (here we should include some key property) and freeing them.
It seems expensive and not such nice - having constructors and destructors
engine at hand, but not using it.

.sau
alienvis...@mail.ru

Re:inheritance, overriding, polymorphism, etc.


Quote
On Thu, 19 Apr 2001 15:28:33 +0400, "Donov Aleksey" <m...@ic.ru> wrote:
>David C. Ullrich <ullr...@math.okstate.edu> wrote in message
>news:3adda2fe.3360695@nntp.sprynet.com...

[...]

>There'll be nested functions, local variables and such.
>Another requirement is that there must be a library of user functions. I
>suppose I have a Global function, which contains declarations of them, so
>they can call each other.
>But even it is not the main reason for a tree - when the complex ariphmetic
>expression is parsed, it is supposed to be broken into simple ones. You have
>already mentioned it - the Left and Right properties of TSumExpression are
>both TExpression, and I suppose creating their instances from TSumExpression
>will be a right decision, because once a expression is calculated and it has
>returned the value, we can immediately free it, and if that TExpression can
>manage its child TExpressions, it will free them, and they will free... etc.
>In another case, we will seek through some TList of TExpression, looking for
>expired ones (here we should include some key property) and freeing them.
>It seems expensive and not such nice - having constructors and destructors
>engine at hand, but not using it.

Just offhand I don't see why the entire thing cannot be done with
various TThings Freeing their sub-things automatically when TThing
is freed.

- Show quoted text -

Quote
>.sau
>alienvis...@mail.ru

Other Threads