Recent

Author Topic: Bug or feature?  (Read 5065 times)

taazz

  • Hero Member
  • *****
  • Posts: 5363
Bug or feature?
« on: November 02, 2015, 12:48:54 pm »
Here is something that I stuble upon today I have no idea why it does not work in fpc not even in delphi mode.
Start up lazarus go to the menu project]new Project and select program for the project type. Now copy paste the code bellow to the unit just opened in you editor and try to compile it.
Code: Delphi  [Select]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes
  10.   { you can add units after this };
  11. {$MODE DELPHI}
  12. var
  13.   FHandle : Thandle;
  14.   FStream :TFileStream;
  15. begin
  16.   FStream := TFileStream.Create(FHandle); end.
  17.  
This should raise the error
Quote from: Lazarus 1.5 r49638 FPC 3.1.1 i386-win32-win32/win64
project1.lpr(16,41) Error: Wrong number of parameters specified for call to "Create"


I tested it with Lazarus 1.5 r49638 FPC 3.1.1 i386-win32-win32/win64 and Lazarus 1.4.2 r49524 FPC 2.6.4 i386-win32-win32/win64. Overall I would expect the above to work with out a problem because the constructor I'm trying to call is declared in THandleStream which is the ancestor of TFileStream and the constructor is declared as public there which means that the compiler should be able to find it and call it, and as you can see it has a very unique signature in the hierarchy so its impossible to have any kind of collision with other constructors.

Thoughts? Reasons why this is?
« Last Edit: November 02, 2015, 12:52:58 pm by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

howardpc

  • Hero Member
  • *****
  • Posts: 3282
Re: Bug or feature?
« Reply #1 on: November 02, 2015, 01:09:28 pm »
I suppose because none of the constructors in these classes is virtual, the Create constructor in TFileStream hides the Create in its ancestor.

taazz

  • Hero Member
  • *****
  • Posts: 5363
Re: Bug or feature?
« Reply #2 on: November 02, 2015, 01:22:59 pm »
I suppose because none of the constructors in these classes is virtual, the Create constructor in TFileStream hides the Create in its ancestor.
Isn't that suppose to be true for constructors with the same signature? ee the same number, types and order of parameters. I suppose you could be right here, then again what happened to the optional keyword overload? Isn't that keyword that allows TFileStream to have two constructors with different signatures?
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: Bug or feature?
« Reply #3 on: November 02, 2015, 05:55:29 pm »
Reasons why this is?

TFileStream has only one constructor that is not marked as overload, meaning you can not have an instance of TFileStream except through its constructor.

The constructor of THandleStream does not know how to create an instance of TFileStream as both constructors are not mutually equivalent.

taazz

  • Hero Member
  • *****
  • Posts: 5363
Re: Bug or feature?
« Reply #4 on: November 02, 2015, 07:12:21 pm »
Quote from: classesh.inc fpc 2.6.4 lazarus 1.4.2
Code: Pascal  [Select]
  1. { TFileStream class }
  2.  
  3.   TFileStream = class(THandleStream)
  4.   Private
  5.     FFileName : String;
  6.   public
  7.     constructor Create(const AFileName: string; Mode: Word);
  8.     constructor Create(const AFileName: string; Mode: Word; Rights: Cardinal);
  9.     destructor Destroy; override;
  10.     property FileName : String Read FFilename;
  11.   end;
  12.  
No. It has 2 constructors none of them marked as overload;
All constructors are equivalent. The only difference is that on thandlestream I pass the handle returned from a os native fileopen call instead of the file name and on the second one I pass the file name to open for me.
I can't instantiate a thandlestream because the file access code is on the tfilestream and I can't use the ancestral constructor to pass my handle. See the problem?
« Last Edit: November 02, 2015, 07:23:45 pm by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Thaddy

  • Hero Member
  • *****
  • Posts: 9449
Re: Bug or feature?
« Reply #5 on: November 02, 2015, 07:24:20 pm »
Yes , that is correct. And No it isn't because of the handlestream third constructor and the default constructor for its grand parent TObject which makes it 4 (four) ;)

Anyway. This is not  intuitive I agree. But all of these are accessible through proper casting.
They should be virtual and marked override. They aren't in Delphi, though.
also related to equus asinus.

taazz

  • Hero Member
  • *****
  • Posts: 5363
Re: Bug or feature?
« Reply #6 on: November 02, 2015, 07:26:11 pm »
Yes , that is correct. And No it isn't because of the handlestream third constructor and the default constructor for its grand parent TObject which makes it 4 (four) ;)

Anyway. This is not  intuitive I agree. But all of these are accessible through proper casting.
They should be virtual and marked override. They aren't in Delphi, though.
You do see why I can't cast on the constructor right?
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Thaddy

  • Hero Member
  • *****
  • Posts: 9449
Re: Bug or feature?
« Reply #7 on: November 02, 2015, 07:43:33 pm »
If you mean that I did not test this in Lazarus, you are correct :0. In that case plz ignore.
If it is about the compiler, I tested this and see no problems
If it is about the classes unit you have a point. (but it is legacy ballast)
Casting a Tfilestream to a THandleStream works. Hard cast and AS.
« Last Edit: November 02, 2015, 07:48:48 pm by Thaddy »
also related to equus asinus.

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: Bug or feature?
« Reply #8 on: November 02, 2015, 10:32:45 pm »
Quote from: classesh.inc fpc 2.6.4 lazarus 1.4.2
Code: Pascal  [Select]
  1. { TFileStream class }
  2.  
  3.   TFileStream = class(THandleStream)
  4.   Private
  5.     FFileName : String;
  6.   public
  7.     constructor Create(const AFileName: string; Mode: Word);
  8.     constructor Create(const AFileName: string; Mode: Word; Rights: Cardinal);
  9.     destructor Destroy; override;
  10.     property FileName : String Read FFilename;
  11.   end;
  12.  
No. It has 2 constructors
Yes, you are right, I should've said:
"TFileStream has two constructors that are not marked as overload, meaning you can not have an instance of TFileStream except through its constructors."

none of them marked as overload;

That is my point.

All constructors are equivalent. The only difference is that on thandlestream I pass the handle returned from a os native fileopen call instead of the file name and on the second one I pass the file name to open for me.

thandlestream's constructor does not know about FFileName and will not change its value. That's what I meant when I said "not mutually equivalent."

I can't instantiate a thandlestream because the file access code is on the tfilestream and I can't use the ancestral constructor to pass my handle. See the problem?

I thought you chose these two classes as an example to show a constructor problem.

taazz

  • Hero Member
  • *****
  • Posts: 5363
Re: Bug or feature?
« Reply #9 on: November 03, 2015, 04:54:51 am »
it took some digging but I found a couple of pieces of information
1) according to the documentation auto overloading works only on the same unit.
There is only one case where the overload modifier is mandatory: if a function must be overloaded that resides in another unit. Both functions must be declared with the overload modifier: the overload modifier tells the compiler that it should continue looking for overloaded versions in other units.
2) It does not work on ancestor classes even in the same unit, you have to be explicit in order to avoid surfacing methods that might be problematic.
If the overload keyword is not present, then all overloaded versions must reside in the same unit, and if it concerns methods part of a class, they must be in the same class, i.e. the compiler will not look for overloaded methods in parent classes if the overload keyword was not specified.
Which makes sense in a way It just doesn't make sense in this specific case (my opinion).
That clears things up nicely.

Yes, you are right, I should've said:
"TFileStream has two constructors that are not marked as overload, meaning you can not have an instance of TFileStream except through its constructors."
Erm you mean that it explicitly hides the ancestral constructors. The way you said it you can replace tfilestream with any class and you will be right :D .

none of them marked as overload;

That is my point.
Your point of what?
All constructors are equivalent. The only difference is that on thandlestream I pass the handle returned from a os native fileopen call instead of the file name and on the second one I pass the file name to open for me.

thandlestream's constructor does not know about FFileName and will not change its value. That's what I meant when I said "not mutually equivalent."
True then again even if it knew and change it what exactly is the value of that property? Where it is used exactly? What problems might arise if it is empty?
I can't instantiate a thandlestream because the file access code is on the tfilestream and I can't use the ancestral constructor to pass my handle. See the problem?

I thought you chose these two classes as an example to show a constructor problem.
Not really I chose this specific example to ask about an inconsistency that puzzled me, mainly because it is extremely useful to be able to call any of the 3 constructors (its the developers responsibility of course to make sure it works as expected in all cases) expanding the class  functionality. If it ended up to be a bug then I would have filed a bug report, now that it seems to be by design if people find it useful then I might file a feature request. In any case I'm going to duplicate the code in my own library, I do not think that I will be able to call the inherited method even if I created a class inherited from tfilestream.

OW well more work for me yeah!
« Last Edit: November 03, 2015, 04:59:05 am by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

serbod

  • Full Member
  • ***
  • Posts: 129
Re: Bug or feature?
« Reply #10 on: November 03, 2015, 07:31:42 am »
Delphi is bad example, it allow override many limits by default, such as using of private and protected class members, off-range array indexes, and maybe even class ancestors constructors.

If you already have handle, you don't need TFileStream, it used only for acquire file handle by file name.

taazz

  • Hero Member
  • *****
  • Posts: 5363
Re: Bug or feature?
« Reply #11 on: November 03, 2015, 07:49:31 am »
Delphi is bad example,
::)
it allow override many limits by default, such as using of private
Example please when/how you are able to override private members of a class?

and protected class members
Erm again example so I can understand what you are talking about. As it is and since I was talking for a inheritance issue I would have to say that access to protected members is a must, so provide an example.

off-range array indexes.
You mean no range checking by default?

and maybe even class ancestors constructors.
and that is a bad thing? well I see how it can be a bad thing and I can see how it is a good thing too.


If you already have handle, you don't need TFileStream, it used only for acquire file handle by file name.
If you mean that the code to access the handle data is in THandleStream then I have to check. If it is there, then THandleStream is badly designed (Following your logic), because it doesn't protects you from assigning a memory address as a handle and rely only on the operating system to use the same api calls to manage a tcp handle, a file handle, etc. Bad, bad, bad designers, bad, bad.  :P

« Last Edit: November 03, 2015, 07:52:04 am by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: Bug or feature?
« Reply #12 on: November 03, 2015, 01:57:02 pm »
none of them marked as overload;

That is my point.
Your point of what?

the compiler will not look for overloaded methods in parent classes if the overload keyword was not specified.

serbod

  • Full Member
  • ***
  • Posts: 129
Re: Bug or feature?
« Reply #13 on: November 04, 2015, 08:29:36 am »
Example please when/how you are able to override private members of a class? As it is and since I was talking for a inheritance issue I would have to say that access to protected members is a must, so provide an example.

In Delphi there's a bug that makes the visibility of all members public within the same unit. The "strict" keyword corrects this behaviour, so that private is actually private, even within a single unit.

You mean no range checking by default?

Yes. It useful (but dangerous) feature for skilled programmers.

and maybe even class ancestors constructors.
and that is a bad thing? well I see how it can be a bad thing and I can see how it is a good thing too.

If object created from his parent constructor, some members can be not initialized.

If you already have handle, you don't need TFileStream, it used only for acquire file handle by file name.
If you mean that the code to access the handle data is in THandleStream then I have to check. If it is there, then THandleStream is badly designed (Following your logic), because it doesn't protects you from assigning a memory address as a handle and rely only on the operating system to use the same api calls to manage a tcp handle, a file handle, etc. Bad, bad, bad designers, bad, bad.  :P
I just say, that THandleStream is exactly the same as TFileStream, but using file handle instead of file name. If you have filename - use TFileStream, if you have handle - use THandleStream.

rvk

  • Hero Member
  • *****
  • Posts: 3885
Re: Bug or feature?
« Reply #14 on: November 04, 2015, 10:15:43 am »
I can't instantiate a thandlestream because the file access code is on the tfilestream and I can't use the ancestral constructor to pass my handle. See the problem?
In any case I'm going to duplicate the code in my own library, I do not think that I will be able to call the inherited method even if I created a class inherited from tfilestream.
If that code doesn't use Filename (which I assume it doesn't otherwise you can't use only a handle) why wasn't the code just implemented with THandleStream? If you implement the code using THandleStream you can still pass it a TFileStream without a problem.

The other way around would be more trickier. Filename wouldn't be initialized so you'll have to make sure that isn't used in the code. Can you pass a THandleStream as a TFileStream if you're sure uninitialized properties are not used ??? (I know the other way around is no problem)
( like Foo(FStream as TFileStream); )
It seems like a hack but using a TFileStream where you don't initialize Filename because you used Create from an ancester (if it was possible) would also be a hack.

Doesn't this work?
Code: Pascal  [Select]
  1. var
  2.   FHandle: Thandle;
  3.   Filename: string;
  4.   FStream: THandleStream;
  5. begin
  6.   if Filename <> '' then
  7.     FStream := TFileStream.Create(Filename, fmCreate)
  8.   else
  9.     FStream := THandleStream.Create(FHandle);
  10.  
  11.   // do all your stuff with FStream
  12.   // maybe Foo(FStream as TFileStream); // HACK ????
  13.  
  14. end.

But the best way would be to implement the code with the lowest ancestor you need. So if Filename isn't needed you should never implement your library-code with TFileStream but with THandleStream. Code that uses this library can still pass it a TFileStream.
So this would be more preferable:
Code: Pascal  [Select]
  1. procedure Foo(const FS: THandleStream);
  2. begin
  3.   // Do something with the THandleStream
  4. end;
  5.  
  6. var
  7.   Filename: string;
  8.   FStream: TFileStream;
  9. begin
  10.   FStream := TFileStream.Create(Filename, fmCreate);
  11.   Foo(FStream);    // <-- this could also be a THandleStream. Foo doesn't care.
  12. end.
« Last Edit: November 04, 2015, 10:18:15 am by rvk »