Roland Bengtsson
Roland Bengtsson

Reputation: 5158

How to avoid using inc-files for code with Delphi

I use Bold for Delphi as ORM framework and I try to write how the codegeneretion works to better understand my question. A model stored in a bld-file is used as source for generating the class definitions for classes in the model.

So a file called BusinessClasses.pas is generated from scratch. Structure is like this:

unit BusinessClasses;

{$DEFINE BusinessClasses_unitheader}
{$INCLUDE BusinessClasses_Interface.inc}

{$INCLUDE AmObject.inc}
{$INCLUDE Quantity.inc}
{$INCLUDE Distance.inc}
// etc. My source have about 80 inc-files with code

const
  BoldMemberAssertInvalidObjectType: string = 'Object of singlelink (%s.%s) is of wrong type (is %s, should be %s)';

{ TBusinessClassesRoot 
  All classes in model inherit from TBusinessClassesRoot.
  Following is base methods that can be used everywhere}

procedure TBusinessClassesRootList.Add(NewObject: TBusinessClassesRoot);
begin
  if Assigned(NewObject) then
    AddElement(NewObject);
end;

function TBusinessClassesRootList.IndexOf(anObject: TBusinessClassesRoot): Integer;
begin
  result := IndexOfElement(anObject);
end;

function TVehicle._GetRegNo: String;
begin
  Result := M_RegNo.AsString;
end;

procedure TVehicle._SetRegNo(const NewValue: String);
begin
  M_RegNo.AsString := NewValue;
end;

// So all basic methods that Get and Set attributes for all classes is here.
// In my case it means a big file about 83000 LOC.

Code in BusinessClasses_Interface.inc look like this:

{$IFNDEF BusinessClasses_Interface.inc}
{$DEFINE BusinessClasses_Interface.inc}

{$IFNDEF BusinessClasses_unitheader}
unit BusinessClasses;
{$ENDIF}

interface

uses
  // interface uses
  {$INCLUDE Attracs_Interface_Uses.inc} ,  
  // other
  Classes,
  // more uses follow...

type
  { forward declarations of all classes }

  TBusinessClassesRoot = class;
  TBusinessClassesRootList = class;
  TaccountProperty_TransportAccountProperty = class;
  TaccountProperty_TransportAccountPropertyList = class;
// A lot of forward declarations follow....

  TBusinessClassesRoot = class(TBoldObject)
  private
  protected
  public
  end;

  TaccountProperty_TransportAccountProperty = class(TBusinessClassesRoot)
  private
    function _GetaccountProperty: TTransportAccountProperty;
    function _Get_M_accountProperty: TBoldObjectReference;
  protected
  public
    property M_accountProperty: TBoldObjectReference read _Get_M_accountProperty;
    property accountProperty: TTransportAccountProperty read _GetaccountProperty;
  end;

// More class declarations follow
// File size is about 35000 LOC

So BusinessClasses_Interface.inc and BusinessClasses.pas is generated from scratch while the inc-files like AmObject.inc with real code is maintained by the developer.

It have been worked like this all the time since D7. But there are some disadvantages.

So my initial idea was to convert all inc-files to standard pas-files instead. The reference to code should go by uses instead of $INCLUDE inc-file. Then I discovered why this odd solution to store code in inc-files was chosen. Two classes in the model can be interlinked. It means that Class A have a reference to Class B. And Class B have reference to Class A. This must work. Class forward declarations is used for that. And Delphi compiler seems to require this.

From http://docwiki.embarcadero.com/RADStudio/Rio/en/Classes_and_Objects_(Delphi)#Forward_Declarations_and_Mutually_Dependent_Classes

A forward declaration must be resolved by a defining declaration of the same class within the same type declaration section. In other words, between a forward declaration and its defining declaration, nothing can occur except other type declarations.

So Delphi 10.3 Rio works exactly the same as in old D7.

Ok, so now at last my question :)

I have not seen any other projects than Bold to use inc-files to store code. Is there any other trick or solution to achieve the same result ?

Upvotes: 0

Views: 1395

Answers (2)

Roland Bengtsson
Roland Bengtsson

Reputation: 5158

I now believe I have a solution to the problem. I think the culprit is that classes have direct access to each other. So class A is aware of class B etc. So interfaces could be the solution.

Unfortunately it means a lot of work in my case as all references in model need to be changed. Also codegeneration need generate interfaces instead of direct links.But the most work required would being Bold framework itself as it don't support interfaces now.

But this small sample works at least:

Unit A

unit TestA;

interface

uses
  TestInterfaces;

type
  TTestA = class
    M_TestB: ITestB;
  end;

implementation

end.

Unit B

unit TestB;

interface

uses
  TestInterfaces;

type
  TTestB = class
    M_TestA: ITestA;
  end;

implementation

end.

interfaces

unit TestInterfaces;

interface

type
  ITestA = interface
    ['{5DE20444-241E-4F59-9B7F-96A76BA3DDFF}']
  end;

  ITestB = interface
    ['{40C19A93-C816-4D1E-9C74-6B710D6974BF}']
  end;

implementation

end.

Upvotes: 0

RM.
RM.

Reputation: 2043

The simplest solution would be to run a pascal preprocessor after generating the include and unit files to merge the include files into the unit. Here is one: http://clootie.narod.ru/delphi/download_utils.html#ppp

So all include files will end up inside the unit. The include files can be deleted to avoid confusion.

Upvotes: 1

Related Questions