A colleague of mine bumped into a constant that had suddenly 'changed value';
Turned out, it was redeclared:
unit Unit1;
interface
const
MyConstant = 1;
implementation
end.
--
unit Unit2;
interface
const
MyConstant = 2;
implementation
end.
--
Uses Unit1, Unit2;
// Uses Unit2, Unit1;
procedure TFrmRedefineConstant.FormShow(Sender: TObject);
begin
ShowMessage('MyConstant: ' + IntToStr(MyConstant));
end;
This shows 2. If you swap the unit order in the Uses statement, it shows 1.
Fine, but why does the Delphi compiler not warn about the duplicate constant name (That would be very helpful)?
Is there anything I can do to enable warnings (does not look that way).
Because of Delphi documented scoping rules. From the Language Guide:
The order in which units appear in the uses clause determines the
order of their initialization and affects the way identifiers are
located by the compiler. If two units declare a variable, constant,
type, procedure, or function with the same name, the compiler uses the
one from the unit listed last in the uses clause. (To access the
identifier from the other unit, you would have to add a qualifier:
UnitName.Identifier.)
This is the expected behaviour since Turbo Pascal 4.0, which introduced units.
Related
I am trying to convert Delphi2005 code to Delphi Tokyo 10.2.3 code.
The function VarType is no longer recognized.
I need the function VarType to determine the basic type of a variant variable. In general I find, according to many postings, that it should be in the unit System.Variants. However, if I search e.g. in:
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/!!FUNCTIONS_System.html
It is not in this unit. Furthermore, I cannot find the unit variants, only a unit variant.
However, using the unit variant I get a runtime error:
Record, object or class type necessary
. So this doesn't work.
if (System.Variant.VarType(Value) and varTypeMask) =
System.Variant.varString then // VarType(Value) unbekannt
begin
TByte8Array(PRecFORMULA3(PBuf).Value)[0] := 0;
end;
Anyway I don't find VarType in System.variant. Does variants not exist anymore?
Can anyone help me?
The documentation you linked to is quite old. It is for Delphi 2009, which predates the introduction of Unit Scope Names. But even in that old documentation, VarType() is documented as being in the Variants unit (not in the Variant unit, which does not exist).
Unit Scope Names, like System, were added to RTL/VCL unit names in XE2 (thus, the Variants unit became System.Variants).
Embarcadero's newer DocWiki, which replaces the old Docs site, clearly shows that the VarType() function is indeed located in the System.Variants unit.
Make sure that either:
you have System.Variants in your uses clause:
uses
..., System.Variants;
you have System in your project's list of Unit Scope Names, and then you can use Variants in your uses clause:
uses
..., Variants;
Either way, you can then use VarType() as expected, without having to fully qualify it:
if (VarType(Value) and varTypeMask) = varString then
begin
TByte8Array(PRecFORMULA3(PBuf).Value)[0] := 0;
end;
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.
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 work on Delphi project who interac with many other small libraries.
I use FastMM4 and I would like work with complex classes passed on dll parameter.
So for exemple I send my form to my dll. Into the dll I test the type of parameter with the operator "IS".
But into the Dll the operator "IS" return always "false"
Exemple
library Dll;
uses
FastMM4,
System.SysUtils,
System.Classes,
Vcl.Dialogs,
Vcl.Forms;
{$R *.res}
procedure Complex(L : TObject);stdcall;
begin
if L is TForm then
showmessage('Ok')
else
showmessage('Pas ok') ;
if L is TCustomFrame then
showmessage('Ok')
else
showmessage('Pas ok')
end;
exports
Complex;
begin
end.
And the call
procedure TffsIsOperator.Button2Click(Sender: TObject);
var
MaDLL : THandle;
Proc : procedure (l : TObject);
begin
try
MaDLL := LoadLibrary(PChar('Dll.dll'));
#Proc := GetProcAddress(MaDLL, 'Complex');
Proc(self);
finally
FreeLibrary(MaDLL);
end;
end;
Firstly, you have a calling convention mis-match. You must fix that by making the calling convention the same on both sides of the interop boundary.
Even when you fix that, the apparent misbehaviour of the is operator is to be expected. You have two instances of the VCL in your process. One in the host and one in the DLL. They each have distinct versions of the classes defined in the VCL. So, the DLL's TForm is a different class form the TForm in the host. And that is why is evaluates false.
The traditional way to handle this is to arrange that you only have one instance of the RTL/VCL in your process. And you achieve that through the use of runtime packages.
If runtime packages are not a viable option for you, and you must use a DLL, then you will have to give up passing any Delphi classes across the DLL boundary. I fully expect this to be unwelcome news, but that is just how it is. You cannot pass TObject instances across a DLL boundary and attempt to call methods, query type identity, etc. That is simply not supported for DLLs. Only for runtime packages.
So, if you have to use DLLs then you need to stick to simple types. Integers, floating point values, character types, arrays (but not dynamic arrays), records, pointers to such types, interfaces. As a simple rule of thumb, if you cannot find an example of your proposed interop in Win32, then it is probably invalid.
If I have a unit that is filled with constants like...
unit AConsts;
interface
const
Const1 : WideString = 'Const1';
Const2 : WideString = 'Const2';
Const3 : WideString = 'Const3';
Const4 = 100;
Const5 = 100;
implementation
end.
and I want to use this unit from another unit, is there any difference between...
unit AUnit;
interface
uses
AConsts;
Implementation
end.
and
unit AUnit;
interface
implementation
uses
AConsts;
end.
?
The question is not about scope, avoiding circular references etc. It is about differences in the compiled application.
If UnitA, UnitB and UnitC all use AConsts, would there be a difference in the compiled application (assuming no name clashes between the constants in the AConsts unit and other code) between App1 where these UnitA, UnitB and UnitC all have AConsts in the interface section and App2 where UnitA, UnitB and UnitC all have AConsts in the implementation section.
The difference has to do with where you're allowed to refer to the things that AConsts has in its interface section. In the first AUnit, you could use Const4 to declare a fixed-size array in that interface section. You couldn't do that in the second AUnit because Const4 isn't in scope.
It can have an effect on the compiled program, if you're not careful. Suppose we have another unit that also declares a constant named Const4:
unit BConsts;
interface
const
Const4 = 50;
implementation
end.
Now we define an array in UnitA like this:
unit AUnit
interface
uses BConsts;
var
data: array[0..Pred(Const4)] of Integer;
implementation
uses AConsts;
procedure Work;
var
i: Integer;
begin
for i := 0 to Const4 - 1 do begin
data[i] := 8;
end;
end;
end.
That code will write beyond the end of the array because the Const4 that's in scope in the interface section is not the same Const4 that's used in the implementation section. This doesn't happen often with constants. It usually just happens with two identifiers, the FindClose function defined in Windows and SysUtils, and TBitmap, defined in Graphics and Windows. And in those two cases, the compiler will tell you that you've done something wrong, although it won't tell you precisely that you've used an identifier that has two different meanings. You can resolve the problem by qualifying the identifier:
for i := 0 to BConsts.Const4 - 1 do
data[i] := 8;
If all the above precautions are addressed, so your program compiles and runs correctly, then it makes no difference where units are used. In your example with App1 and App2, the two programs will be the same. They won't be identical — the compiler will have processed things in a different order and thus will likely put things in different places — but it will have no effect on the execution of your program.
I put all references in the implementation section and only put those unit names in the interface that I have to.
I like to limit the scope of everything as much as possible, though, and this policy is pursuant to that.
The IDE also uses how where you declare your uses to ascertain what it needs to compile.
If your interface section uses UnitA and your implementation section uses UnitB then, if unit B needs recompiling, your unit won't, but if unitA changes then your unit will need to be recompiled.
It's one of the secrets of Delphis super fast build speed.
As to your finished executable, I expect that it will turn out the same size wherever you put the declarations (the linker is clever and only links in methods etc that are actually used by your application), but the actual location of various sources would almost certainly change if the order of the unit declarations is changed.
Items in the uses statement in the interface are visible in the entire unit.
Items in the uses statement in the implementation are only visible in the implemantation section.
Example:
unit A;
interface
const
cA = 1;
..
unit B;
interface
const
cB = 1;
..
unit C;
interface
uses
A;
const
cC1 = cA;
cC2 = cB; // Error
implementation
uses
B;
const
cC3 = cA;
cC4 = cB;
end.
You can create mutual dependent units if at least one unit is included in the implementation section:
unit A;
interface
implementation
uses
B;
end.
unit B;
interface
implementation
uses
A;
end.
If both are used in the interface section it won't compile/link.
I follow a rule that I put everything on Interface part unless I need to deal with problems with circular references. This helps to bring a little clarity.
Some wizards in Delphi and "File>Use Unit..." dialog put units on implementation section.
Except for the scoping traps which Rob Kennedy highlighted, it's doesn't matter. Make your standard and stick with it.