Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2010-12

generics class hierarchy

17 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

David Emerson

Hi all,

How can I use inheritance with generics? e.g.

type
  generic gt_point<_num> = class
    ...
    end;
  generic gt_box<_tpoint,_num> = class (_tpoint)
    ...
    end;

The above gives me an error; I've tried some other variations but haven't yet found anything that works

Thanks,
David

David Emerson
to David Emerson

I'll give a little more detail...

Right now I have these non-generic types:

t_point = class
  f_left, f_top : longint;  // so named for descendents
  // several fields and methods to manage it as I need
  end;
t_box = class (t_point)
  f_width, f_height : longint;
  // more fields and methods to manage it as I need
  end;

They've been great.
Now I have need for a point and box that use real coordinates. The structure would be identical -- simply replacing "longint" with "single" -- which is why I thought generics would be perfect.

However, t_box inherits from t_point, and this appears to be a sticking point.

My code looks like this:

generic gt_point<_num> = class
  f_left, f_top : _num;
  // other fields, methods
  end;
generic gt_box<_t_point,_num> = class (_t_point)
  f_width, f_height : _num;
  // other fields, methods
  end;
t_real_point = specialize gt_point<single>;
t_real_box = specialize gt_box<t_real_point,single>;

Thanks all!
~David.

David Emerson
to David Emerson

Well, I'm guessing generics aren't really ready to be fully used... I'm getting an error "There is no method in an ancestor class to be overridden" where the ancestor class was a specialized generic. Doesn't seem very promising for actual use.

So I'm abandoning generics for now. Hopefully they'll become more usable in the future!

Keep up the good work, guys

Cheers,
David

...here's what I couldn't do:

generic gt_point<_num> = class
  f_left, f_top : _num;
  procedure set_lt (l, t : _num); virtual;
  end;
procedure gt_point.set_lt (l, t : _num);
  begin
    f_left := l;
    f_top := t;
  end;
generic gt_box<_t_point,_num> = class (_t_point)   // FAILS :-(
  f_width, f_height : _num;
  end;
t_real_point = specialize gt_point<single>;
t_real_box = specialize gt_box<t_real_point,single>;
t_special_real_box = class (t_real_box)
  procedure set_lt (l, t : single); override;  // FAILS :-(
  end;
David Emerson
to David Emerson

err... my mistake. the error message was referring to a different problem, which I was blind to in my hurry. Sorry for all the messages. I'll shut up now. :)

David Emerson wrote
Well, I'm guessing generics aren't really ready to be fully used... I'm

getting

Show quoted text 5 lines
an error "There is no method in an ancestor class to be overridden" where the ancestor class was a specialized generic. Doesn't seem very promising for actual use. So I'm abandoning generics for now. Hopefully they'll become more usable in

the

Show quoted text 38 lines
future! Keep up the good work, guys Cheers, David ...here's what I couldn't do: generic gt_point<_num> = class   f_left, f_top : _num; procedure set_lt (l, t : _num); virtual;   end; procedure gt_point.set_lt (l, t : _num); begin f_left := l; f_top := t; end; generic gt_box<_t_point,_num> = class (_t_point) // FAILS :-(   f_width, f_height : _num;   end; t_real_point = specialize gt_point<single>; t_real_box = specialize gt_box<t_real_point,single>; t_special_real_box = class (t_real_box) procedure set_lt (l, t : single); override; // FAILS :-( end; _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Sven Barth
to David Emerson
David Emerson wrote
err... my mistake. the error message was referring to a different problem, which I was blind to in my hurry. Sorry for all the messages. I'll shut up now. :)

Would you please state your FPC version the next time? (If you have stated I, but I haven't seen it: I'm sorry) Some problems might be fixed in the development version while they aren't in the latest release.

Regards,
Sven

David Emerson
to Sven Barth
Would you please state your FPC version the next time? (If you have stated I, but I haven't seen it: I'm sorry) Some problems might be fixed in the development version while they aren't in the latest release.

I tried with both 2.4.2 and 2.5.1 (fetched via svn and compiled. Took me quite some time to figure out how to get it to run properly without installing it as root)

The main error was the same in both:

genericstest.pas(22,50) Error: Identifier not found "_t_point"

line 22, in "type" context:
generic gt_box<_t_point,_num> = class (_t_point) // FAILS :-(

Cheers,
David

Sven Barth
to David Emerson
David Emerson wrote 14 lines
> Would you please state your FPC version the next time? (If you have > stated I, but I haven't seen it: I'm sorry) Some problems might be fixed > in the development version while they aren't in the latest release. I tried with both 2.4.2 and 2.5.1 (fetched via svn and compiled. Took me quite some time to figure out how to get it to run properly without installing it as root) The main error was the same in both: genericstest.pas(22,50) Error: Identifier not found "_t_point" line 22, in "type" context: generic gt_box<_t_point,_num> = class (_t_point) // FAILS :-(

I've now looked at your example code in more detail (I was working the last time I wrote you). Where did you define "_t_point"? I only found a "t_point" in your second mail.

Would you please post (or at least attach) your complete test unit? In the meantime I'll try to test it with the code pieces you wrote.

Regards,
Sven

David Emerson
to Sven Barth
Sven Barth wrote
I've now looked at your example code in more detail (I was working the last time I wrote you). Where did you define "_t_point"? I only found a "t_point" in your second mail.

_t_point is part of the template list.

I guess it's a limitation. Conceptually it doesn't seem that different to be specifying a class to inherit from in the template list, but the compiler isn't handling it yet.

Would you please post (or at least attach) your complete test unit? In the meantime I'll try to test it with the code pieces you wrote.
unit genericstest;
{$mode objfpc}
interface

uses
classes,

  sysutils;
type
  generic gt_point <_num> = class
    f_left, f_top : _num;
    procedure set_lt (l, t : _num); virtual;
    end;
type
  generic gt_box<_t_point,_num> = class (_t_point)   // FAILS :-(
    f_width, f_height : _num;
    end;
  t_real_point = specialize gt_point<single>;
  t_special_real_point = class (t_real_point)
    procedure set_lt (l, t : single); override;
    end;
implementation
procedure gt_point.set_lt (l, t : _num);
  begin
    f_left := l;
    f_top := t;
  end;
procedure t_special_real_point.set_lt (l, t : single);
  begin
    inherited;
  end;
end.
Honza
to David Emerson

2010/12/19 David Emerson :

type  generic gt_box<_t_point,_num> = class (_t_point)   // FAILS :-( f_width, f_height : _num; end;

I think it should fail according to the docs, see:

http://www.freepascal.org/docs-html/ref/refse42.html

"There is a single placeholder _T. It will be substituted by a type identifier when the generic class is specialized. The identifier _T *may not be used for anything else than a placehoder*. "

The bold part is IMO violated by the declaration. Anyway, it could be perhaps (not tested) written as:

type
  TBoxProxy = class(_t_point);
  generic gt_box<_t_point, _num> = class(TBoxProxy)
    f_width, f_height : _num;
  end;

Another strange point is, that the declaration of gt_box doesn't use the formal specialization paramater `_t_point` at all (in the posted code), so the same the other way around should also work:

type
  generic gt_box<_num> = class<_t_point>
    f_width, f_height : _num;
  end;

A 3rd note is that your code can't compile as _t_point is not declared when gt_box is declared, but the declaration wants to inherit from _t_point, so IMO also for this the code is rightfully rejected the compiler.

HTH

bflm freepascal-bits.blogspot.com
Sven Barth
to Honza: 2010/12/19 David Emerson : I think it should fail according to the docs, see:…
Honza wrote 14 lines
2010/12/19 David Emerson: > type > generic gt_box<_t_point,_num> = class (_t_point) // FAILS :-( > f_width, f_height : _num; > end; I think it should fail according to the docs, see: http://www.freepascal.org/docs-html/ref/refse42.html "There is a single placeholder _T. It will be substituted by a type identifier when the generic class is specialized. The identifier _T *may not be used for anything else than a placehoder*. "

According to the documentation I'd say that it should succeed, because in "class(BaseClass)" "BaseClass" is a type identifier. But of course that has its own set of problems... see the second last paragraph of this mail.

Show quoted text 9 lines
The bold part is IMO violated by the declaration. Anyway, it could be perhaps (not tested) written as: type TBoxProxy = class(_t_point); generic gt_box<_t_point, _num> = class(TBoxProxy) f_width, f_height : _num; end;

No, this won't work, because "_t_point" won't be defined when "TBoxProxy" is parsed.

Another strange point is, that the declaration of gt_box doesn't use the formal specialization paramater `_t_point` at all (in the posted code), so the same the other way around should also work:

It IS used, because David wants to influence the class the generic class gt_box inherits from when specializing the class.

E.g.:

type
   TIntPoint = class
     x, y: Integer;
   end;
   TFloatPoint = class
     x, y: Single;
   end;
   generic gt_box<_t_point, _num> = class(_t_point)
     width, height: _num;
   end;
   TFloatBox = specialize gt_box<TFloatPoint, Single>;
   TIntBox = specialize gt_box<TIntPoint, Integer>;
Show quoted text 5 lines
type generic gt_box<_num> = class<_t_point> f_width, f_height : _num; end;

This won't compile because of the "<...>" around "_t_point". Also it's not what Daniel intends.

A 3rd note is that your code can't compile as _t_point is not declared when gt_box is declared, but the declaration wants to inherit from _t_point, so IMO also for this the code is rightfully rejected the compiler.

The question is whether this should be rejected if "_t_point" is a template parameter... on the other hand this would violate compile time checks of the generic class...

I'm still thinking how David's idea could be achieved in another way which is supported by the compiler...

Regards,
Sven

David Emerson
to Honza: 2010/12/19 David Emerson : I think it should fail according to the docs, see:…
Honza wrote 13 lines
2010/12/19 David Emerson : > type >  generic gt_box<_t_point,_num> = class (_t_point)   // FAILS :-( > f_width, f_height : _num; > end; I think it should fail according to the docs, see: http://www.freepascal.org/docs-html/ref/refse42.html "There is a single placeholder _T. It will be substituted by a type identifier when the generic class is specialized. The identifier _T *may not be used for anything else than a placehoder*. "

Well, IMO the docs are a bit vague as to the definition of "placeholder". However, according to experience:
- a placeholder _can_ be used for a type identifier that is used as a field within the class
- a placeholder can't be used for a type identifier that is used to specify the ancestor class to inherit from

Maybe "placeholder" is also referring to something that's going on internally to the compiler -- when the generic is defined, its class structure and VMT and everything must be known. I'm no compiler writer, but I guess it makes sense from that perspective. I was thinking of "placeholder" from the other perspective, in terms of words: the specialization substitutes these words into those places, and those places can by any type identifiers. More like what I'd expect from e.g. a scripting language. But it doesn't work like that.

Show quoted text 8 lines
The bold part is IMO violated by the declaration. Anyway, it could be perhaps (not tested) written as: type TBoxProxy = class(_t_point); generic gt_box<_t_point, _num> = class(TBoxProxy) f_width, f_height : _num; end;

no, this is completely not is a placeholder. I want to use a placeholder to specify the ancestor class. In your example here you're treating _t_point as an already-defined class which TBoxProxy can inherit from. _t_point doesn't exist, it never exists, it's just a placeholder for a proper type identifier.

Another strange point is, that the declaration of gt_box doesn't use the formal specialization paramater `_t_point` at all (in the posted code)

yes it does. It is used to specify the ancestor class. But I think I have now beaten the issue to death.

I am curious, though, if e.g. delphi allows this syntax?

Cheers,
David

Show quoted text 20 lines
, so the same the other way around should also work: type generic gt_box<_num> = class<_t_point> f_width, f_height : _num; end; A 3rd note is that your code can't compile as _t_point is not declared when gt_box is declared, but the declaration wants to inherit from _t_point, so IMO also for this the code is rightfully rejected the compiler. HTH -- bflm freepascal-bits.blogspot.com _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Honza
to Sven Barth: According to the documentation I'd say that it should succeed, because in…

2010/12/19 Sven Barth :

Show quoted text 8 lines
> "There is a single placeholder _T. It will be substituted by a type > identifier when the generic class is specialized. The identifier _T > *may not be used for anything else than a placehoder*. " > According to the documentation I'd say that it should succeed, because in "class(BaseClass)" "BaseClass" is a type identifier. But of course that has its own set of problems... see the second last paragraph of this mail.

The type parameter in the inheritance part clause, i.e. class(T) part of the generic class declaration has nothing to do with the placeholder formal parameter. The placeholder formal arg list are given inside the sharp brackets as a kind of macro parameters. And the docs explicitly states that a placeholder identifier may not be used elsewhere in the generic declaration except for identifying the place where the "macro" expansion should substitute the instantiation/specialization parameter. So using it in the inheritance clause is invalid. IMO the docs are clear on this and my experiments seems to confirm this behaviour. I have excersised generics a lot to get heLib compiled and working. Still the latest changes in the compiler broke the published code as I realized very recently and not yet uploaded the remedy which sits on my local disk.

Show quoted text 12 lines
> The bold part is IMO violated by the declaration. Anyway, it could be > perhaps (not tested) written as: > > type >   TBoxProxy = class(_t_point); >   generic gt_box<_t_point, _num>  = class(TBoxProxy) >     f_width, f_height : _num; >   end; > No, this won't work, because "_t_point" won't be defined when "TBoxProxy" is parsed.

Yes it's not defined and that was my 3rd note, you can't base a generic declaration on a not yet specialized ancestor.

Show quoted text 6 lines
> Another strange point is, that the declaration of gt_box doesn't use > the formal specialization paramater `_t_point` at all (in the posted > code), so the same the other way around should also work: It IS used, because David wants to influence the class the generic class gt_box inherits from when specializing the class.

It is not used *anywhere except* in the invalid place of the ancestor type.

Show quoted text 24 lines
type  TIntPoint = class    x, y: Integer;  end;  TFloatPoint = class    x, y: Single;  end;  generic gt_box<_t_point, _num> = class(_t_point)    width, height: _num;  end;  TFloatBox = specialize gt_box<TFloatPoint, Single>;  TIntBox = specialize gt_box<TIntPoint, Integer>; > type >   generic gt_box<_num>  = class<_t_point> >     f_width, f_height : _num; >   end; > This won't compile because of the "<...>" around "_t_point". Also it's not what Daniel intends.

Yes, that's just a typo, round parenthesis are what I've meant and should wrote there.

Show quoted text 11 lines
> A 3rd note is that your code can't compile as _t_point is not declared > when gt_box is declared, but the declaration wants to inherit from > _t_point, so IMO also for this the code is rightfully rejected the > compiler. The question is whether this should be rejected if "_t_point" is a template parameter... on the other hand this would violate compile time checks of the generic class... I'm still thinking how David's idea could be achieved in another way which is supported by the compiler...

I've not yet got time to look at his goal at all, so I don't know. I just spotted the invalid constructs presented.

bflm freepascal-bits.blogspot.com
Sven Barth
to Honza: 2010/12/19 Sven Barth : The type parameter in the inheritance part clause, i.e. class(T)…
Honza wrote 23 lines
2010/12/19 Sven Barth: >> "There is a single placeholder _T. It will be substituted by a type >> identifier when the generic class is specialized. The identifier _T >> *may not be used for anything else than a placehoder*. " >> > > According to the documentation I'd say that it should succeed, because in > "class(BaseClass)" "BaseClass" is a type identifier. But of course that has > its own set of problems... see the second last paragraph of this mail. The type parameter in the inheritance part clause, i.e. class(T) part of the generic class declaration has nothing to do with the placeholder formal parameter. The placeholder formal arg list are given inside the sharp brackets as a kind of macro parameters. And the docs explicitly states that a placeholder identifier may not be used elsewhere in the generic declaration except for identifying the place where the "macro" expansion should substitute the instantiation/specialization parameter. So using it in the inheritance clause is invalid. IMO the docs are clear on this and my experiments seems to confirm this behaviour. I have excersised generics a lot to get heLib compiled and working. Still the latest changes in the compiler broke the published code as I realized very recently and not yet uploaded the remedy which sits on my local disk.

While I DO agree with you (after some thinking about the consequences) that a base class should not be allowed to be specified by a template parameter (and this is the way it already is), I don't agree with you that the documentation states this as clearly as you propose it.

The documentation says that every occurrence of a template parameter will be replaced by a type identifier (e.g. Integer, String, TObject). Now the documentation of a normal class declaration ( http://www.freepascal.org/docs-html/ref/refse31.html#x67-770006.1 ) states that the heritage clause contains a "class type identifier" which is just a special case of a type identifier.
So when one reads the documentation of generics one CAN (!) come to the (wrong) conclusion that you can also put a template parameter into the heritage clause of a class.

Here the documentation of generics should state more clearly that "class(T)" is not allowed, neither in the main class nor in sub classes.

Show quoted text 6 lines
> I'm still thinking how David's idea could be achieved in another way which > is supported by the compiler... I've not yet got time to look at his goal at all, so I don't know. I just spotted the invalid constructs presented.

@David: Maybe you can restructure your class hierarchy to something like this (you'll need to be a bit creative here ^^):

   generic gt_box<_num> = class
     type
       t_point = specialize gt_point<_num>;
     var
       f_position: t_point;
       f_width, f_height : _num;
     end;

or this

   generic gt_box<_t_point, _num> = class
     f_position: _t_point;
     f_width, f_height: _num;
   end;

I don't really see another possibility.

Regards,
Sven

Honza
to Sven Barth

2010/12/19 Sven Barth :

While I DO agree with you (after some thinking about the consequences) that a base class should not be allowed to be specified by a template parameter (and this is the way it already is), I don't agree with you that the documentation states this as clearly as you propose it.

I admit, that it looks clear to me only now - after/because of several hours bouncing my head against the keyboard when I struggled to get generics make what I wanted, so I'm not anymore unbiased when looking at the docs :-)

Here the documentation of generics should state more clearly that "class(T)" is not allowed, neither in the main class nor in sub classes.

I believe any reasonable improvement patch of the docs would be welcome by the dev team. You can then get into the chapter text exactly those words you would like to read there - a nice compensation for a little effort I think :-)

Best regards,

bflm freepascal-bits.blogspot.com