Sign in Join

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

Mailing lists / lazarus / 2010-04

OOP basics - 2

48 messages · 21 people · –

Reply by email Subscribe to this list Your subscription

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

Kjow

I'm sorry for the first email, it was a bit confusing.

Now, I created a project to make more clear what I mean. Take it as a stupid example, I need this to make more "advanced" things:

____________________

unit Unit1;
{$mode objfpc}{$H+}
interface

uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,

  unit2, unit3;
type
  { TForm1 }
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
    x,y: integer;
    tester1: TTest;
  end;
var
  Form1: TForm1;
implementation
{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  init(1,2);
  ShowMessage('Result: '+IntToStr(tester1.test(x,y)));
end;
end.

____________________

unit Unit2;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, unit1, unit3;
procedure init(a,b: integer; MainForm: TForm1);
implementation
var
  tester2: TTest;
procedure init(a,b: integer; MainForm: TForm1);
begin
  MainForm.x:=tester2.test(a,b);
  MainForm.y:=tester2.test(a,-b);
end;
end.

____________________

unit Unit3;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils;
Type
 TTest = class
  a: byte;
  b: byte;
  function test(x,y: integer): integer;
 end;
implementation
function TTest.test(x,y: integer): integer;
begin
 Result:=x+y;
end;
end.

____________________

Obviously I can't declare unit1 on unit2's "uses", because:

unit2.pas(8,27) Fatal: Circular unit reference between Unit2 and Unit1

So, how I can make it?

Reply by email

Michael Van Canneyt
to Kjow
Kjow wrote 106 lines
I'm sorry for the first email, it was a bit confusing. Now, I created a project to make more clear what I mean. Take it as a stupid example, I need this to make more "advanced" things: ____________________ unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, unit2, unit3; type { TForm1 } TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private { private declarations } public { public declarations } x,y: integer; tester1: TTest; end; var Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin init(1,2); ShowMessage('Result: '+IntToStr(tester1.test(x,y))); end; end. ____________________ unit Unit2; {$mode objfpc}{$H+} interface uses Classes, SysUtils, unit1, unit3; procedure init(a,b: integer; MainForm: TForm1); implementation var tester2: TTest; procedure init(a,b: integer; MainForm: TForm1); begin MainForm.x:=tester2.test(a,b); MainForm.y:=tester2.test(a,-b); end; end. ____________________ unit Unit3; {$mode objfpc}{$H+} interface uses Classes, SysUtils; Type TTest = class a: byte; b: byte; function test(x,y: integer): integer; end; implementation function TTest.test(x,y: integer): integer; begin Result:=x+y; end; end. ____________________ Obviously I can't declare unit1 on unit2's "uses", because: unit2.pas(8,27) Fatal: Circular unit reference between Unit2 and Unit1

Put unit2 in the implementation uses list of unit1. Then it should work.

Michael.

Reply by email

Paul Ishenin
to Kjow: I'm sorry for the first email, it was a bit confusing. Now, I created a project to make…

Why class and init are in the different units?

Show quoted text 10 lines
TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private { private declarations } public { public declarations } x,y: integer; tester1: TTest; end;

...

procedure init(a,b: integer; MainForm: TForm1);

Why not to move init to the TForm1?

Such problems are often happen when either you don't own a piece of code and therefore can't modify it or your code is designed bad.

Best regards,
Paul Ishenin.

Reply by email

Kjow
to Michael Van Canneyt: Put unit2 in the implementation uses list of unit1. Then it should work. Michael.

2010/4/14 Michael Van Canneyt :

Put unit2 in the implementation uses list of unit1. Then it should work. Michael.

Ohh, thank you!
It works :)

I have an other question (just to know if it is possible and how):

if I want to implement the function:

function TTest.test(x,y: integer): integer;
begin
 Result:=x+y;
end;

on Unit2, so I want to leave all things at the same place, but copy this function on Unit2: is it possible to declare methods and implement its on different unit?

Thanks,
Kjow

Reply by email

Kjow
to Paul Ishenin: Why class and init are in the different units? ... Why not to move init to the TForm1?…

2010/4/14 Paul Ishenin :

Why not to move init to the TForm1? Such problems are often happen when either you don't own a piece of code and therefore can't modify it or your code is designed bad.

It is simply a trivial example :)

It is at the base of an idea to make a "general" unit that can be used between more projects, so I want avoid to make copy/paste everytime the same functions. Often I forgot something... with a "generic" unit that initialize some "standard things" I can do it easily.

Best Regards,
Kjow

Reply by email

Kjow
to Michael Van Canneyt: Put unit2 in the implementation uses list of unit1. Then it should work. Michael.

2010/4/14 Michael Van Canneyt :

Put unit2 in the implementation uses list of unit1. Then it should work.

Other question:

if I have a similar situation, it is possible to implement?

____________________

unit Unit1;
{$mode objfpc}{$H+}
interface

uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, unit3;

type
  { TForm1 }
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
    x,y: integer;
    tester1: TTest;
  end;
var
  Form1: TForm1;
implementation
uses
  unit2;
{$R *.lfm}

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  init(5, 2, Form1);
  ShowMessage('Result: '+IntToStr(tester1.test(x,y,Form1)));
end;
end.
____________________
unit Unit2;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, unit1, unit3;
procedure init(a,b: integer; MainForm: TForm1);
implementation
var
  tester2: TTest;
procedure init(a,b: integer; MainForm: TForm1);
begin
  MainForm.x:=tester2.test(a,b,MainForm);
  MainForm.y:=-tester2.test(-a,-b,MainForm);
end;
end.
____________________
unit Unit3;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, unit1;
Type
 TTest = class
  a: byte;
  b: byte;
  function test(c,d: integer; MainForm: TForm1): integer;
 end;
implementation
function TTest.test(c,d: integer; MainForm: TForm1): integer;
begin
 Result:=c+d+MainForm.x+MainForm.y;
end;
end.
____________________

I changed the 3rd unit, so now it returns:

unit3.pas(8,27) Fatal: Circular unit reference between Unit3 and Unit1

Thanks!
Kjow

Reply by email

John vd Waeter
to Kjow

You cannot refer to unitX in the interface section of unitY AND at the same time refer to unitY in the interface section of unitX.

The compiler sees unitX needs unitY and so will try to compile unitY first. In unitY it sees unitX is needed and wants to compile unitX first and so on. Hence: circular reference.

If unitX needs unitY and vice versa, at least in one of them the reference to the other unit should be moved to the implementation section.

e.g:

unit unitX;
interface
uses unitY
implementation
var...
begin
end;
unit unitY
interface
...
implementation
uses unitX
var...
begin
end;

John

Kjow wrote 119 lines
2010/4/14 Michael Van Canneyt : > Put unit2 in the implementation uses list of unit1. Then it should work. Other question: if I have a similar situation, it is possible to implement? ____________________ unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, unit3; type { TForm1 } TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private { private declarations } public { public declarations } x,y: integer; tester1: TTest; end; var Form1: TForm1; implementation uses unit2; {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin init(5, 2, Form1); ShowMessage('Result: '+IntToStr(tester1.test(x,y,Form1))); end; end. ____________________ unit Unit2; {$mode objfpc}{$H+} interface uses Classes, SysUtils, unit1, unit3; procedure init(a,b: integer; MainForm: TForm1); implementation var tester2: TTest; procedure init(a,b: integer; MainForm: TForm1); begin MainForm.x:=tester2.test(a,b,MainForm); MainForm.y:=-tester2.test(-a,-b,MainForm); end; end. ____________________ unit Unit3; {$mode objfpc}{$H+} interface uses Classes, SysUtils, unit1; Type TTest = class a: byte; b: byte; function test(c,d: integer; MainForm: TForm1): integer; end; implementation function TTest.test(c,d: integer; MainForm: TForm1): integer; begin Result:=c+d+MainForm.x+MainForm.y; end; end. ____________________ I changed the 3rd unit, so now it returns: unit3.pas(8,27) Fatal: Circular unit reference between Unit3 and Unit1 Thanks! Kjow -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus
John vd Waeter www.jvdw.nl www.gps-carpool.net www.shotinthedark.nl www.pdaforms.nl www.dbapocket.nl

Reply by email

José Mejuto
to Kjow: 2010/4/14 Paul Ishenin : It is simply a trivial example :) It is at the base of an idea…

Hello Lazarus-List,

Wednesday, April 14, 2010, 4:38:49 PM, you wrote:

K> It is simply a trivial example :)
K> It is at the base of an idea to make a "general" unit that can be used K> between more projects, so I want avoid to make copy/paste everytime K> the same functions. Often I forgot something... with a "generic" unit K> that initialize some "standard things" I can do it easily.

For such repetitive things in your form, just use OOP, this means, instead separate that functions in different units implement such functions in a TForm descendent and inherit from it in your specialized forms, something like:

Unit MyBaseForm;
type
  TMyBaseForm=class(TForm)
  private
  protected
    procedure SetUsualCaption(); virtual;
    procedure AlignToUsualBorders(); virtual;
    procedure InsertDefaultPanelWithSomeIcons(); virtual;
  public
  end;

..............

And just in the form designed unit change the usual:

type
  Tform1=class(TForm)

by

  TForm1=class(TMyBaseForm);

This way you have the functions and usual procedures in a separated unit but you can access them from the form being designed.

Best regards, Jos?

Reply by email

Kjow
to John vd Waeter: You cannot refer to unitX in the interface section of unitY AND at the same time refer to…

2010/4/14 John vd Waeter :

Show quoted text 9 lines
You cannot refer to unitX in the interface section of unitY AND at the same time refer to unitY in the interface section of unitX. The compiler sees unitX needs unitY and so will try to compile unitY first. In unitY it sees unitX is needed and wants to compile unitX first and so on. Hence: circular reference. If unitX needs unitY and vice versa, at least in one of them the reference to the other unit should be moved to the implementation section.

Yes, I know, but how to use a class that is declared on different unit if it is needed before the implementation section?

In my example:

1) I need on unit1 (for a method, so in the type section) the TTest on unit3
2) I need on unit3 (for a method, so in the type section) the TForm1 on unit1

in both cases I can't declare each unit on implementation section, cause they are needed before the section.

is not possible?

Kjow

Reply by email

Kjow
to José Mejuto: K> It is simply a trivial example :) K> It is at the base of an idea to make a "general"…

2010/4/14 Jos? Mejuto :

This way you have the functions and usual procedures in a separated unit but you can access them from the form being designed.

All right,
Thanks!

But, can I implement functions/methods of "designed form" on others units? If I need to access to a component resource of the "design form", I need to implement it on the unit that contain:

TForm1=class(TMyBaseForm);

right?

So, if I need to access to the properties of a Tbutton that is on the "designed form", I can't... is it right? How to do? The question is similar to the one I :)

Reply by email

José Mejuto
to Kjow

Hello Lazarus-List,

Wednesday, April 14, 2010, 6:13:22 PM, you wrote:

K> But, can I implement functions/methods of "designed form" on others units? K> If I need to access to a component resource of the "design form", I K> need to implement it on the unit that contain:
K> TForm1=class(TMyBaseForm);
K> right?
K> So, if I need to access to the properties of a Tbutton that is on the K> "designed form", I can't... is it right? How to do? K> The question is similar to the one I me answer in a different way ;) If you need to access a futurable component in the form, something is wrong in your design ;)

If you would need to access a control it must be designed in TMyBaseForm, the problem is that I think it can not be visually designed (I think this is called VFI aka Visual Form Inheritance) and I think it is not supported or not well supported currently in Lazarus.

If you know, in example, that you will need to access a TMemo, because all forms have a TMemo with a log of what is happening then you can create in the BaseForm a dummy TMemo reference to be used by the functions and just in the descendent setup a reference in the FormCreate event:

type
  TMyBaseForm=class(TForm)
  protected
    TMemoLog: TMemo;
    procedure AddToLog(text: String);
  end;
  .....
procedure TMyBaseForm.AddToLog(text: String);
begin
  if not Assigned(TMemoLog) then exit;
  TMemoLog.Lines.Add(text);
end;
...................................
type
  TForm1=class(TMyBaseForm)
  procedure FormCreate(Sender: TObject);
  .....
TForm1.procedure FormCreate(Sender: TObject);
begin
  TMemoLog:=Memo1;
end;
...................................

You should not implement a function/procedure using futurable present objects, unless you like to play with castings and the nice AV when casting a button to a memo because the names where exchanged :(

If you plan to use dynamic add/remove objects then the code would be a bit more complex because the free of Memo1 and set to nil will not be inherited by TMemoLog so you should proceed with care.

The advantage of this is that you keep a type control of everything without nasty castings.

Best regards, Jos?

Reply by email

Michael Van Canneyt
to Juha Manninen: Hi Circular references are a real problem in real (non-trivial) Object Pascal programs.…
Juha Manninen wrote 10 lines
Hi Circular references are a real problem in real (non-trivial) Object Pascal programs. You could look at this thread: http:/msg16051.html The solution is to copy everything into one big unit. That's what everybody finally does after fighting with interfaces, abstract base classes and type casting.

It is all a matter of design.
In more than 20 years of pascal programming, I never encountered this problem.

Michael.

Reply by email

Mattias Gaertner
to Michael Van Canneyt: It is all a matter of design. In more than 20 years of pascal programming, I never…
Show quoted text 17 lines
On Wed, 14 Apr 2010, Juha Manninen wrote: > Hi > > Circular references are a real problem in real (non-trivial) Object Pascal > programs. You could look at this thread: > > http:/msg16051.html > > The solution is to copy everything into one big unit. That's what everybody > finally does after fighting with interfaces, abstract base classes and type > casting. It is all a matter of design. In more than 20 years of pascal programming, I never encountered this problem.

+1

Mattias

Reply by email

Juha Manninen
to Michael Van Canneyt: It is all a matter of design. In more than 20 years of pascal programming, I never…
It is all a matter of design.

Not only. Sometimes many classes are tightly dependent on each other. It doesn't mean the design is bad, they just are logically close and interdependent.
Some other problem scope allows one-way dependency between classes. It doesn't make the design better.

In more than 20 years of pascal programming, I never encountered this problem.

Lazarus core itself has such code. Earlier I tried to understand Designer, ControlSelection and Object Inspector code. They are highly interdependent, for logical reasons. References to core classes were typecasted in many places in code, clearly to avoid circular reference.
Some files are also very big which is one way to avoid circular reference.

Juha

Reply by email

José Mejuto
to Juha Manninen

Hello Lazarus-List,

Wednesday, April 14, 2010, 9:58:45 PM, you wrote:

JM> Lazarus core itself has such code. Earlier I tried to understand Designer, JM> ControlSelection and Object Inspector code. They are highly interdependent, JM> for logical reasons. References to core classes were typecasted in many places JM> in code, clearly to avoid circular reference.
JM> Some files are also very big which is one way to avoid circular reference.

Many of this "problems" can be solved with some refactoring like creating a base class for the 2 or 3 different classes. It's known that sometimes this is impossible because you already must use a base class already defined like TWinControl or any other.

Best regards, Jos?

Reply by email

Lee Jenkins
to Juha Manninen: Hi Circular references are a real problem in real (non-trivial) Object Pascal programs.…
Juha Manninen wrote 11 lines
Hi Circular references are a real problem in real (non-trivial) Object Pascal programs. You could look at this thread: http:/msg16051.html The solution is to copy everything into one big unit. That's what everybody finally does after fighting with interfaces, abstract base classes and type casting.

Agreed. I think I remember bringing this up on the Delphi NG's not too long ago. Little things like that do not make it impossible, but still I think make more work to implement OOP patterned software. You really have to give extra thought to the "geography" of an application's code base to avoid painting yourself into a corner. For some things, I just make it easier on myself and try to stick as much of an object hierarchy into one unit as I can in good (self) conscience get away with. State Pattern comes to mind where I use objects to represent state. Either create all your objects at one time or stick the class definitions in one unit otherwise the circular reference things pokes its head out again.

I know there are interfaces (which I definitely use though not to extent that I do in other languages like C# or ActionScript where I seem to get more mileage with them) and the implementation uses area to reference safely other units, but the end is the same. Namely that it feels in many instances like I'm using more of workarounds to achieve some OO things when it should feel like I'm implementing in a way that feels more natural as it seems to me to be with some other languages.

Of course, the GC'd vs explicit memory management seems to the the cause of many of these differences. I know the circular reference thing is related to the compiler or linker, just one other little thing that I'd like to see resolved some day in OP as I plan to stick with it for some time.

BTW, not a criticism. Just an observation probably in need of correcting in many places :-)

Warm Regards, Lee

Reply by email

Marco Alvarado
to Paul Ishenin

Just an idea. It would be useful to have something like PHP's include_once, but with the functionality of "uses". For example, instead of inserting the whole content of the included file, it could break it at interface and implementation keywords, and insert each block in the corresponding sections.

Regards!
-Marco

2010/4/14 Paul Ishenin :

Show quoted text 16 lines
15.04.2010 0:13, Kjow wrote: > > But, can I implement functions/methods of "designed form" on others units? For this you need class helpers which are not implemented at the moment in FPC. Best regards, Paul Ishenin. -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Michael Van Canneyt
to Juha Manninen: Not only. Sometimes many classes are tightly dependent on each other. It doesn't mean the…
Juha Manninen wrote 5 lines
> It is all a matter of design. Not only. Sometimes many classes are tightly dependent on each other. It doesn't mean the design is bad, they just are logically close and interdependent.

If that is really the case they should be in the same unit, and the problem is moot.

It's not for nothing that TCollection and TCollectionItem are in the same unit.

Michael.

Reply by email

Alexander Klenin
to Juha Manninen: Not only. Sometimes many classes are tightly dependent on each other. It doesn't mean the…
Juha Manninen wrote 7 lines
> It is all a matter of design. Not only. Sometimes many classes are tightly dependent on each other. It doesn't mean the design is bad, they just are logically close and interdependent. Some other problem scope allows one-way dependency between classes. It doesn't make the design better.

I agree with Michael here. "Put everything in one big file" is a pure evil, equivalent to "I do not want to think about proper design, let's just pile all the code in one big heap"

> In more than 20 years of pascal programming, I never encountered this > problem. Lazarus core itself has such code. Earlier I tried to understand Designer, ControlSelection and Object Inspector code.

Yes, Lazarus has this "one big file" problem it and it is horrible. I tried to fix some parts of it, but my fixes were rejected due to backwards-compatibility concerns.
This highlights yet another reason to design the code properly -- after you create
the big mess, you might not be able to untangle it even if you wanted to!

Alexander S. Klenin

Reply by email

Marco van de Voort
to Lee Jenkins: Agreed. I think I remember bringing this up on the Delphi NG's not too long ago. Little…
Lee Jenkins wrote 6 lines
> finally does after fighting with interfaces, abstract base classes and type > casting. Agreed. I think I remember bringing this up on the Delphi NG's not too long ago. Little things like that do not make it impossible, but still I think make more work to implement OOP patterned software.

Each time I work with a curly braces language, I think the opposite. Since it is a lot harder to interpret compiler messages because assumptions about forward defined types are inconsistent.

There might also be a relation to the way languages with a Oberon like hierachical classpath (like Java and C#) search files.

You really have to give extra thought to the "geography" of an application's code base to avoid painting yourself into a corner.

Yes. Two times an year. However the opposite is to face more cryptic errormessages every day.

Reply by email

Graeme Geldenhuys
to Juha Manninen: I don't know what is wrong with you guys. SW design can be good even if it has…

2010/4/15 Juha Manninen :

Show quoted text 5 lines
"We never encountered a problem. We just typecast everything! Hey no problem!" Funny :-)

True. Lazarus even forces new code contributions to always reference units in the Interface section to force circular reference errors as soon as possible (I was told that apparently FPC had a issue with loading units - this must have been before my time). Then classes must be slapped into a single large unit to overcome the issue. I definitely agree Juha, it is not a great design idea.

I must confess, over the years I have hit circular reference problems, but not many. In the cases that I did get the problem, I had to resort to a base class, and then typecast later. I just assumed this is part of the Object Pascal language design (to improve compilation speed), and is now stuck with us forever.

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

Reply by email

Juha Manninen
to Alexander Klenin: I agree with Michael here. "Put everything in one big file" is a pure evil, equivalent to…
I agree with Michael here. "Put everything in one big file" is a pure evil, equivalent to "I do not want to think about proper design, let's just pile all the code in one big heap"

I don't know what is wrong with you guys. SW design can be good even if it has interdependent classes. It depends on the original problem scope. This is from Michael's other mail:

Show quoted text 5 lines
If that is really the case they should be in the same unit, and the problem is moot. It's not for nothing that TCollection and TCollectionItem are in the same unit.

Right, so the original comment
"In more than 20 years of pascal programming, I never encountered this problem."
actually means to copy everything into one file.
That's what I was saying, too! :-)

If I understand right, Michael and Mattias have made big parts of Lazarus core which I referred to. They have used types casting to avoid circular references. Now they say they never encountered this problem. It is like:
"We never encountered a problem. We just typecast everything! Hey no problem!"

Funny :-)

Juha
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20100415/f8a10cd6/attachment.html>

Reply by email

Florian Klaempfl
to Juha Manninen
Show quoted text 28 lines
> I agree with Michael here. "Put everything in one big file" is a pure evil, > equivalent to "I do not want to think about proper design, let's just > pile all the code in one big heap" I don't know what is wrong with you guys. SW design can be good even if it has interdependent classes. It depends on the original problem scope. This is from Michael's other mail: > If that is really the case they should be in the same unit, and the > problem is moot. > > It's not for nothing that TCollection and TCollectionItem are in the > same unit. Right, so the original comment "In more than 20 years of pascal programming, I never encountered this problem." actually means to copy everything into one file.

Yes, this is what is done in C++ basically as well. Even worse, C/C++ pulls in this big file (aka header mess) in every compilation unit again and again. You can also split your pascal code in several include files as in C/C++ and claim that you don't use one big file.

Show quoted text 11 lines
That's what I was saying, too! :-) If I understand right, Michael and Mattias have made big parts of Lazarus core which I referred to. They have used types casting to avoid circular references. Now they say they never encountered this problem. It is like: "We never encountered a problem. We just typecast everything! Hey no problem!"

In the TCollection and TCollectionItem case typecasting is always required.

Reply by email

Juha Manninen
to Alexander Klenin: I agree with Michael here. "Put everything in one big file" is a pure evil, equivalent to…
Show quoted text 9 lines
> Lazarus core itself has such code. Earlier I tried to understand Designer, > ControlSelection and Object Inspector code. Yes, Lazarus has this "one big file" problem it and it is horrible. I tried to fix some parts of it, but my fixes were rejected due to backwards-compatibility concerns. This highlights yet another reason to design the code properly -- after you create the big mess, you might not be able to untangle it even if you wanted to!

How would you fix it for Designer, ControlSelection and Object Inspector? They are truly interdependent. It is not caused by poor design.

Juha

Reply by email

Michael Van Canneyt
to Juha Manninen: I don't know what is wrong with you guys. SW design can be good even if it has…
Juha Manninen wrote 19 lines
> I agree with Michael here. "Put everything in one big file" is a pure evil, > equivalent to "I do not want to think about proper design, let's just > pile all the code in one big heap" I don't know what is wrong with you guys. SW design can be good even if it has interdependent classes. It depends on the original problem scope. This is from Michael's other mail: > If that is really the case they should be in the same unit, and the > problem is moot. > > It's not for nothing that TCollection and TCollectionItem are in the > same unit. Right, so the original comment "In more than 20 years of pascal programming, I never encountered this problem." actually means to copy everything into one file. That's what I was saying, too! :-)

Nono, you should not put words in my mouth which I didn't say nor mean.

If I understand right, Michael and Mattias have made big parts of Lazarus core which I referred to. They have used types casting to avoid circular references. Now they say they never encountered this problem.

Well, I didn't create any of the code you are referring to. I never use typecasts of the sort described in the mail.

Then: TCollection/TCollectionItem were defined by Borland.

Regarding that particular example:
They are so closely bound together that it makes no sense to put them in separate units. If someone wanted to put them in separate units, he would have some serious explaining to do regarding the need for that.

For the rest: I have a code base of many millons of lines. Nowhere I have encountered the problems you describe. No circular dependencies, no need for huge files either. A typical unit contains 4, maybe 5 classes, in rare cases 10. And those classes are always closely bound together, so it simply makes sense to put them in 1 unit.

As a nice side effect, it makes the uses clauses more simple. If every class I ever used was in a separate unit, the uses clauses would simply explode; Delphi (or FPC) has trouble enough finding the files it needs. As it is, a typical project takes 15 minutes to compile with delphi on a recent machine. I can't imagine what would happen if I put every class in a separate unit...

To summarize: All I wanted to say is that

1. I don't think that each class should be in it's own unit; If it is beneficial to do so, I will. If not: I don't.

2. The structure of my classes has never forced me to solve circular references by throwing classes together in 1 unit, or by using strange typecasts.

Michael.

Reply by email

Alexander Klenin
to Juha Manninen: How would you fix it for Designer, ControlSelection and Object Inspector? They are truly…
Juha Manninen wrote 9 lines
> Yes, Lazarus has this "one big file" problem it and it is horrible. > I tried to fix some parts of it, but my fixes were rejected due to > backwards-compatibility concerns. > This highlights yet another reason to design the code properly -- > after you create > the big mess, you might not be able to untangle it even if you wanted to! How would you fix it for Designer, ControlSelection and Object Inspector? They are truly interdependent. It is not caused by poor design.

By cleanly separating implementation layers and defining responsibilities and requirements for each in e hierarchical manner. Yes, the above sounds like a bunch of meaningless buzzwords ;-) It is hard to describe such things with words, but if you are interested you can look at the code in attached patch
(it is very big, but most of it is code movement, which you can easily see if you use git).

[The patch turned out so big that the mail was rejected, re-sending with compressed patch]

Alexander S. Klenin -------------- next part -------------- A non-text attachment was scrubbed... Name: propedit_extract_hook.7z Type: application/octet-stream Size: 7638 bytes Desc: not available URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20100415/9037cbe4/attachment-0001.obj>

Reply by email

Juha Manninen
to Florian Klaempfl: Yes, this is what is done in C++ basically as well. Even worse, C/C++ pulls in this big…
Yes, this is what is done in C++ basically as well. Even worse, C/C++ pulls in this big file (aka header mess) in every compilation unit again and again. You can also split your pascal code in several include files as in C/C++ and claim that you don't use one big file.

Yes, I also finally realized that a big source file is not necessarily a bad thing. It is a question of attitude. A good editor and IDE helps browse and maintain the code.
Include files seem to be useful with FPC/Lazarus. With Delphi they are not so useful.

Juha

Reply by email

Juha Manninen
to Michael Van Canneyt: Nono, you should not put words in my mouth which I didn't say nor mean. Well, I didn't…
Well, I didn't create any of the code you are referring to.

Ok, I should have checked the facts here. Sorry.

Show quoted text 18 lines
For the rest: I have a code base of many millons of lines. Nowhere I have encountered the problems you describe. No circular dependencies, no need for huge files either. A typical unit contains 4, maybe 5 classes, in rare cases 10. And those classes are always closely bound together, so it simply makes sense to put them in 1 unit. As a nice side effect, it makes the uses clauses more simple. If every class I ever used was in a separate unit, the uses clauses would simply explode; Delphi (or FPC) has trouble enough finding the files it needs. As it is, a typical project takes 15 minutes to compile with delphi on a recent machine. I can't imagine what would happen if I put every class in a separate unit... To summarize: All I wanted to say is that 1. I don't think that each class should be in it's own unit; If it is beneficial to do so, I will. If not: I don't. 2. The structure of my classes has never forced me to solve circular

references

by throwing classes together in 1 unit, or by using strange typecasts.

Huge amounts of code you have!
My experience is that you sometimes end up with big files when you put all interdependent classes in it.
Now, the question is also if a big source file is a bad thing or not. I have adjusted my way of thinking. The code can be maintained with good tools (like Lazarus) even in big files.
A big file is of course intimidating for someone who looks at it for the first time, but so are hundreds of small files.

Regards,
Juha

Reply by email

Graeme Geldenhuys
to Juha Manninen
Juha Manninen wrote
Now, the question is also if a big source file is a bad thing or not. I have adjusted my way of thinking. The code can be maintained with good tools (like Lazarus) even in big files.

It's not only a issue about managing the large files. If you dump all classes in a single large unit (or include files, it makes no difference), then you give up on all the class visibility rules. Now all classes can access all private field variables or methods of all other classes in the same unit. THIS is not always a good thing, and splitting those classes over multiple units, allows for visibility rules.

'Strict Private' etc resolves this, but is only supported under Delphi mode I believe. It will be useful in ObjFPC mode supports "strict" as well.

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

Reply by email

Juha Manninen
to Alexander Klenin: By cleanly separating implementation layers and defining responsibilities and…
Show quoted text 7 lines
By cleanly separating implementation layers and defining responsibilities and requirements for each in e hierarchical manner. Yes, the above sounds like a bunch of meaningless buzzwords ;-) It is hard to describe such things with words, but if you are interested you can look at the code in attached patch (it is very big, but most of it is code movement, which you can easily see if you use git).

I use git but I don't know all the features. How to compare the changes which are moved to another file?

I was looking at your patch with a diff tool (Kompare) and it looks good. Not many typecasts. :-)
IMO it should be accepted. What are those backwards-compatibility concerns?

Juha

Reply by email

Juha Manninen
to Graeme Geldenhuys: It's not only a issue about managing the large files. If you dump all classes in a single…
Show quoted text 11 lines
It's not only a issue about managing the large files. If you dump all classes in a single large unit (or include files, it makes no difference), then you give up on all the class visibility rules. Now all classes can access all private field variables or methods of all other classes in the same unit. THIS is not always a good thing, and splitting those classes over multiple units, allows for visibility rules. 'Strict Private' etc resolves this, but is only supported under Delphi mode I believe. It will be useful in ObjFPC mode supports "strict" as well.

Strict Private works in ObjFPC mode, too. That's what I have read, not even tried myself.
Yes it should be used more. Then big source files are still less of a problem.

Juha

Reply by email

Alexander Klenin
to Juha Manninen: I use git but I don't know all the features. How to compare the changes which are moved…
Juha Manninen wrote
I use git but I don't know all the features. How to compare the changes which are moved to another file?

After you have committed the patch, execute
git blame -C propedithook.pas | grep -v propedits
too see only those lines from propedithook.pas that are not moved from propedits.pp

I was looking at your patch with a diff tool (Kompare) and it looks good. Not many typecasts. :-) IMO it should be accepted. What are those backwards-compatibility concerns?

http://lists.lazarus.freepascal.org/pipermail/lazarus/2010-January/048584.html

Alexander S. Klenin

Reply by email

Marco van de Voort
to Graeme Geldenhuys: It's not only a issue about managing the large files. If you dump all classes in a single…
Graeme Geldenhuys wrote 8 lines
It's not only a issue about managing the large files. If you dump all classes in a single large unit (or include files, it makes no difference), then you give up on all the class visibility rules. Now all classes can access all private field variables or methods of all other classes in the same unit. THIS is not always a good thing, and splitting those classes over multiple units, allows for visibility rules.

I've always thought that (and strict private) to be highly doubtful.

IMHO there is a bigger chance in making mistakes in micromanaging visibility that you regret later, then making dangerous mistakes because private is visible inside an unit.

Reply by email

Alexander Klenin
to Marco van de Voort
Marco van de Voort wrote 5 lines
I've always thought that (and strict private) to be highly doubtful. IMHO there is a bigger chance in making mistakes in micromanaging visibility that you regret later, then making dangerous mistakes because private is visible inside an unit.

Maybe, but note that fixing "too strict" visibility is a trivial one-line change,
while fixing "too lax" visibility might require a serious code refactoring.

More importantly, most bugs are easy to fix once you fully understand the software architecture and the nature of the problem. It is that understanding that is greatly harmed by too large units and excessive fields scope.

Alexander S. Klenin

Reply by email

Marco van de Voort
to Alexander Klenin
Alexander Klenin wrote 6 lines
> IMHO there is a bigger chance in making mistakes in micromanaging visibility > that you regret later, then making dangerous mistakes because private is > visible inside an unit. Maybe, but note that fixing "too strict" visibility is a trivial one-line change,

And of course the reworking of the manual (if public interface changes), and coping with descendants that define something with higher visibility already.

IMHO visibility is like a contract. It shouldn't be constantly changed.

while fixing "too lax" visibility might require a serious code refactoring.

I don't see why. It will very rarely bite you when moving classes out of units, but then there is already refactoring going on, interfaces are changed, and one or two lines more won't care.

More importantly, most bugs are easy to fix once you fully understand the software architecture and the nature of the problem. It is that understanding that is greatly harmed by too large units and excessive fields scope.

Sure. Which is I'm all for the middle ground, but this is IMHO going into the micromanaging bit. Unit is still local scope for me.

If the visibility in the unit is a problem, the unit is too big. Period.

Reply by email

Jürgen Hestermann
to Juha Manninen: Yes, I also finally realized that a big source file is not necessarily a bad thing. It is…
Show quoted text 5 lines
Yes, I also finally realized that a big source file is not necessarily a bad thing. It is a question of attitude. A good editor and IDE helps browse and maintain the code. Include files seem to be useful with FPC/Lazarus. With Delphi they are not so useful.

The problem discussed here is not about OOP. It's about units (modules) and how compiler and linker use them.

The general question is: For what reason should I split my program in units at all? The only reasons I can think of are:

1.) Modularity: You use a unit written by someone else. Then of course this code is in a separate unit. Since Turbo Pascal you can get this (compiled) unit without having the source code.

2.) Compile speed: If you have code in a separate unit which you do not change (any longer), then the compiler does not need to compile all the functions/classes within this unit on each build of the program. In the past that has been a large speed up but I am not sure whether this is still an issue on fast machines today (maybe only for very large projects).

Any other known reasons? In the past there were other limitations that forced people to split programs into units (like 64 segment barriers) but I don't know about such things today.

Include files only make sense if you include such a file in multiple source files.

Reply by email

waldo kitty
to Juha Manninen: Ok, I should have checked the facts here. Sorry. references Huge amounts of code you have!
Juha Manninen wrote 5 lines
Now, the question is also if a big source file is a bad thing or not. I have adjusted my way of thinking. The code can be maintained with good tools (like Lazarus) even in big files. A big file is of course intimidating for someone who looks at it for the first time, but so are hundreds of small files.

personally speaking, i'd rather wade into one huge file than a forest of many small ones... especially when wading into someone else's code... it is much easier to find the routines in one huge file than it is to have to search many small ones for it and then remember where you were when you started the search ;)

Reply by email

Aleksa Todorovic
to waldo kitty
waldo kitty wrote 14 lines
On 4/15/2010 05:33, Juha Manninen wrote: > > Now, the question is also if a big source file is a bad thing or not. I have > adjusted my way of thinking. The code can be maintained with good tools (like > Lazarus) even in big files. > A big file is of course intimidating for someone who looks at it for the first > time, but so are hundreds of small files. personally speaking, i'd rather wade into one huge file than a forest of many small ones... especially when wading into someone else's code... it is much easier to find the routines in one huge file than it is to have to search many small ones for it and then remember where you were when you started the search ;) From my experience (looking at code of projects with severals hundred

thousands loc), if you have good refactoring tools in your development environment, size of individual files doesn't matter that much, because those tools allow you to have a picture of execution flow as well as overall structure in terms of classes, functions, variables without need to think of individual files.

Reply by email

waldo kitty
to Aleksa Todorovic
Aleksa Todorovic wrote 15 lines
On Thu, Apr 15, 2010 at 18:35, waldo kitty wrote: > On 4/15/2010 05:33, Juha Manninen wrote: >> Now, the question is also if a big source file is a bad thing or not. I have >> adjusted my way of thinking. The code can be maintained with good tools (like >> Lazarus) even in big files. >> A big file is of course intimidating for someone who looks at it for the first >> time, but so are hundreds of small files. > personally speaking, i'd rather wade into one huge file than a forest of many small ones... especially when wading into someone else's code... it is much easier to find the routines in one huge file than it is to have to search many small ones for it and then remember where you were when you started the search ;) From my experience (looking at code of projects with severals hundred thousands loc), if you have good refactoring tools in your development environment, size of individual files doesn't matter that much, because those tools allow you to have a picture of execution flow as well as overall structure in terms of classes, functions, variables without need to think of individual files.

agreed to a point... that point being that those tools must exist and one must have those tools... they didn't exist when i started and, in fact, many programmers used wordstar as their coding environment... they were quite happy when programming editors came around that used wordstar commands... they were even happier because now they could trigger a command that would call up the compiler and return with the result of the compile...

yes, times have changed but some of us still use the old ways because we know them... i'm still amazed at what the Lazarus IDE does for me (ie: opening up the referenced routine) but even with that, it doesn't help me truly learn which files all the code is in... at least it takes some time and paying much attention to the window's titles and such...

Reply by email

Thierry Coq
to Juha Manninen: Hi Circular references are a real problem in real (non-trivial) Object Pascal programs.…

Hello,

I see the opposite comments in curly braced languages about circular dependencies and (lack) of reuse all the time. It's a question of <lack> of design which paints them into corners regularly.

The fact that the compiler enforces the acyclic properties of the dependency graph is, in fact, a huge advantage for the pascal programmer.

Two design techniques not mentioned in the thread, and very useful for thinking large scale programming are:
- the inversion of dependencies, (which leads to creating interfaces and abstract classes),
- the use of (self-designed) events.

Best regards,
Thierry

Juha Manninen wrote 21 lines
Hi Circular references are a real problem in real (non-trivial) Object Pascal programs. You could look at this thread: http:/msg16051.html The solution is to copy everything into one big unit. That's what everybody finally does after fighting with interfaces, abstract base classes and type casting. Juha -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Doug Chamberlin
to Michael Van Canneyt: Nono, you should not put words in my mouth which I didn't say nor mean. Well, I didn't…
Michael Van Canneyt wrote 9 lines
To summarize: All I wanted to say is that 1. I don't think that each class should be in it's own unit; If it is beneficial to do so, I will. If not: I don't. 2. The structure of my classes has never forced me to solve circular references by throwing classes together in 1 unit, or by using strange typecasts.

My experience parallels Michael's. No need to resort to one big file (or unit) unless you want to "give up" and take the easy route.

Doug C. ----- A: Because it messes up the order in which people normally read text. Q: Why is top-posting such a bad thing? A: Top-posting. Q: What is the most annoying thing in e-mail?

Reply by email

Justin Smyth
to Marco van de Voort: And of course the reworking of the manual (if public interface changes), and coping with…

Marco

Can you please take a look at mantis bug id 16321

We need this addessed ASAP its breaking SendMessage under x64.

Summary

when CopyDataStruct is used on win64 with sendmessage/postmessage the dest wont get the messages.

if we provide CopyDataStruct64 like this -

CopyDataStruct64 = record
  dwData : ULONG_PTR;
  cbData : DWORD;
  lpData : PVOID;
 end;

then this showstopper will be fixed.

Kind Regards

Justin Smyth

Reply by email