Delphi - Using interfaces from another unit - delphi

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).

Related

Is it good practice to exploit a compiler bug?

Recently I found some odd-looking (to me) Delphi code and I have isolated it to a separate small project. Here is what I discovered. Unit1 compiles with no errors. Unit2 (which I provide for comparison) does not. The difference is in the way that Classes is used.
unit Unit1;
interface
uses Classes; // difference here
type TThread = class(Classes.TThread)
public
property Terminated;
end;
implementation
end.
Unit2 does not compile. Various errors are produced.
unit Unit2;
interface
uses System.Classes; // difference here
type TThread = class(Classes.TThread)
public
property Terminated;
end;
implementation
end.
[dcc32 Error] Unit1.pas(7): E2003 Undeclared identifier: 'Classes'
[dcc32 Error] Unit1.pas(7): E2029 ',' or ':' expected but ')' found
[dcc32 Error] Unit1.pas(9): E2147 Property 'Terminated' does not exist in base class
So my concern is that this project is exploiting a compiler bug to achieve it's goals. The compiler bug might be fixed in a later release, and then the code won't work anymore.
There is no compiler bug that makes Unit1 compile. It compiles because in the project setting the entry for Unit Scope Names contains at least the item System, which is used to resolve the reference to Classes in the full name System.Classes. As the uses contains Classes, the reference to Classes.TThread also succeeds.
In Unit2 the uses contains System.Classes. Therefore the reference Classes.TThread cannot be resolved anymore. Change it to System.Classes.TThread and it works.
If you uses System.Classes you must also use System.Classes when referring to the classes unit in the code as shown below.
unit Unit2;
interface
uses System.Classes; // difference here
type TThread = class(System.Classes.TThread)
public
property Terminated;
end;
implementation
end.

Delphi - How do you resolve a conflict when a unit name is the same as a property name?

The trivial example below is a condensation of a problem I had trying to resolve a conflict where I had an enumerated type member with the same name as a VCL member.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TSomeType = (
alNone,
alSome,
alMany) ;
procedure TForm1.FormCreate(Sender: TObject);
begin
Self.Align := alNone ; // 1. type mismatch
Self.Align := Controls.alNone ; // 2. "Controls" is also a property of TForm
end ;
end.
The first assignment fails because the compiler thinks alNone is the one I declared and not the TAlign member defined in Controls.pas.
The second one fails because it took Controls to mean the TForm property of that name.
I realise there are ways around this (renaming the alNone member being the simplest), but I'm curious as to whether there is a way of qualifying a reference to a property in another unit where the unit name conflicts with an identifier in the current scope.
Qualify it with the type name:
TAlign.alNone
I had not realised when I wrote this that the compiler version was relevant. This syntax only became available in Delphi 2010 or XE. There the answer is not appropriate for the tagged version, Delphi 2007. Deltics answer covers much more detail.
As David's answer suggests, for an enum type or other situation where a type can be used to qualify the identifier involved then you can of course simply use the type name as required:
someAlign := TAlign.alNone;
someMyType := TMyType.alNone;
This use of enums is referred to as "scoped enums" and is not supported in older versions of the Delphi compiler. I believe XE2 may have been when it was introduced. Certainly this was the version that made scoping enums in this way mandatory by default.
Though it can be turned off via a compiler directive. When turned off, you can still use scoped enums but you are not required to.
In versions that support this, you must qualify any enums that are defined while this is turned on. You can choose whether to quality or not when using enums that are defined when this is turned off.
type
{$SCOPEDENUMS ON}
TFoo = (Black, White); // MUST qualify: eg. "TFoo.Black"
{$SCOPEDENUMS OFF}
TBar = (Black, White); // MAY qualify or not if/as needed
For older versions of Delphi without scoped enum support, or in situations where the identifier is not an enum member and cannot otherwise be qualified by type - for example if your identifiers conflict with some unit-level identifier (e.g. such as mrOk, in Controls) then you need to a little bit more work, but not much.
In these cases, simply define a new constant to create an unambiguous "local alias" for the constant in the other unit, and introduce this where the unit name is unambiguous. Similar to:
type
TMyResult = (
mrOk,
mrFailed) ;
const
Controls_mrOk = Controls.mrOk; // mrOk is a const, not an enum member

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...

Circular reference between class

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).

Resources