Sign in Join

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

Mailing lists / fpc-devel / 2010-05

Parameters must match exactly?

74 messages · 17 people · –

Reply by email Subscribe to this list Your subscription

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

Graeme Geldenhuys

Hi,

I tried using FPC 2.5.1 today to see how compatible is our application with it compared to FPC 2.4.1

I got stacks of the following errors. Why is this change forced in FPC 2.5.1? TBulkInvoiceRateListForm class is a descendant of TfpgWindowBase so there should be a problem. I don't understand. :-/

-------------
frm_bulkinvoiceratelist.pas(84,77) Error: Call by var for arg no. 2 has to match exactly: Got "TBulkInvoiceRateListForm" expected "TfpgWindowBase" fpg_base.pas(2207,31) Hint: Found declaration:
TfpgApplicationBase.CreateForm(TComponentClass,out TfpgWindowBase); frm_bulkinvoiceratelist.pas(307) Fatal: There were 1 errors compiling module, stopping
--------------------

To get my code to compile again, I have to change the following code from

 fpgApplication.CreateForm(TBulkInvoiceRateListForm, BulkDiscountListForm);

...to this...

 fpgApplication.CreateForm(TBulkInvoiceRateListForm,
    TfpgWindowBase(BulkDiscountListForm));

Now I have to include ugly casts all over the place. :-(

I got similar issues with TStrings and TStringList. TStringList is a descendant of TStrings, but I can't pass a TStringList type as a parameter to a method declared with TStrings.

Maybe I don't understand the var parameter correctly (but haven't tested to see what happens if I remove var from parameter list). If I have a class instance, when should I pass it to a method or function/procedure as var and when don't I need to use var parameters?

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Flávio Etrusco
to Graeme Geldenhuys
Graeme Geldenhuys wrote 47 lines
Hi, I tried using FPC 2.5.1 today to see how compatible is our application with it compared to FPC 2.4.1 I got stacks of the following errors.  Why is this change forced in FPC 2.5.1?  TBulkInvoiceRateListForm class is a descendant of TfpgWindowBase so there should be a problem. I don't understand. :-/ ------------- frm_bulkinvoiceratelist.pas(84,77) Error: Call by var for arg no. 2 has to match exactly: Got "TBulkInvoiceRateListForm" expected "TfpgWindowBase" fpg_base.pas(2207,31) Hint: Found declaration: TfpgApplicationBase.CreateForm(TComponentClass,out TfpgWindowBase); frm_bulkinvoiceratelist.pas(307) Fatal: There were 1 errors compiling module, stopping -------------------- To get my code to compile again, I have to change the following code from  fpgApplication.CreateForm(TBulkInvoiceRateListForm, BulkDiscountListForm); ...to this...  fpgApplication.CreateForm(TBulkInvoiceRateListForm,    TfpgWindowBase(BulkDiscountListForm)); Now I have to include ugly casts all over the place. :-( I got similar issues with TStrings and TStringList. TStringList is a descendant of TStrings, but I can't pass a TStringList type as a parameter to a method declared with TStrings. Maybe I don't understand the var parameter correctly (but haven't tested to see what happens if I remove var from parameter list). If I have a class instance, when should I pass it to a method or function/procedure as var and when don't I need to use var parameters? Regards,  - Graeme - --

I thought this feature was an improvement over Delphi too. But IIRC the argumentation was that it generated confusion, as somebody could think a reintroduced method would be invoked instead of the base one. I guess there was some other more compelling reason, but I can't remember right now, but I'm positive this was already brought up on the list.

Best regards,
Flávio

Reply by email

Michael Van Canneyt
to Graeme Geldenhuys: I tried using FPC 2.5.1 today to see how compatible is our application with it compared…
Graeme Geldenhuys wrote 40 lines
Hi, I tried using FPC 2.5.1 today to see how compatible is our application with it compared to FPC 2.4.1 I got stacks of the following errors. Why is this change forced in FPC 2.5.1? TBulkInvoiceRateListForm class is a descendant of TfpgWindowBase so there should be a problem. I don't understand. :-/ ------------- frm_bulkinvoiceratelist.pas(84,77) Error: Call by var for arg no. 2 has to match exactly: Got "TBulkInvoiceRateListForm" expected "TfpgWindowBase" fpg_base.pas(2207,31) Hint: Found declaration: TfpgApplicationBase.CreateForm(TComponentClass,out TfpgWindowBase); frm_bulkinvoiceratelist.pas(307) Fatal: There were 1 errors compiling module, stopping -------------------- To get my code to compile again, I have to change the following code from fpgApplication.CreateForm(TBulkInvoiceRateListForm, BulkDiscountListForm); ...to this... fpgApplication.CreateForm(TBulkInvoiceRateListForm, TfpgWindowBase(BulkDiscountListForm)); Now I have to include ugly casts all over the place. :-( I got similar issues with TStrings and TStringList. TStringList is a descendant of TStrings, but I can't pass a TStringList type as a parameter to a method declared with TStrings. Maybe I don't understand the var parameter correctly (but haven't tested to see what happens if I remove var from parameter list). If I have a class instance, when should I pass it to a method or function/procedure as var and when don't I need to use var parameters?

You only need var/out when you want to change the instance pointer, i.e. switch to another instance. And I count FreeAndNil() among this. (.Free on the other hand is still possible, but not recommended).

If you just want to set properties or call methods on an existing instance, there is no need to use out or var.

Michael.

Reply by email

Mattias Gaertner
to Graeme Geldenhuys: I tried using FPC 2.5.1 today to see how compatible is our application with it compared…
Show quoted text 11 lines
[...] I got similar issues with TStrings and TStringList. TStringList is a descendant of TStrings, but I can't pass a TStringList type as a parameter to a method declared with TStrings. Maybe I don't understand the var parameter correctly (but haven't tested to see what happens if I remove var from parameter list). If I have a class instance, when should I pass it to a method or function/procedure as var and when don't I need to use var parameters?

"var" means, you can do this:

type
  TQStrings = class(TStrings)
  end;
procedure A(var s: TStrings);
begin
  s:=TQStrings.Create;
end;

Then your TStringList will no longer be a TStringList. That's why 'var' must match exactly.
Same for out.
Const and no modifier allows to pass descendants.

Mattias

Reply by email

Graeme Geldenhuys
to Michael Van Canneyt: You only need var/out when you want to change the instance pointer, i.e. switch to…

Michael Van Canneyt het geskryf:

Show quoted text 7 lines
You only need var/out when you want to change the instance pointer, i.e. switch to another instance. And I count FreeAndNil() among this. (.Free on the other hand is still possible, but not recommended). If you just want to set properties or call methods on an existing instance, there is no need to use out or var.

Thank you Michael, I understand it much better now. Could you please add this information into the FPC Language Reference document? I re-read Chapter 11 in ref.pdf and there is no mention of what you said, and the documentation doesn't cover what happens if a Class is passed as a parameter. It only mentions simple types and arrays as parameters. Maybe adding a extra section in that chapter explaining when to use what?

This brings me to another question. What is supposed to happen if you pass a Class instance as a const parameter? Does that even make sense? If not, why does the compiler allow that? Again, the documentation doesn't cover this scenario. Same question applies to passing a record structure.

eg:
----------------------------
procedure ProcessPerson(const APerson: TPerson);
begin
... // Am I allowed to change APerson.Name property? ... // Am I allowed to assign a new instance to APerson? end;

...
  lPerson := TPerson.Create;
  lPerson.Name := 'Graeme Geldenhuys';
  ProcessPerson(lPerson);

----------------------------

* I see I can't assign a new instance to APerson - this makes sense.
* I can assign new values to APerson.Name - am I supposed to be allowed to do that?

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Michael Van Canneyt
to Graeme Geldenhuys: Michael Van Canneyt het geskryf: Thank you Michael, I understand it much better now.…
Graeme Geldenhuys wrote 16 lines
Michael Van Canneyt het geskryf: > > You only need var/out when you want to change the instance pointer, i.e. > switch to another instance. And I count FreeAndNil() among this. (.Free > on the other hand is still possible, but not recommended). > > If you just want to set properties or call methods on an existing instance, > there is no need to use out or var. Thank you Michael, I understand it much better now. Could you please add this information into the FPC Language Reference document? I re-read Chapter 11 in ref.pdf and there is no mention of what you said, and the documentation doesn't cover what happens if a Class is passed as a parameter. It only mentions simple types and arrays as parameters. Maybe adding a extra section in that chapter explaining when to use what?

I will do that.

Show quoted text 25 lines
This brings me to another question. What is supposed to happen if you pass a Class instance as a const parameter? Does that even make sense? If not, why does the compiler allow that? Again, the documentation doesn't cover this scenario. Same question applies to passing a record structure. eg: ---------------------------- procedure ProcessPerson(const APerson: TPerson); begin ... // Am I allowed to change APerson.Name property? ... // Am I allowed to assign a new instance to APerson? end; ... lPerson := TPerson.Create; lPerson.Name := 'Graeme Geldenhuys'; ProcessPerson(lPerson); ---------------------------- * I see I can't assign a new instance to APerson - this makes sense. * I can assign new values to APerson.Name - am I supposed to be allowed to do that?

Yes. In the same way as when you have a const pointer to a array of char, you can change the array's contents, but not the pointer.

const means: you cannot change the value of the passed parameter.

Const doesn't make a lot of sense for pointers, since, with some imagination, passing a pointer is equivalent to a var parameter. (see C and other such languages)

Michael.

Reply by email

Martin
to Mattias Gaertner: On Fri, 14 May 2010 16:45:50 +0200 "var" means, you can do this: type TQStrings =…
Mattias Gaertner wrote 16 lines
"var" means, you can do this: type TQStrings = class(TStrings) end; procedure A(var s: TStrings); begin s:=TQStrings.Create; end; Then your TStringList will no longer be a TStringList. That's why 'var' must match exactly. Same for out. Const and no modifier allows to pass descendants.

yes, true, then if we follow that consequently:

procedure Foo(out s: TStrings);
begin
end;
var x: TObject;
foo(x);

should work? (or well only if it is made sure, that out really means out, so the procedure is not allowed to use the value that was passed in)

"s" can be assigned a TStrings (or descendant of) => which should be fine, since it can be stored in x too.

Martin

Reply by email

Mattias Gaertner
to Martin
Show quoted text 33 lines
On 14/05/2010 15:52, Mattias Gaertner wrote: > "var" means, you can do this: > > type > TQStrings = class(TStrings) > end; > > procedure A(var s: TStrings); > begin > s:=TQStrings.Create; > end; > > Then your TStringList will no longer be a TStringList. That's why 'var' > must match exactly. > Same for out. > Const and no modifier allows to pass descendants. > yes, true, then if we follow that consequently: procedure Foo(out s: TStrings); begin end; var x: TObject; foo(x); should work? (or well only if it is made sure, that out really means out, so the procedure is not allowed to use the value that was passed in) "s" can be assigned a TStrings (or descendant of) => which should be fine, since it can be stored in x too.

Theoretically: yes, this could work for classes.
But as Jonas explained a few times: 'out' is *not* write only. Out is finalised and must therefore match exactly. And even write-only is not enough for "object"s because an ancestor has too less memory.

Mattias

Reply by email

Martin
to Mattias Gaertner
Mattias Gaertner wrote 45 lines
On Mon, 17 May 2010 10:05:27 +0100 Martin wrote: > On 14/05/2010 15:52, Mattias Gaertner wrote: > >> "var" means, you can do this: >> >> type >> TQStrings = class(TStrings) >> end; >> >> procedure A(var s: TStrings); >> begin >> s:=TQStrings.Create; >> end; >> >> Then your TStringList will no longer be a TStringList. That's why 'var' >> must match exactly. >> Same for out. >> Const and no modifier allows to pass descendants. >> >> > yes, true, then if we follow that consequently: > > > procedure Foo(out s: TStrings); > begin > end; > > var x: TObject; > foo(x); > > should work? (or well only if it is made sure, that out really means > out, so the procedure is not allowed to use the value that was passed in) > > "s" can be assigned a TStrings (or descendant of) => which should be > fine, since it can be stored in x too. > Theoretically: yes, this could work for classes. But as Jonas explained a few times: 'out' is *not* write only. Out is finalised and must therefore match exactly. And even write-only is not enough for "object"s because an ancestor has too less memory.

Hm, finalization isn't happening in the user code. So shouldn't be an issue... and:
- are class-instance finalized? (maybe with interfaces)
- it's an implementation detail of the compiler => it could (maybe does) happen, in the calling code (where type info is known)

memory is allocated on the heap
s and x are both just pointers => so the size matches Actually x is a pointer to s, and s a pointer to the memory. but s can be changed, so the memory can be re-allocted.

Martin

Reply by email

Graeme Geldenhuys
to Michael Van Canneyt: You only need var/out when you want to change the instance pointer, i.e. switch to…

Hi,

Sorry, I'm still battling with this. My original code looked as follows:

procedure TfpgApplicationBase.CreateForm(AFormClass: TComponentClass; out
AForm: TfpgWindowBase);
begin
  try
    AForm := TfpgWindowBase(AFormClass.Create(self));
    if FMainForm = nil then
      FMainForm := AForm;
  except
    AForm := nil;
    raise;
  end;
end;

I then used that method as follows:

var
  BulkDiscountListForm: TBulkInvoiceRateListForm;
function DisplayMyForm: boolean:
begin
  fpgApplication.CreateForm(TBulkInvoiceRateListForm,
     BulkDiscountListForm);
  try
    Result := BulkDiscountListForm.ShowModal = mrOK;
    if Result then
      // save data
  finally
    BulkDiscountListForm.Free;
  end;
  ...
end;

This doesn't work with FPC 2.5.1. I then removed the 'out' parameter type and my code compiled, but now it gives the following compiler warning all over the place for each form I create.

frm_bulkdiskcountlist.pas(55,3) Warning: Variable BulkDiscountListForm"
read but nowhere assigned

This is something the 'out' parameter type normally prevents. So now what? Looking at LCL's Application.CreateForm(), they do something totally different. They use a 'out' parameter, but make it untyped??? Why untyped (plain untyped pointer)? I don't want somebody passing a TStringList type as the AForm parameter, which the compiler will not detect with a untyped parameter. That is why I used TfpgWindowBase so I know only descendant types of TfpgWindowBase is allowed.

Looking at the Lazarus code for Application.CreateForm. Now they do other strange things too, creating a instance without calling the constructor etc... This doesn't seem like something I want either and still doesn't solve the problem where somebody could pass in any object type to CreateForm.

I'm out of ideas here... :-/

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Mattias Gaertner
to Martin: Hm, finalization isn't happening in the user code. So shouldn't be an issue... and: - are…
Show quoted text 19 lines
On 17/05/2010 10:29, Mattias Gaertner wrote: [...] > Theoretically: yes, this could work for classes. > But as Jonas explained a few times: 'out' is *not* write only. > Out is finalised and must therefore match exactly. > And even write-only is not enough for "object"s because an ancestor > has too less memory. > Hm, finalization isn't happening in the user code. So shouldn't be an issue... and: - are class-instance finalized? (maybe with interfaces) - it's an implementation detail of the compiler => it could (maybe does) happen, in the calling code (where type info is known) memory is allocated on the heap s and x are both just pointers => so the size matches Actually x is a pointer to s, and s a pointer to the memory. but s can be changed, so the memory can be re-allocted.

No, for example:

type
  TAncestor = object
    i: string;
  end;
  TMyObject = object(TAncestor)
    j: string;  // need more mem
  end;
procedure Doh(out o: TMyObject);
begin
  o.j:='3'; // writing beyond
end;
procedure A;
var
  o: TAncestor; // limited mem
begin
  Doh(o);
end;

Mattias

Reply by email

Michael Van Canneyt
to Graeme Geldenhuys: Sorry, I'm still battling with this. My original code looked as follows: procedure…
Graeme Geldenhuys wrote 41 lines
Hi, Sorry, I'm still battling with this. My original code looked as follows: procedure TfpgApplicationBase.CreateForm(AFormClass: TComponentClass; out AForm: TfpgWindowBase); begin try AForm := TfpgWindowBase(AFormClass.Create(self)); if FMainForm = nil then FMainForm := AForm; except AForm := nil; raise; end; end; I then used that method as follows: var BulkDiscountListForm: TBulkInvoiceRateListForm; function DisplayMyForm: boolean: begin fpgApplication.CreateForm(TBulkInvoiceRateListForm, BulkDiscountListForm); try Result := BulkDiscountListForm.ShowModal = mrOK; if Result then // save data finally BulkDiscountListForm.Free; end; ... end; This doesn't work with FPC 2.5.1. I then removed the 'out' parameter type and my code compiled, but now it gives the following compiler warning all over the place for each form I create.

And correctly so, because you need the OUT: you are modifying the pointer and need to pass back the modified pointer to the caller.

Michael.

Reply by email

Graeme Geldenhuys
to Michael Van Canneyt

Michael Van Canneyt het geskryf:

And correctly so, because you need the OUT: you are modifying the pointer and need to pass back the modified pointer to the caller.

Yes, but I can't use OUT because the type of AForm doesn't match exactly - back to my initial problem when I started with FPC 2.5.1. TfpgWindowBase vs TBulkInvoiceRateListForm type.

This compiler change will play havoc on the Simple Factory Method design pattern as well - a base class is used as the parameter type, but descendants are created and passed back. This is a very common case and used a lot.

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Martin
to Mattias Gaertner: On Mon, 17 May 2010 10:40:12 +0100 No, for example: type TAncestor = object i: string;…
Mattias Gaertner wrote 27 lines
On Mon, 17 May 2010 10:40:12 +0100 Martin wrote: > On 17/05/2010 10:29, Mattias Gaertner wrote: > [...] > >> Theoretically: yes, this could work for classes. >> But as Jonas explained a few times: 'out' is *not* write only. >> Out is finalised and must therefore match exactly. >> And even write-only is not enough for "object"s because an ancestor >> has too less memory. >> >> > Hm, finalization isn't happening in the user code. So shouldn't be an > issue... and: > - are class-instance finalized? (maybe with interfaces) > - it's an implementation detail of the compiler => it could (maybe does) > happen, in the calling code (where type info is known) > > memory is allocated on the heap > s and x are both just pointers => so the size matches > Actually x is a pointer to s, and s a pointer to the memory. but s can > be changed, so the memory can be re-allocted. > No, for example:

I quite clearly wrote "class-instance"

I am aware it doesn't work for object.

Show quoted text 27 lines
type TAncestor = object i: string; end; TMyObject = object(TAncestor) j: string; // need more mem end; procedure Doh(out o: TMyObject); begin o.j:='3'; // writing beyond end; procedure A; var o: TAncestor; // limited mem begin Doh(o); end; Mattias _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Florian Klaempfl
to Graeme Geldenhuys: Michael Van Canneyt het geskryf: Yes, but I can't use OUT because the type of AForm…
Show quoted text 12 lines
Michael Van Canneyt het geskryf: > And correctly so, because you need the OUT: you are modifying the pointer > and need to pass back the modified pointer to the caller. Yes, but I can't use OUT because the type of AForm doesn't match exactly - back to my initial problem when I started with FPC 2.5.1. TfpgWindowBase vs TBulkInvoiceRateListForm type. This compiler change will play havoc on the Simple Factory Method design pattern as well - a base class is used as the parameter type, but descendants are created and passed back. This is a very common case and used a lot.

... and one uses normally a function and not a var/out parameter.

Reply by email

Mattias Gaertner
to Graeme Geldenhuys: Sorry, I'm still battling with this. My original code looked as follows: procedure…
Show quoted text 5 lines
[...] Looking at the Lazarus code for Application.CreateForm. Now they do other strange things too, creating a instance without calling the constructor etc... This doesn't seem like something I want either and still doesn't solve the problem where somebody could pass in any object type to CreateForm.

VCL/LCL sets first the variable. This way code can already access the variable while the OnCreate event is processed.

Mattias

Reply by email

Vinzent Höfler
to Florian Klaempfl: ... and one uses normally a function and not a var/out parameter.

Florian Klaempfl :

Show quoted text 8 lines
Graeme Geldenhuys schrieb: > This compiler change will play havoc on the Simple Factory Method design > pattern as well - a base class is used as the parameter type, but > descendants are created and passed back. This is a very common case and > used a lot. ... and one uses normally a function and not a var/out parameter.

And return the error code in an out parameter instead? Great idea. So readable. So much in the spirit of Pascal.

Any more such suggestions on how to write code? Where can one download "Florian's Style Guide" - so one will not do that mistake again?

Sorry for the sarcasm, but that pattern worked in the past and there's absolutely no reason it shouldn't. Come on, guys, that's really schizophrenic: On one hand such changes break compatibility in a BIG way and on the other hand there's whining about adding a new keyword, because it could break compatibility in a couple of rare cases.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Florian Klaempfl
to Vinzent Höfler: Florian Klaempfl : And return the error code in an out parameter instead? Great idea. So…
Show quoted text 11 lines
Florian Klaempfl : > Graeme Geldenhuys schrieb: > >> This compiler change will play havoc on the Simple Factory Method design >> pattern as well - a base class is used as the parameter type, but >> descendants are created and passed back. This is a very common case and >> used a lot. > ... and one uses normally a function and not a var/out parameter. And return the error code in an out parameter instead? Great idea. So readable. So much in the spirit of Pascal.

Doing/allowing unsafe implicit type casts is neither in the spirit of pascal. When working with modern OOP design methods one uses also modern error handling mechanisms: exceptions.

Any more such suggestions on how to write code? Where can one download "Florian's Style Guide" - so one will not do that mistake again?

Maybe you should start with WP:
http://en.wikipedia.org/wiki/Factory_method_pattern

Sorry for the sarcasm,

Sarcasm? For sarcasm, you need to have a clue.

Reply by email

Martin
to Marcos Douglas: On Mon, May 17, 2010 at 4:24 PM, Florian Klaempfl No... If you forget to put a variable…
Marcos Douglas wrote 9 lines
On Mon, May 17, 2010 at 4:24 PM, Florian Klaempfl wrote: > ... and one uses normally a function and not a var/out parameter. > No... If you forget to put a variable to receive the function result (a object created), a memory leak will happen. The "out" parameter requires a variable.
I am sorry, am I missing the point? The idea is to have a factoruy method
procedure xxx(var SomeFoo: TFooType): Boolean;

so it will return in SomeFoo a newly created object?

As far as I can see that still works very well.

The type *declaration* of the var param and the variable passed in must match 100%; so what

var
   FooToBeCreated: TFooType;
begin
   if  xxx(FooToBeCreated) then ...;

that should to nice.
FooToBeCreated can be assigtne a value of TFooType, or any descendant of TFooType; which is exactly what a factory is there fore.

Martin

Reply by email

José Mejuto
to Graeme Geldenhuys: Michael Van Canneyt het geskryf: Yes, but I can't use OUT because the type of AForm…

Hello Graeme,

Monday, May 17, 2010, 11:56:29 AM, you wrote:

GG> Yes, but I can't use OUT because the type of AForm doesn't match exactly - GG> back to my initial problem when I started with FPC 2.5.1. GG> TfpgWindowBase vs TBulkInvoiceRateListForm type.

So you do not need "out" you need the non-exist "varout" which act as a var (non finalize) but do not emit the warning.

Best regards, José

Reply by email

Henry Vermaak
to Vinzent Höfler: Florian Klaempfl : And return the error code in an out parameter instead? Great idea. So…
"Vinzent Höfler" wrote 15 lines
Florian Klaempfl : > Graeme Geldenhuys schrieb: > > > This compiler change will play havoc on the Simple Factory Method design > > pattern as well - a base class is used as the parameter type, but > > descendants are created and passed back. This is a very common case and > > used a lot. > > ... and one uses normally a function and not a var/out parameter. And return the error code in an out parameter instead? Great idea. So readable. So much in the spirit of Pascal. Any more such suggestions on how to write code? Where can one download "Florian's Style Guide" - so one will not do that mistake again?

Perhaps you should establish a technical argument instead of wasting two paragraphs on childish sarcasm?

Henry

Reply by email

Vinzent Höfler
to Florian Klaempfl: Doing/allowing unsafe implicit type casts is neither in the spirit of pascal. When…

Florian Klaempfl :

Show quoted text 18 lines
Vinzent Höfler schrieb: > Florian Klaempfl : > >> Graeme Geldenhuys schrieb: >> >>> This compiler change will play havoc on the Simple Factory Method design >>> pattern as well - a base class is used as the parameter type, but >>> descendants are created and passed back. This is a very common case and >>> used a lot. >> ... and one uses normally a function and not a var/out parameter. > > And return the error code in an out parameter instead? Great idea. So readable. So much in the spirit of Pascal. Doing/allowing unsafe implicit type casts is neither in the spirit of pascal.

You wouldn't *need* typecasts. It's a pointer and the instance it is pointing to will be initialized by the subroutine you're calling. And that one *knows* the size of the class it created. So there's typecast involved at all, if used correctly.

And stop calling me clueless, please. Just because you do not understand what I write, doesn't exactly mean, I am the one who has no clue.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Vinzent Höfler
to Henry Vermaak: Perhaps you should establish a technical argument instead of wasting two paragraphs on…

Henry Vermaak

Show quoted text 19 lines
On 17 May 2010 21:59, "Vinzent Höfler" wrote: > Florian Klaempfl : > >> Graeme Geldenhuys schrieb: >> >> > This compiler change will play havoc on the Simple Factory Method design >> > pattern as well - a base class is used as the parameter type, but >> > descendants are created and passed back. This is a very common case and >> > used a lot. >> >> ... and one uses normally a function and not a var/out parameter. > > And return the error code in an out parameter instead? [...] Perhaps you should establish a technical argument instead of wasting two paragraphs on childish sarcasm?

The technical argument was in the first sentence, if you were so kind to have read it.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Graeme Geldenhuys
to Florian Klaempfl: ... and one uses normally a function and not a var/out parameter.

Florian Klaempfl het geskryf:

... and one uses normally a function and not a var/out parameter.

The design pattern doesn't specify that. The developer can implement it as they see if. A parameter or a function return type - I have see and used both methods.

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Graeme Geldenhuys
to Florian Klaempfl: Doing/allowing unsafe implicit type casts is neither in the spirit of pascal. When…

Florian Klaempfl het geskryf:

http://en.wikipedia.org/wiki/Factory_method_pattern > Sorry for the sarcasm, Sarcasm? For sarcasm, you need to have a clue.

No need to be rude.

Maybe I can chip in, because design patterns are right up my alley. Maybe you forgot, but a design pattern is not a code template! It normally describes *one* possible way of implementing that pattern - many other ways can exist. One very obvious example is the way tiOPF implements Visitor and Iterator. Definitely not like any of the text I have read or code I have seen, yet it's the same design patterns as described in the GOF's book. And yes, I have been studying and using design patterns for the last 9 years extensively.

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Florian Klaempfl
to Vinzent Höfler: Florian Klaempfl : You wouldn't *need* typecasts. It's a pointer and the instance it is…
Show quoted text 28 lines
Florian Klaempfl : > Vinzent Höfler schrieb: >> Florian Klaempfl : >> >>> Graeme Geldenhuys schrieb: >>> >>>> This compiler change will play havoc on the Simple Factory >>>> Method > design >>>> pattern as well - a base class is used as the parameter type, >>>> but descendants are created and passed back. This is a very >>>> common case > and >>>> used a lot. >>> ... and one uses normally a function and not a var/out >>> parameter. >> And return the error code in an out parameter instead? Great >> idea. So > readable. So much in the spirit of Pascal. > > Doing/allowing unsafe implicit type casts is neither in the spirit > of pascal. You wouldn't *need* typecasts. It's a pointer and the instance it is pointing to will be initialized by the subroutine you're calling. And that one *knows* the size of the class it created. So there's typecast involved at all, if used correctly.

You even didn't get the problem :) I'll try again but I fear it's a waste of time. Before the change, code like

{$mode objfpc}
type
  tc1 = class
  end;
  tc2 = class(tc1)
  end;
  tc3 = class(tc1)
  end;
procedure p(var c : tc1);
  begin
    c:=tc2.create;
  end;
var
  c3 : tc3;
begin
  p(c3);
end.

was allowed and this code is simply broken, the compiler has to complain and this was fixed. Code depending on this lazy behaviour simply needs to be fixed, there are several solutions like inserting a explicit type cast or using functions maybe together with the as operator.

And stop calling me clueless, please.

You started to get personally rude so unfortunatly I think you've to accept the same.

Just because you do not understand what I write, doesn't exactly mean, I am the one who has no clue.

Oh really? You want to allow the code above? You call this pascalish?

Reply by email

Florian Klaempfl
to Graeme Geldenhuys: Florian Klaempfl het geskryf: No need to be rude. Maybe I can chip in, because design…
Show quoted text 11 lines
Florian Klaempfl het geskryf: > http://en.wikipedia.org/wiki/Factory_method_pattern >> Sorry for the sarcasm, > Sarcasm? For sarcasm, you need to have a clue. No need to be rude. Maybe I can chip in, because design patterns are right up my alley. Maybe you forgot, but a design pattern is not a code template! It normally describes *one* possible way of implementing that pattern - many other ways can exist.

Nobody doubts this, this is also why I wrote "normally" in my initial post. Nevertheless I didn't see a single valid reason yet why implementing the factory method pattern cannot be done using a function:
- it is one of the basic programming principles: if a result is returned by a subroutine, use a function
- it makes things more intuitive to read because it looks like a constructor

If you really want to use a var-parameter, insert an explicit typecast then everybody knows: "this code is dangerous, we have to keep an eye on it".

Reply by email

Micha Nelissen
to Florian Klaempfl: ... and one uses normally a function and not a var/out parameter.
Florian Klaempfl wrote
... and one uses normally a function and not a var/out parameter.

That also would need a typecast since the function would return a parent class of the real class that was created, but the somehow the callee knows it's a descendent, so needs a cast to get to it (or use the "is" or "as" operators or ... etc).

This actually proves that a compiler error should also be required for the var/out parameter case as well.

Micha

Reply by email

Florian Klaempfl
to Micha Nelissen
Show quoted text 9 lines
Florian Klaempfl wrote: > > ... and one uses normally a function and not a var/out parameter. That also would need a typecast since the function would return a parent class of the real class that was created, but the somehow the callee knows it's a descendent, so needs a cast to get to it (or use the "is" or "as" operators or ... etc).

Yes. If needed the proper solution is to use the as-operator.

Reply by email

Graeme Geldenhuys
to Florian Klaempfl: Nobody doubts this, this is also why I wrote "normally" in my initial post. Nevertheless…
Florian Klaempfl wrote
- it is one of the basic programming principles: if a result is returned by a subroutine, use a function

The function might return a boolean (success/fail), or an error code etc.. The function result doesn't need to return the new instance created by the factory method.

Anyway, I knocked up a very basic Factory Method example, and I fear (it pains me dearly <wink>) to say that you are correct in this example. :) Normal usage of a Factory Method will pass in a base class, which will actually match the signature of the Factory Method.

But getting back to may original issue about
TfpgApplication.CreateForm(), this works very different to a Factor Method and the usage is not deemed "unsafe".

  fpgApplication.CreateForm(TMyForm, MyForm);

But I guess the only solution (forced upon me now) is to use some magic code like LCL does. An untyped variable (YUCK!!! They are as bad as Variants!) and then type cast it inside the implementation of CreateForm and use the 'is' operator to find out if the instance really is a TfpgWindowBase descendant. A whole much of crap code that wasn't needed with FPC 2.4.1. Oh well. :-/

Anyway, to show that I can admit I was wrong.... Here is an example to show that Factory Method can be implemented without problems using latest FPC 2.5.1
----------------------------
type
TBaseReport = class(TObject)

   public
     procedure Run; virtual; abstract;
   end;
   TReportA = class(TBaseReport)
   public
     procedure Run; override;
   end;
   TReportB = class(TBaseReport)
   public
     procedure Run; override;
   end;
   TReportClass = class of TBaseReport;
   TFactory = class(TObject)
   public
     procedure RegisterReport(const AName: string; AClass: TReportClass);
     function  CreateReport(const AName: string; out ARpt:
TBaseReport): Boolean;
   end;
  // singleton of TFactory
 function gFactory: TFactory;

... and then when used...

var
  rpt: TBaseReport;
  ok: Boolean;
begin
  ok := gFactory.CreateReport('a', rpt);
  if ok then
  begin
    rpt.Run;
    rpt.Free;
  end;
end;
Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/

Reply by email

Florian Klaempfl
to Graeme Geldenhuys
Show quoted text 7 lines
On 18 May 2010 10:12, Florian Klaempfl wrote: > - it is one of the basic programming principles: if a result is returned > by a subroutine, use a function The function might return a boolean (success/fail), or an error code etc.. The function result doesn't need to return the new instance created by the factory method.

I see, we should change the constructor syntax for you as well ;)

Show quoted text 7 lines
But I guess the only solution (forced upon me now) is to use some magic code like LCL does. An untyped variable (YUCK!!! They are as bad as Variants!) and then type cast it inside the implementation of CreateForm and use the 'is' operator to find out if the instance really is a TfpgWindowBase descendant. A whole much of crap code that wasn't needed with FPC 2.4.1. Oh well. :-/

Yes, but imo it doesn't get worse.
As far as I understand CreateForm, before
fpgApplication.CreateForm(TMyForm, NotAMyForm);
was possible as well, so you need to check the parameters anyways.

Maybe (and really only maybe) the proper solution in this case are C++ styled template methods.

Reply by email

Graeme Geldenhuys
to Florian Klaempfl

Florian Klaempfl het geskryf:

I see, we should change the constructor syntax for you as well ;)

I normally (90% of the time) use function return value as the instance of a Factory Method. But as stated, there are other alternatives which might be used to make code easier to read if errors occur (instead of using try..except blocks).

Yes, but imo it doesn't get worse.

Luckily that was the only incompatible code I had with FPC 2.5.1. I'll make the change using an untyped parameter - but that will hopefully be the first and last untyped parameter I ever use. :)

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Vinzent Höfler
to Florian Klaempfl: You even didn't get the problem :) I'll try again but I fear it's a waste of time. Before…

Florian Klaempfl :

Show quoted text 10 lines
Vinzent Höfler schrieb: > Florian Klaempfl : > > > You wouldn't *need* typecasts. It's a pointer and the instance it is > pointing to will be initialized by the subroutine you're calling. And > that one *knows* the size of the class it created. So there's > typecast involved at all, if used correctly. You even didn't get the problem :)

Maybe I didn't. But you were talking about factory pattern, so obviously you didn't get it neither. :P Enough of that personal nonsense.

Your example cites "var" parameters. Those, of course should match. But I was (and I believe Graeme, too) talking about "out" parameters for classes in a certain context. Maybe I'm wrong.

I'll try again but I fear it's a waste of time.

Well, we do a lot of things that are a waste of time, I suppose. Explaining somebody else why something wouldn't work isn't one of them unless done for the third time, at least. ;)

Show quoted text 14 lines
Before the change, code like {$mode objfpc} type tc1 = class end; tc2 = class(tc1) end; tc3 = class(tc1) end; procedure p(var c : tc1);
procedure p (out c : tc1);
Show quoted text 13 lines
begin c:=tc2.create; end; var c3 : tc3; begin p(c3); end. was allowed and this code is simply broken, the compiler has to complain and this was fixed.

Agreed. But in that case a function result of type tc1 wouldn't be allowed to be assigned to tc3 either. So your previous statement ("use a function instead") didn't make too much sense in this context, did it?

Code depending on this lazy behaviour simply needs to be fixed, there are several solutions like inserting a explicit type cast or using functions maybe together with the as operator.

But that wasn't the problem. The problem I mean is that:

procedure p (out c : tc2)
begin
   c := tc3.create;
end;
var
  c1 : tc1;
begin
  p(c1);
end.

seems not to be allowed anymore. And there's no reason for it. tc2 is a descendant of tc1, so just like assigning any descendant of tc1 to a tc1 variable, such a variable should also be allowed as an out parameter. Your example above tries to do it the other way around which is indeed flawed - in any case.

BTW, I still can not follow the argument of that "out" parameters need finalization. Sure, for reference counted types this may be true, I won't argue that, but "out" used to work for integers as well as other types needing no finalization, so the compiler should not insert finalization code when classes are involved (despite of the history of "out" coming from reference counted COM objects or whatever). The compiler should be able to statically determine the type of parameter involved and insert or omit finalization code as necessary for the /type/, not for a /parameter/ /mode/.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Vinzent Höfler
to Florian Klaempfl: Nobody doubts this, this is also why I wrote "normally" in my initial post. Nevertheless…

Florian Klaempfl :

Show quoted text 11 lines
Nobody doubts this, this is also why I wrote "normally" in my initial post. Nevertheless I didn't see a single valid reason yet why implementing the factory method pattern cannot be done using a function: - it is one of the basic programming principles: if a result is returned by a subroutine, use a function - it makes things more intuitive to read because it looks like a constructor If you really want to use a var-parameter, insert an explicit typecast then everybody knows: "this code is dangerous, we have to keep an eye on it".

With the example you gave (see my answer), the typecast would be necessary as well when using a function. And it would also be wrong in precisely the same way.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Aleksa Todorovic
to Florian Klaempfl: I see, we should change the constructor syntax for you as well ;) Yes, but imo it doesn't…
Florian Klaempfl wrote
Maybe (and really only maybe) the proper solution in this case are C++ styled template methods.

Different languages deal with this problem in different ways:
- C++ (Java) have powerful enough template (generics) system which can handle this problem in most cases (personally, I like Java generics - they are pretty simple and not confusing like C++ templates; otoh, C++ templates allow you to blow up your own leg, as well as lot of other things)
- in Eiffel, this is know problem of catcalls - here's some food for thought on this matter:
http://dev.eiffel.com/Comparison_of_catcall_solutions

I hope we'll see generics in FPC handle these situations in ... future :-)

Reply by email

Graeme Geldenhuys
to Vinzent Höfler: Florian Klaempfl : Maybe I didn't. But you were talking about factory pattern, so…

Vinzent Höfler het geskryf:

types needing no finalization, so the compiler should not insert finalization code when classes are involved (despite of the history of "out" coming from reference counted COM objects or whatever).

I don't understand this finalization thing about out parameters either, but I agree with the above statement. Borland did exactly this with IUnknown, IInterface, TInterfacedObject - initially it just implemented because of COM, them COM dependency was removed so it could work on Delphi and Kylix (independent of COM).

Probably the same should then apply to the out parameter instead of introducing something new like the 'varout' that somebody else mentioned.

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Florian Klaempfl
to Vinzent Höfler: Florian Klaempfl : With the example you gave (see my answer), the typecast would be…
Show quoted text 15 lines
Florian Klaempfl : > Nobody doubts this, this is also why I wrote "normally" in my > initial post. Nevertheless I didn't see a single valid reason yet > why implementing the factory method pattern cannot be done using a > function: - it is one of the basic programming principles: if a > result is returned by a subroutine, use a function - it makes > things more intuitive to read because it looks like a constructor > > If you really want to use a var-parameter, insert an explicit > typecast then everybody knows: "this code is dangerous, we have to > keep an eye on it". With the example you gave (see my answer), the typecast would be necessary as well when using a function.

With a function result one can easily use the as-operator. I thought such simple things are clear without additional notes.

Reply by email

Florian Klaempfl
to Vinzent Höfler: Florian Klaempfl : Maybe I didn't. But you were talking about factory pattern, so…
Show quoted text 8 lines
> > was allowed and this code is simply broken, the compiler has to > complain and this was fixed. Agreed. But in that case a function result of type tc1 wouldn't be allowed to be assigned to tc3 either. So your previous statement ("use a function instead") didn't make too much sense in this context, did it?

It is pretty obvious for me that one needs an as-operator in this case? Using the as-operator with var parameters is however not possible without a temp. variable.

Show quoted text 11 lines
> Code depending on this lazy behaviour simply needs to be fixed, > there are several solutions like inserting a explicit type cast or > using functions maybe together with the as operator. But that wasn't the problem. The problem I mean is that: procedure p (out c : tc2) begin c := tc3.create;

This breaks already here if I assume the type definitions I used.

Show quoted text 11 lines
end; var c1 : tc1; begin p(c1); end. seems not to be allowed anymore.

Please test your examples first before doing some wild guesses. It is useless to discuss about completly broken code.

Show quoted text 6 lines
BTW, I still can not follow the argument of that "out" parameters need finalization. Sure, for reference counted types this may be true, I won't argue that, but "out" used to work for integers as well as other types needing no finalization, so the compiler should not insert finalization code when classes are involved

Who said that this is done?

Reply by email

Jonas Maebe
to Vinzent Höfler: Florian Klaempfl : Maybe I didn't. But you were talking about factory pattern, so…
Vinzent Höfler wrote 15 lines
But that wasn't the problem. The problem I mean is that: procedure p (out c : tc2) begin c := tc3.create; end; var c1 : tc1; begin p(c1); end. seems not to be allowed anymore.

I guess you mean tc2.create instead of tc3.create. You're correct that this is no longer accepted by FPC (nor is it accepted by Kylix). And if you don't abuse "out" parameters as "var" parameters (although I'm quite sure there's plenty of code out there that does this -- we've also had issues with that in the compiler source itself), I also do not immediately see how the above could be dangerous. Then again, for some 10 or more years nobody thought of how the old behaviour allowed subverting the complete type checking model of the compiler either (which is why "useless restriction from Delphi" probably wasn't implemented initially).

BTW, I still can not follow the argument of that "out" parameters need finalization.

It does not apply in the case of classes. Finalisation is only inserted, at the caller side (hence, where the exact type is known), in case of reference counted types.

Jonas

Reply by email

Florian Klaempfl
to Jonas Maebe
It does not apply in the case of classes. Finalisation is only inserted, at the caller side (hence, where the exact type is known), in case of reference counted types.

Actually automated types as well (variants).

Reply by email

Marcos Douglas
to Florian Klaempfl: With a function result one can easily use the as-operator. I thought such simple things…
Florian Klaempfl wrote 8 lines
Nobody doubts this, this is also why I wrote "normally" in my initial post. Nevertheless I didn't see a single valid reason yet why implementing the factory method pattern cannot be done using a function: - it is one of the basic programming principles: if a result is returned by a subroutine, use a function - it makes things more intuitive to read because it looks like a constructor
Florian Klaempfl wrote
With a function result one can easily use the as-operator. I thought such simple things are clear without additional notes.

Now, I have a question:
If a function return an object, like Result := TStringList.Create, and I use like that:

var
  sl: TStringList;
begin
  sl := myfunc;
  try
    // some code
  finally
     sl.Free;
  end;
end;

that is OK... but if a call without a variable for return, what will happen with the object created inside the function? A memory leak?

Thanks,
Marcos Douglas

Reply by email

Flávio Etrusco
to Graeme Geldenhuys: Florian Klaempfl het geskryf: No need to be rude. Maybe I can chip in, because design…
Graeme Geldenhuys wrote 8 lines
Florian Klaempfl het geskryf: > http://en.wikipedia.org/wiki/Factory_method_pattern >> Sorry for the sarcasm, > > Sarcasm? For sarcasm, you need to have a clue. No need to be rude.

At first I thought it was rude too. But thinking again, sarcasm is nothing more than a worse-than-cynical form of rudeness...

-Flávio

Reply by email

Vinzent Höfler
to Florian Klaempfl: With a function result one can easily use the as-operator. I thought such simple things…

Florian Klaempfl :

Vinzent Höfler schrieb: > Florian Klaempfl : With a function result one can easily use the as-operator.

Yes, and then instead of doing the "undefined behaviour" thingie, it would cause an RTE instead. Not much of an improvement, I'd say.

I thought such simple things are clear without additional notes.

And I thought, that there's no point in giving compilable alternatives when they are still wrong. Probably that's where the confusion came from.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Vinzent Höfler
to Florian Klaempfl: It is pretty obvious for me that one needs an as-operator in this case? Using the…

Florian Klaempfl :

Show quoted text 11 lines
Vinzent Höfler schrieb: >> >> was allowed and this code is simply broken, the compiler has to >> complain and this was fixed. > > Agreed. But in that case a function result of type tc1 wouldn't be > allowed to be assigned to tc3 either. So your previous statement > ("use a function instead") didn't make too much sense in this > context, did it? It is pretty obvious for me that one needs an as-operator in this case?

And it is pretty obvious to me that the typecast would change the wrongness of the code.

Show quoted text 12 lines
>> Code depending on this lazy behaviour simply needs to be fixed, >> there are several solutions like inserting a explicit type cast or >> using functions maybe together with the as operator. > > > But that wasn't the problem. The problem I mean is that: > > procedure p (out c : tc2) > begin > c := tc3.create; This breaks already here if I assume the type definitions I used.

I assumed a tc1 -> tc2 -> tc3 inheritance, sorry if I missed something here. So creating an instance of tc3 and assigning it to a variable of tc2 should be legal.

Show quoted text 12 lines
> end; > > var > c1 : tc1; > > begin > p(c1); > end. > > seems not to be allowed anymore. Please test your examples first before doing some wild guesses.

I think it was Jonas who said it wouldn't be possible anymore. So it's far from being a wild guess, although I must admit that I haven't tried it for myself. Actually I never used FPC bleeding edge, so unless 2.6 comes out, I won't even notice the problem.

Show quoted text 7 lines
> BTW, I still can not follow the argument of that "out" parameters > need finalization. Sure, for reference counted types this may be > true, I won't argue that, but "out" used to work for integers as well > as other types needing no finalization, so the compiler should not > insert finalization code when classes are involved Who said that this is done?

Well, Mattias Gärtner wrote:

"But as Jonas explained a few times: 'out' is *not* write only. Out is finalised and must therefore match exactly."

I'm reading my mail on-line, so I apologize in advance excuse me if it's hard to me to find the correct mail with the precise wording. Maybe, Mattias can.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Jonas Maebe
to Vinzent Höfler: Florian Klaempfl : Yes, and then instead of doing the "undefined behaviour" thingie, it…
Vinzent Höfler wrote 8 lines
Florian Klaempfl : > Vinzent Höfler schrieb: >> Florian Klaempfl : > > With a function result one can easily use the as-operator. Yes, and then instead of doing the "undefined behaviour" thingie, it would cause an RTE instead. Not much of an improvement, I'd say.

Exceptions can be caught, unlike violations of the typing system that are silently ignored by the compiler.

Jonas

Reply by email

Florian Klaempfl
to Vinzent Höfler: Florian Klaempfl : Yes, and then instead of doing the "undefined behaviour" thingie, it…
Show quoted text 8 lines
Florian Klaempfl : > Vinzent Höfler schrieb: >> Florian Klaempfl : > With a function result one can easily use the as-operator. Yes, and then instead of doing the "undefined behaviour" thingie, it would cause an RTE instead. Not much of an improvement, I'd say.

It's the important difference between C and Pascal: "undefined behaviour" against proper error message.

Show quoted text 6 lines
> I thought such simple things are clear without additional notes. And I thought, that there's no point in giving compilable alternatives when they are still wrong. Probably that's where the confusion came from.

At least we try to avoid it to make it people too easy to shoot themself into the foot.

Reply by email

Vinzent Höfler
to Jonas Maebe: I guess you mean tc2.create instead of tc3.create. You're correct that this is no longer…

Jonas Maebe :

Show quoted text 19 lines
On 18 May 2010, at 15:25, Vinzent Höfler wrote: > But that wasn't the problem. The problem I mean is that: > > procedure p (out c : tc2) > begin > c := tc3.create; > end; > > var > c1 : tc1; > > begin > p(c1); > end. > > seems not to be allowed anymore. I guess you mean tc2.create instead of tc3.create.

No, tc3, where tc3 is an descendant of tc2.

Show quoted text 6 lines
You're correct that this is no longer accepted by FPC (nor is it accepted by Kylix). And if you don't abuse "out" parameters as "var" parameters (although I'm quite sure there's plenty of code out there that does this -- we've also had issues with that in the compiler source itself), I also do not immediately see how the above could be dangerous.

Ok, at least we're on the same page here, then.

Show quoted text 5 lines
Then again, for some 10 or more years nobody thought of how the old behaviour allowed subverting the complete type checking model of the compiler either (which is why "useless restriction from Delphi" probably wasn't implemented initially).

Well, wouldn't it be more precise, instead of disallowing "non-matching" out-parameters, issue a warning if the "out" parameter is read before written the first time instead? The compiler should issue a "possibly uninitialized" warning, anyway. ;)

(I'll get slapped again, but in Ada83 "out" parameters were write-only, even inside the procedure, but this restriction was dropped in Ada95, because it felt to awkward. No need to make the same mistake again.)

As several people (including me) pointed out, using "out" parameters for class hierarchies does have its usages.

> BTW, I still can not follow the argument of that "out" parameters > need finalization. It does not apply in the case of classes.

Ok, I would have thought so, too, but the impression was made that it applies here, also. Thanks for clarifying that.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Vinzent Höfler
to Jonas Maebe: Exceptions can be caught, unlike violations of the typing system that are silently…

Jonas Maebe :

Show quoted text 14 lines
On 18 May 2010, at 20:06, Vinzent Höfler wrote: > Florian Klaempfl : > >> Vinzent Höfler schrieb: >>> Florian Klaempfl : >> >> With a function result one can easily use the as-operator. > > Yes, and then instead of doing the "undefined behaviour" thingie, it would cause an RTE instead. Not much of an improvement, I'd say. Exceptions can be caught, unlike violations of the typing system that are silently ignored by the compiler.

Yes, sure, it's a more predictable way of failing. It still wouldn't solve the problem.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Florian Klaempfl
to Vinzent Höfler: Florian Klaempfl : And it is pretty obvious to me that the typecast would change the…
Show quoted text 8 lines
>> But that wasn't the problem. The problem I mean is that: >> >> procedure p (out c : tc2) >> begin >> c := tc3.create; > This breaks already here if I assume the type definitions I used. I assumed a tc1 -> tc2 -> tc3 inheritance, sorry if I missed something here.

Yes, the type declarations. I used tc1 -> tc2 and tc1 -> tc3.

Show quoted text 15 lines
So creating an instance of tc3 and assigning it to a variable of tc2 should be legal. >> end; >> >> var >> c1 : tc1; >> >> begin >> p(c1); >> end. >> >> seems not to be allowed anymore. > Please test your examples first before doing some wild guesses. I think it was Jonas who said it wouldn't be possible anymore.

This was *never* possible as far as I remember, so nothing changed here and no reason to complain that existing code gets havoc as Graeme feared. It is a different discussion if it should be allowed.

Reply by email

Graeme Geldenhuys
to Florian Klaempfl: It's the important difference between C and Pascal: "undefined behaviour" against proper…

Florian Klaempfl het geskryf:

At least we try to avoid it to make it people too easy to shoot themself into the foot.

Developers should be free to shoot themselves wherever they want! It should be their choice, not yours. :-)

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Adem
to Graeme Geldenhuys
Graeme Geldenhuys wrote 8 lines
Florian Klaempfl het geskryf: > At least we try to avoid it to make it people too easy to shoot themself > into the foot. > Developers should be free to shoot themselves wherever they want! It should be their choice, not yours. :-)

Graeme,

I think you're grossly exaggerating the limits of your freedoms by overlooking the fact that there are special/edge cases where your spouse (or significant other) may fiercely object to.

;)

Reply by email

Marco van de Voort
to Graeme Geldenhuys: Florian Klaempfl het geskryf: Developers should be free to shoot themselves wherever they…

In our previous episode, Graeme Geldenhuys said:

Show quoted text 6 lines
> At least we try to avoid it to make it people too easy to shoot themself > into the foot. Developers should be free to shoot themselves wherever they want! It should be their choice, not yours. :-)

You can shoot yourself in the foot any time by using the following syntax

pchar(nil)^:=#0;

don't worry :-)

Reply by email

Jonas Maebe
to Vinzent Höfler: Jonas Maebe : No, tc3, where tc3 is an descendant of tc2. Ok, at least we're on the same…
Vinzent Höfler wrote 12 lines
Jonas Maebe : > Then again, for > some 10 or more years nobody thought of how the old behaviour allowed > subverting the complete type checking model of the compiler either > (which is why "useless restriction from Delphi" probably wasn't > implemented initially). Well, wouldn't it be more precise, instead of disallowing "non- matching" out-parameters, issue a warning if the "out" parameter is read before written the first time instead? The compiler should issue a "possibly uninitialized" warning, anyway. ;)

It already does.

Maybe someone should ask a question on the Embarcadero forums asking why the *safe variant* is forbidden (not why it is forbidden in general), in case that's still true for current Delphi versions. It would be useful to have their feedback before we set ourselves up for enabling and later on disabling this again.

Jonas

Reply by email

Flávio Etrusco
to Florian Klaempfl
Florian Klaempfl wrote 10 lines
Graeme Geldenhuys schrieb: > Florian Klaempfl het geskryf: >> At least we try to avoid it to make it people too easy to shoot themself >> into the foot. > > > Developers should be free to shoot themselves wherever they want! It should > be their choice, not yours.  :-) Then you should switch to K&R-C, it makes this much easier.

Sometimes I just don't when Graeme is kidding. I hope no one ever disagrees Pascal is about helping developers not shooting themselves - and others ;) - in the foot...

-Flávio

Reply by email

Graeme Geldenhuys
to Flávio Etrusco

2010/5/19 Flávio Etrusco :

Sometimes I just don't when Graeme is kidding.

The rules are simple. If there is some form of "smiley face", then I am just kidding.

[..but clearly some developers here in the FPC & Lazarus mailing list have some or other "smiley face filter" in their email clients, and they never see what intent I had. Such a pity sometimes...]

disagrees Pascal is about helping developers not shooting themselves - and others ;) - in the foot...

Disclaimer:
I definitely do not condone developers shooting themselves or somebody else for that matter.

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/

Reply by email

Vinzent Höfler
to Florian Klaempfl: Yes, the type declarations. I used tc1 -> tc2 and tc1 -> tc3. This was *never* possible…

Florian Klaempfl :

This was *never* possible as far as I remember, so nothing changed here and no reason to complain that existing code gets havoc as Graeme feared.

If my memory doesn't play nasty tricks on me, I was using this pattern in the past, so I'd say it was possible somewhere between 1.9.6 - 2.0.4 versions.

I don't work for the company anymore, so no chance to look at the code.

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Vinzent Höfler
to Graeme Geldenhuys: Florian Klaempfl het geskryf: Developers should be free to shoot themselves wherever they…

Graeme Geldenhuys :

Show quoted text 7 lines
Florian Klaempfl het geskryf: > > At least we try to avoid it to make it people too easy to shoot > themself into the foot. Developers should be free to shoot themselves wherever they want! It should be their choice, not yours. :-)

In that respect I'm 100% with Florian, although our opinions may differ in the specific details. ;) Yes, shooting yourself in the foot should be possible. But should it be easy, too? Definitely no!

<http://www-users.cs.york.ac.uk/susan/joke/foot.htm>:

-- 8< --
Pascal
The compiler won't let you shoot yourself in the foot. -- 8< --

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Flávio Etrusco
to Vinzent Höfler
"Vinzent Höfler" wrote 19 lines
Graeme Geldenhuys : > Florian Klaempfl het geskryf: > > > > At least we try to avoid it to make it people too easy to shoot > > themself into the foot. > > Developers should be free to shoot themselves wherever they want! It > should be their choice, not yours.  :-) In that respect I'm 100% with Florian, although our opinions may differ in the specific details. ;) Yes, shooting yourself in the foot should be possible. But should it be easy, too? Definitely no! <http://www-users.cs.york.ac.uk/susan/joke/foot.htm>: -- 8< -- Pascal   The compiler won't let you shoot yourself in the foot. -- 8< --

If you only used ":-P" instead, Graeme... ;-)

Best regards,
Flávio

Reply by email

Alexander Klenin
to Florian Klaempfl: Yes, the type declarations. I used tc1 -> tc2 and tc1 -> tc3. This was *never* possible…

I would like to return the discussion to the original question, now with the real code sample ;-)
Look at r21318 of Lazarus (tachart: fixed compilation with fpc 2.5.1, by Vincent).

[BTW, is there any web-interface for browsing Lazarus SVN?]

I think it is a typical sample of change required to production code by the restriction we are discussing.
Looking at the diff, I'd argue that:
1) Old version was cleaner.
2) New version is _not_ any safer, since any type error in the old code will result in exactly the same consequences with the new one. =>
So, the additional inconvenience to developer did not pay off.

To really address the problem, I think compiler should insert a run-time type check (e.g. the as operator) automatically.
This way, original code will continue to work unchanged _and_ the programmer will get a clear exception in the case of error.

Alexander S. Klenin

Reply by email

Graeme Geldenhuys
to Alexander Klenin

Alexander Klenin het geskryf:

Look at r21318 of Lazarus (tachart: fixed compilation with fpc 2.5.1, by Vincent). I think it is a typical sample of change required to production code

Good example, and a terrible code change. That code change has no benefits [compared to the old code] as far as I can see.

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Vinzent Höfler
to Graeme Geldenhuys

Graeme Geldenhuys :

Show quoted text 5 lines
Vinzent Höfler het geskryf: > > <http://www-users.cs.york.ac.uk/susan/joke/foot.htm>: Those are brilliant. I love the Java one. :)

Thinking about it, with all those typecasts involved, this one fits much better to the current discussion:

-- 8< .--
Object Oriented Pascal
You perform a shooting on what might currently be a foot with what might currently be a bullet fired from what might currently be a gun. -- 8< --

:)

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Florian Klaempfl
to Alexander Klenin: I would like to return the discussion to the original question, now with the real code…
Show quoted text 15 lines
I would like to return the discussion to the original question, now with the real code sample ;-) Look at r21318 of Lazarus (tachart: fixed compilation with fpc 2.5.1, by Vincent). [BTW, is there any web-interface for browsing Lazarus SVN?] I think it is a typical sample of change required to production code by the restriction we are discussing. Looking at the diff, I'd argue that: 1) Old version was cleaner. 2) New version is _not_ any safer, since any type error in the old code will result in exactly the same consequences with the new one. => So, the additional inconvenience to developer did not pay off.

It does. Everybody knows now that there is happening something strange because type casts are used.

To really address the problem, I think compiler should insert a run-time type check (e.g. the as operator) automatically.

When? Before and/or after the call?

Reply by email

Joost van der Sluis
to Alexander Klenin: I would like to return the discussion to the original question, now with the real code…
Alexander Klenin wrote 6 lines
I would like to return the discussion to the original question, now with the real code sample ;-) Look at r21318 of Lazarus (tachart: fixed compilation with fpc 2.5.1, by Vincent). [BTW, is there any web-interface for browsing Lazarus SVN?]

Can't live without?

http://svn.freepascal.org/cgi-bin/viewvc.cgi/?root=lazarus

Joost.

Reply by email

Alexander Klenin
to Joost van der Sluis
Joost van der Sluis wrote 11 lines
On Thu, 2010-05-20 at 16:57 +1100, Alexander Klenin wrote: > I would like to return the discussion to the original question, > now with the real code sample ;-) > Look at r21318 of Lazarus (tachart: fixed compilation with fpc 2.5.1, > by Vincent). > > [BTW, is there any web-interface for browsing Lazarus SVN?] Can't live without? http://svn.freepascal.org/cgi-bin/viewvc.cgi/?root=lazarus

I do not usually need it, but if I post a link like http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/components/tachart/tatypes.pas?root=lazarus&r1=21318&r2=21317&pathrev=21318 it may be more convenient to readers than just mentioning r21318

Alexander S. Klenin

Reply by email

Alexander Klenin
to Florian Klaempfl: It does. Everybody knows now that there is happening something strange because type casts…
Florian Klaempfl wrote 13 lines
Alexander Klenin schrieb: > I think it is a typical sample of change required to production code > by the restriction we are discussing. > Looking at the diff, I'd argue that: > 1) Old version was cleaner. > 2) New version is _not_ any safer, since any type error in the old code >   will result in exactly the same consequences with the new one. > => > So, the additional inconvenience to developer did not pay off. It does. Everybody knows now that there is happening something strange because type casts are used.

I am not so sure -- the chance to catch a type error at compile-time is just the same as before (that is, absent).
And the need to cast an object to its base class is rather confusing, so unprepared developer will probably just write it off as a compiler quirk. This might be alleviated by adding a comment, but:
1) Such a comment will be longer than the code it explains
2) If a compiler feature requires a comment every time it is used, something is not well ;-)

> To really address the problem, I think compiler should insert a run-time type > check (e.g. the as operator) automatically. When? Before and/or after the call?

After, of course. There is nothing to check before. To be more precise, the old code was:

InitHelper(FFont, TFont);

it is now converted to

InitHelper(TFPCanvasHelper(FFont), TFont);

the safe version would be

var tmp: TFPCanvasHelper;
...
InitHelper(tmp, TFont);
tmp := FFont as TFont;

I suggest that compiler should generate the code equivalent to the third version based on the source from the first. This would be backwards compatible, and a strict improvement over 2.4.0 behavior.

Alexander S. Klenin

Reply by email

Jonas Maebe
to Alexander Klenin
Alexander Klenin wrote 6 lines
the safe version would be var tmp: TFPCanvasHelper; ... InitHelper(tmp, TFont); tmp := FFont as TFont;
I guess you mean "FFont := tmp as TFont" here.
I suggest that compiler should generate the code equivalent to the third version based on the source from the first. This would be backwards compatible, and a strict improvement over 2.4.0 behavior.

It would not be backwards compatible, because if InitHelper or a function it calls also uses FFont, it will still see the old value instead of the value assigned to the "out" parameter. The code would have to be translated into something like this:

InitHelper(TFPCanvasHelper(FFont), TFont);
FFont := TFPCanvasHelper(FFont) as TFont;

However, having the compiler implicitly start inserting transformations like that behind the back of the programmer is not a nice principle either (except possibly if something like -CR is used).

Jonas

Reply by email

Graeme Geldenhuys
to Alexander Klenin: I do not usually need it, but if I post a link like…

Alexander Klenin het geskryf:

I do not usually need it, but if I post a link like http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/components/tachart/tatypes.pas?root=lazarus&r1=21318&r2=21317&pathrev=21318 it may be more convenient to readers than just mentioning r21318

True, and that looooong URL could be replaced with:

http://tinyurl.com/296wloa

...thanks to TinyURL. :) By the way, you if you use Firefox (maybe others too), you can add a TinyURL button to the Firefox Toolbar which will automatically generate such a small URL based on the current website you have open.

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Alexander Klenin
to Jonas Maebe: I guess you mean "FFont := tmp as TFont" here. It would not be backwards compatible,…
Jonas Maebe wrote 11 lines
On 20 May 2010, at 13:57, Alexander Klenin wrote: > the safe version would be > > var tmp: TFPCanvasHelper; > ... > InitHelper(tmp, TFont); > tmp := FFont as TFont; I guess you mean "FFont := tmp as TFont" here.

Yes, sorry.

Show quoted text 7 lines
It would not be backwards compatible, because if InitHelper or a function it calls also uses FFont, it will still see the old value instead of the value assigned to the "out" parameter. The code would have to be translated into something like this: InitHelper(TFPCanvasHelper(FFont), TFont); FFont := TFPCanvasHelper(FFont) as TFont;

That would be good too.

However, having the compiler implicitly start inserting transformations like that behind the back of the programmer is not a nice principle either (except possibly if something like -CR is used).

I see no problem if this check is controlled by a compiler flag, similar to R+, O+ etc, "on" by default.
Thinking about it, I like the idea very much -- it gives a way out to those who are concerned with extreme speed, and those whose code will be broken (although breakage with suggested implementation will in 99% of cases mean a real bug, as contrasted to the current implementation, when in 99% of cases the correctly working code is broken).

Alexander S. Klenin

Reply by email