Circular reference between class - delphi

i need develop the next diagram class:
I wrote code, but, have problems of circular unit reference.
The XmlFileManager Class contains:
unit XmlFileManager;
interface
uses
xmldom, XMLIntf, msxmldom, XMLDoc, SysUtils, DateUtils, Classes, Dialogs,
XmlEnpManager;
type
TXmlFileManager = class
private
[...]
xmEnp: TXmlEnpManager;
xmEnpInicial: TXmlEnpManager;
xmEnpFinal: TXmlEnpManager;
[...]
end.
The abstract class, XmlNodeManager:
unit XmlNodeManager;
interface
uses
xmldom, XMLIntf, msxmldom, XMLDoc, SysUtils, DateUtils, Classes, Dialogs,
XmlFileManager;
type
TXmlNodeManager = class
protected
{ sgy alias para strategy }
sgyIterator: Integer;
sgyContext: TXmlFileManager;
sgyAttributes: TStringList;
sgyNode: IXMLNode;
[...]
end.
And the XmlEnpManager concrete class:
unit XmlEnpManager;
interface
uses
xmldom, XMLIntf, msxmldom, XMLDoc, SysUtils, DateUtils, Classes, Dialogs,
XmlNodeManager;
type
TXmlEnpManager = class (TXmlNodeManager)
public
constructor Create(aContext: TXmlFileManager); overload; override;
constructor CreateInicial(aContext: TXmlFileManager); reintroduce; overload;
constructor CreateFinal(aContext: TXmlFileManager); reintroduce; overload;
[...]
end.
The builds fails with error:
[dcc32 Fatal Error] XmlNodeManager.pas(7): F2047 Circular unit
reference to 'XmlFileManager'
Any ideas how to solve this problem ?.

Put TXmlFileManager and TXmlNodeManager in both the same unit and the same type section, then make sure that type section starts with this class forward: TXmlNodeManager = class;
See the official documentation: Forward Declarations and Mutually Dependent Classes.
unit XmlFileManagerAndXmlNodeManager;
interface
uses
xmldom, XMLIntf, msxmldom, XMLDoc, SysUtils, DateUtils, Classes, Dialogs,
[...]
type
TXmlNodeManager = class;
TXmlFileManager = class
private
[...]
xmEnp: TXmlEnpManager;
xmEnpInicial: TXmlEnpManager;
xmEnpFinal: TXmlEnpManager;
[...]
TXmlNodeManager = class
protected
sgyIterator: Integer;
sgyContext: TXmlFileManager;
sgyAttributes: TStringList;
sgyNode: IXMLNode;
[...]
end.

If possible, easiest way is to have all classes in same unit.

Although putting all classes in the same unit is a perfect solution, you might consider breaking the mutual reference by interfacing at least one of the classes, defining that interface in a separate unit and letting the other unit refer to the unit with the interface in its interface section. Then you can move the first unit reference from the interface to the implementation.The creation of an instance will then be done in the implementation section, where a mutual reference is allowed, but thus breaking the mutual reference in the interface section, which is not allowed, as you already noticed. This allows you to keep both classes in a separate unit, should that be required.
Hope this makes sense.

If the reference between the units is in the implementation section instead of the interface section then this will work.
You could create a base class in another unit with no out going reference and reference it from the interface and cast into the actual class on demand by adding the specific unit reference to the implementation section.

In XMLNodeManager, Make:
sgyContext: TObject;
Then you don't need to use the unit XmlFileManager in interface. Use it in the Implementation section's Uses clause. When using sgyContext in the implementation code, cast it to TXmlFileManager(sgyContext).

Related

I have two units and a class in each unit that need each other to function. How do I avoid a circular reference?

I have 2 classes, ClassA and ClassB, each in their own seperate unit, UnitA and UnitB.
UnitA uses UnitB. This allows ClassA to call the constructor for ClassB. Part of ClassB's functionality however requires the use of ClassA's methods. I cannot make UnitB use UnitA, else this causes a circular reference. What alternative do I have in allowing ClassB to access ClassA's methods from within UnitB?
It depends a lot on how the two classes need to use each other. If one class uses the other in the implementation, but not the interface, then you can move that unit in the uses clause from the interface section down to the implementation section instead.
This would work either way around:
UnitA;
interface
uses
Classes, UnitB;
type
TClassA = class(TObject)
end;
...
.
UnitB;
interface
uses
Classes;
type
TClassB = class(TObject)
end;
implementation
uses
UnitA;
...
However, if the interface of both your classes rely on each other, then you have no choice but to put both classes in the same unit (and using forward declarations).
UnitA;
interface
uses
Classes;
type
TClassA = class;
TClassB = class;
TClassA = class(TObject)
end;
TClassB = class(TObject)
end;
...
In rare cases, you might even be able to move an entire class definition down to implementation. That wouldn't be useful though if you needed to actually use that class elsewhere.
UnitB;
interface
implementation
uses
Classes, UnitA;
type
TClassB = class(TObject)
end;
...
Depending on the complexity, it's also common practice to put commonly shared code in a separate unit of its own. For example constants, record array types, global variables, etc. This common unit shall not use any other of these units.
To expand a little on the previous answer, another possiblity (although definitely less readable) is to refer to an ancestor that both have acess to ( in the extreme case TObject) and then cast in the implementation stage. So
interface
TClassA = class
...
fRefToClassB : TObject; // internal ref
...
procedure UsingBRef( pVar : TObject );
...
end;
implementation
procedure TClassA.UsingClassB
begin
with fRefToClass as TClassB do
...
end;
procedure TClassB.UsingBRef( pVar : TObject );
begin
with pVar as TClassB do
...
end;
Obviously there are many variations on a theme and you would implement is better than this. Again I would emphasise the previous solution is better, but this provides a get out when all else fails.
If you have ClassA and ClassB and both use methods in ClassA, then you may want to refactor those methods out of ClassA into their unit (and class if you fancy).

Error: "Undeclared identifier" using a form from another Unit - Delphi 2010

I'm trying to use a Form from another Unit, but the code isn't recognizing the other Unit.
Example:
unit uImpressao;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, uniGUITypes, uniGUIAbstractClasses,
uniGUIClasses, uniGUIForm, uniGUIBaseClasses, uniPanel, uniURLFrame;
type
TfImpressao = class(TUniForm)
ufRelatorio: TUniURLFrame;
UniImage1: TUniImage;
procedure UniImage1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
uses
MainModule, Main, uBancoHoras;
procedure TfImpressao.UniImage1Click(Sender: TObject);
begin
fBh.iTeste.Visible := false;
end;
end.
unit uBancoHoras;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Dialogs, uniGUITypes, uniGUIAbstractClasses,
uniGUIClasses, uniGUIForm, uniLabel, pngimage, uniImage, uniGUIBaseClasses,
uniPanel, uniPageControl, uniButton, uniBitBtn, uniSpeedButton, uniCanvas,
uniDateTimePicker, uniMemo, uniMultiItem, uniComboBox, uniEdit, uniBasicGrid,
uniDBGrid, uniDBMemo, uniRadioButton, uniDBText, uniRadioGroup, frxClass,
frxDBSet;
type
TfBH = class(TUniForm)
iTeste : TUniImage;
private
{ Private declarations }
public
{ Public declarations }
end;
var
url: string;
function fBH: TfBH;
implementation
{$R *.dfm}
uses
MainModule, Main, uImpressao;
function fBH: TfBH;
begin
Result := TfBH(MM.GetFormInstance(TfBH));
end;
procedure TfBH.iTesteClick(Sender: TObject);
begin
fImpressao.ShowModal;
end;
When I try using the uImpressao unit in uBancohoras unit, returns the error "Undeclared identifier 'fImpressao'".
Using uBancoHoras unit in the uImpressao unit, works fine.
I don't understand why this error is happening with one unit, but not with the other.
I hope you can help me!
Note: Please note that this answer was based on the original code posted in the question, which was subsequently replaced in its entirety with new and vastly different code.
fBH is not declared in unit A,so fBH.iTeste.Visible := True; can't possibly work; there is no such variable. You deleted the global variable fBH that the IDE creates for you (although, interestingly you left the var statement above it right above the implementation keyword).
Either add back in the declaration (by adding var fBH: TfBH; between the end of the class declaration and the implementation keyword), or create an instance of the form in unit B when you need to use it and access it through the local variable from there.
(Whichever direction you go, you never address a form using the variable from within that form's methods; use Self instead. Don't use fBH.ShowModal; use either Self.ShowModal or ShowModal instead.)
In uBancoHoras you have defined
function fBH: TfBH;
...
implementation
...
function fBH: TfBH;
begin
Result := TfBH(MM.GetFormInstance(TfBH));
end;
So you have defined a global function called fBH that returns an instance of the TfBH form class, seemingly through some sort of factory method (probably defined in MainModule?).
There is no corresponding method or variable in uImpressao with the name fImpressao, however - the the compiler error that fImpressao is an undeclared identifier.
Assuming that MM.GetFormInstance is suited to the task, and also assuming you wish to keep this design pattern, you would have to define (in uImpressao)something like :
function fImpressao: TfImpressao;
...
implementation
...
function fImpressao : TfImpressao;
begin
Result := TfImpressao(MM.GetFormInstance(TfImpressao));
end;
We can't see the implementation details of MM.GetFormInstance, however, so there is no guarantee that this will work - it only follows the pattern that fBH has set. Agreed with Ken that you should perhaps consider a better way to manage your forms. Resorting to global variables or global methods that reach across units to dig up a class instance feels like a headache waiting to happen...

Dcu file not found

i have a problem compiling my Delphi code.
I have 3 classes, XmlFileManager (concrete), XmlNodeManager (abstract), XmlEnpManager (child of XmlNodeManager and concrete).
Below, a little of definition class code:
XmlFileManager
unit XmlFileManager;
interface
uses
xmldom, XMLIntf, msxmldom, XMLDoc, SysUtils, DateUtils, Classes, Dialogs,
XmlNodeManager, XmlEnpManager;
type
TXmlFileManager = class
[...]
end;
[...]
end.
XmlNodeManager
unit XmlNodeManager;
interface
uses
xmldom, XMLIntf, msxmldom, XMLDoc, SysUtils, DateUtils, Classes, Dialogs,
XmlFileManager;
type
TXmlNodeManager = class
[...]
end;
[...]
end.
XmlEnpManager
unit XmlEnpManager;
interface
uses
xmldom, XMLIntf, msxmldom, XMLDoc, SysUtils, DateUtils, Classes, Dialogs,
XmlFileManager, XmlNodeManager;
type
TXmlEnpManager = class (TXmlNodeManager)
[...]
end;
[...]
end.
In the XmlNodeManager and XmlEnpManager, not recognize TXmlFileManager class. An whe i compile, the compilation fails with message:
[dcc32 Fatal Error] SiGAS.dpr(23): F1026 File not found:
'XmlManager.dcu'
In the past, the XmlFileManager was called XmlManager.
Any ideas ?.
My .dpr:
uses
Forms,
Main in 'forms\Main.pas' {Principal},
Globals in 'units\Globals.pas',
CrearProyectoForm in 'forms\CrearProyectoForm.pas' {NuevoProyecto},
Validadores in 'units\Validadores.pas',
IdiomaClass in 'units\IdiomaClass.pas',
IdiomaCastellanoClass in 'units\IdiomaCastellanoClass.pas',
ExcelFileManagerClass in 'units\ExcelFileManagerClass.pas',
SeleccionarIdioma in 'forms\SeleccionarIdioma.pas' {SelectLang},
EnpView in 'forms\EnpView.pas' {ENP},
EnpViewGeneric in 'forms\EnpViewGeneric.pas' {EnpGeneric},
Vcl.Themes,
Vcl.Styles,
EnpViewAdd in 'forms\EnpViewAdd.pas' {EnpAdd},
EnpViewAddAfter in 'forms\EnpViewAddAfter.pas' {EnpAddAfter},
EnpViewEdit in 'forms\EnpViewEdit.pas' {EnpEdit},
EnpInicial in 'forms\EnpInicial.pas' {ENPViewInicial},
XmlFileManager in 'units\XmlFileManager.pas',
XmlNodeManager in 'units\XmlNodeManager.pas',
XmlEnpManager in 'units\XmlEnpManager.pas';
Your main source file, SiGAS.dpr, still has it listed as XmlManager, so...
Open your *.dpr file (it is just Delphi code) and fix the unit name in the uses clause, then rebuild.

Access to public methods and properties inside a Delphi BPL

I have an application that loads a BPL that as inside a simple form.
This form is an optional option of the main application.
The BPL loads correctly, the form is shown correctly, but I don’t know how to access the public methods and properties of the form inside the bpl.
Can anyone provide a simple example?
my code:
// Load the BPL on aplication Load
LoadPackage( 'About.bpl' );
// CAll for TForm1 inside the About.BPL
var
AClass: TClass;
AForm: TForm;
begin
AClass := GetClass('TForm1');
if AClass <> nil then
begin
Application.CreateForm(TComponentClass(AClass), AForm);
AForm.Show;
end;
// The unit TForm1 inside the BPL package
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
PublicMthd;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
Procedure TForm1.PublicMthd;
Begin
ShowMessage('Inside call');
End;
initialization
RegisterClass(TForm1);
finalization
UnRegisterClass(TForm1);
end.
How can i access "PublicMthd" in Tform1 ?
One of the interest of having TOptionalForm in a dynamically loaded bpl (assuming this from the "optional" bit)) is to avoid for your application to hold the definition of the TOptionalForm class specifically (it's in the unit contained in the package and only there).
That means that your application cannot know anything about it unless you use either:
- a shared base Class
- an Interface declaring the properties and methods you want to access
- some basic RTTI to access published properties and methods
- some extended RTTI to access public properties and methods (if you have D2010 or later)
- some external routines from the bpl accepting a base class parameter (or TObject/pointer) typecasting it as TOptionalForm internally.
This is very vague and general and more precision about your code would be needed to refine...
If you need to load the BPL dynamically, you should use - as already metioned by François:
an abstract class (which is more Delphi-like or)
an interface (which I consider cleaner and have better experience with)
placed into an interface-only unit used by both the main application and the form BPL.
I use an intermediate "contract/interface" BPL, statically used by both the main application and the dynamically loaded ones.
In the case of interface usage, may also look at the $WEAKPACKAGEUNIT directive to further decouple the BPL fom the application.
To comment on the comments - by using DLL exports or RTTI, you would basically bypass the whole point of BPLs, which is type and namespace sharing.

Delphi - Using interfaces from another unit

I am constantly getting the: Undeclared identifier for an interface type I have defined in another unit.
Here is what I have:
unit Drawers;
interface
implementation
type
IDrawer = interface
['{070B4742-89C6-4A69-80E2-9441F170F876}']
procedure Draw();
end;
end.
unit Field;
interface
uses
Graphics, Classes, Drawers;
TField = class(TInterfacedObject, IField)
private
FSymbolDrawer: IDrawer;
At FSymbolDrawer I get the complier error.
Of course I have the uses Drawers; in the unit where TField is defined.
What's this about?
Thank you
In the unit Drawers the type declaration of IDrawer has to be in the interface part of the unit. You have inserted it in the implementation part where it is only visible for in-unit declarations.
Here is the code:
unit Drawers;
interface
type
IDrawer = interface
['{070B4742-89C6-4A69-80E2-9441F170F876}']
procedure Draw();
end;
implementation
end.
Which uses clause do you add Drawers to? It has to be in the interface uses clause (above the definition of TField that uses it).

Resources