I get an AccessViolation when running the following simple code with Delphi XE5:
Access violation at 0x0040213c: write of address 0x00000000
This runs without any problems with Delphi 2009! What has changed since than?!
program consola;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Classes;
type
MyRecord = Record
name : string;
age : Integer;
end;
var
recPointer : ^MyRecord;
FList: PPointerList;
begin
New(recPointer);
recPointer.name := 'Brian';
recPointer.age := 23;
GetMem(FList, 4 * SizeOf(recPointer));
FList^[0] := recPointer;
end.
Have you seen declaration of PPointerList on XE5? This is pointer to dynamic array now
Extract from help:
Delphi type PPointerList = ^TPointerList;
Delphi type TPointerList = array of Pointer;
so possible usage is:
New(Flist);
SetLength(Flist^, 4);
FList^[0] := recPointer;
(of course, PPointerList is useless here, it's enough to exploit TPointerList)
You are using the wrong types here. Your FList variable is a pointer to a dynamic array of untyped pointers. That is not what you need at all. And you don't allocate the memory properly. I'm not going to try to explain more about your code because your current approach is fundamentally flawed.
You should use either a dynamic array or TList<MyRecord>. The former looks like this:
var
Arr: TArray<MyRecord>;
....
SetLength(Arr, 4);
Arr[0].Name := ...
And the latter:
var
List: TList<MyRecord>;
Rec: MyRecord;
....
List := TList<MyRecord>.Create;
Rec.Name := ...
List.Add(Rec);
Your code is a recipe for disaster. Even if you fix the types to allow you to allocate using GetMem, how are you going to handle management of the managed string member of your record. Make life easy by using type safe data structures and so allow the compiler to do the heavy lifting.
Related
Working with anonymous functions I found out that sometimes the compiler throws the following error:
E2555 Cannot capture symbol 'Self' when I try to use some field of the object.
I also noticed that this error seems to be related to the fact that a type, the method belongs to, is declared with "object" key word:
MyType = object()
field: integer;
...
end;
MyType.Method1()
begin
p := procedure
begin
// do something with field
end;
end;
However when a type is declared with "class" keyword it seems it works fine.
I know that to prevent the compiler error I can make a local copy of needed fields and use them inside the anonymous functions, but just to be sure - is "object" type cause of the compiler error and what's the reason of that?
Thanks in advance
As David properly analyzed it is because Self in your case is a value and not a reference. It cannot be moved to the internally created class - same is the case with any method arguments that are records. They also cannot be captured for the very same reason.
For arguments I usually copy them to a local variable which is being captured.
The same can be done for capturing Self in a record or object.
However if you capture it as value you get a copy and calling the closure later might have the "wrong" state because it captured a copy. To make it work similar you would have to capture a reference to Self but then for a value type you cannot guarantee that this reference is still valid when you call the closure.
You can see this in the following code:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TProc = reference to procedure;
PRecord = ^TRecord;
TRecord = object
y: Integer;
procedure Foo;
function GetProc: TProc;
end;
procedure TRecord.Foo;
begin
Writeln(y);
end;
function TRecord.GetProc: TProc;
var
this: PRecord;
begin
this := #Self;
Result :=
procedure
begin
this.Foo;
end;
end;
procedure Nested(var p: TProc);
var
r: TRecord;
begin
p := r.GetProc();
r.y := 0;
p();
r.y := 32;
p();
end;
procedure Main;
var
p: TProc;
begin
Nested(p);
p(); // <- wrong value because PRecord not valid anymore
end;
begin
Main;
end.
If you would capture TRecord it would do a local copy that it captures - you can see that it then will print 0 all the time.
Since Turbo Pascal object is long deprecated, it is reasonable for new language features not to have support for object.
There's not really any need to look much further. Since you are maintaining legacy code, I would not expect you to be introducing new language features like anonymous methods. Once you start introducing such language features, this no longer feels like legacy code maintenance and it would be reasonable to re-factor the code away from the legacy language features like object.
Having said that, I do note that the same restriction to capture applies in methods of advanced records.
type
TProc = reference to procedure;
TRecord = record
procedure Foo;
end;
procedure TRecord.Foo;
var
P: TProc;
begin
P :=
procedure
begin
Foo;
end;
end;
This fails to compile with error:
E2555 Cannot capture symbol 'Self'
Why does this code fail, even though advanced records are a fully supported modern feature?
I don't have an explanation for that and the documentation does not make it clear. A plausible explanation is that records are value types. When a local variable is captured, it is hoisted from being a stack allocated variable to a variable owned by an internally created class. That's possible for Self when Self is a reference to an instance of a class. But when Self is a value like a record, it is too late to hoist the record.
Or perhaps it is much more prosaic. Maybe the designers just implemented the most important use case (capturing Self for a class) and omitted the less widely used cases for expediency. It is frustrating that the documentation does not appear to give any rules for what can and cannot be captured.
I want to refactor DelphiAST to use interfaces to deal with different types, rather than the clunky
TDirectionary it uses now.
Some research shows that 70%+ of the running time is spend in the dictionary.
So I'll make interfaces like:
TSyntaxNode = class
...
end;
IIdentifier = interface
['{D72E945D-E397-4E02-B702-8AE6C235F9C6}']
function GetIdentifier: string;
property Identifier: string read GetIdentifier;
end;
IMethod = interface(Identifier)
['{8E6119DC-E0F3-42BD-A5BF-FB658E34499E}']
.....
end;
TMethodNode = class(TSyntaxNode, IMethod, IIdentifier,...)
...
end;
The problem according to Roman is:
Reference counting may cause performance issues. DelphiAST creates thousands of classes to produce the syntax tree (more than 100,000 of TSyntaxNode instances, when input file is big enough). How many times the reference counter would be called?
Every time that happens a hidden try finally is invoked and that will slow things way down.
Strict use of const in method params prevents the refcount code calling the method, but afaik it still happens every time you do something like, say, MyRef = List[0] - it will increment the refcount assigning to MyRef, even though the item is still present in the list.
How can I work with interfaces whilst not having to worry about refcounting and try-finally blocks?
I'm perfectly happy to manage destruction of classes manually.
Further info
I'm guessing I need to use TAggregatedObject as a base ancestor.
And I read somewhere that not assigning a GUID inhibits reference counting, but have to source to back that up.
However losing the GUID's would lead to problems in obtaining sub-interfaces so I'd have to devise a solution to that....
Can I use interfaces without invoking hidden try-finally's?
No. The compiler emits reference counting code with interfaces no matter what. You cannot avoid it.
You can implement you own version of interfaces using a record of function pointers. It will be more clunky but will avoid heap allocation and reference counting.
"Thousands of objects" always gives me a shiver. There is a significant overhead to an object in memory. You forget about it, but it pops up again when you're trying to manage thousands, or notice you loose performance on it, or start to try writing or reading from file...
Using interfaces won't change much as far as I can tell, since you still use objects (class instances) underneath.
Endeavours of this magnitude require specific use of good-old straight-to-memory data-structures. For example I've been playing with an AST stored in an array of records: https://github.com/stijnsanders/strato
Yes No you cancannot use interfaces without invoking try-finally's and refcounting.
You can however greatly reduce the number of hidden exception handlers.
You just have to be really careful to do two things.
Always use const parameters when passing interfaces.
Never store the interface in an interface type variable, but use a homebrew record to encapsulate the interface so that its refcount will not be touched.
Here's a sample of the encapsulating record:
type
TInterface<Intf: IInterface> = record
private
P: Pointer;
public
function I: Intf; inline;
class operator Implicit(const A: Intf): TInterface<Intf>; inline;
end;
function TInterface<Intf>.I: Intf;
begin
pointer(IInterface(Result)):= P;
end;
class operator TInterface<Intf>.Implicit(const A: Intf): TInterface<Intf>;
begin
Result.P:= pointer(IInterface(A));
end;
Here's a sample program to demonstrate the concept.
program Project32;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TInterface<Intf: IInterface> = record
private
P: Pointer;
public
function I: Intf; inline;
class operator Implicit(const A: Intf): TInterface<Intf>; inline;
end;
ITest1 = interface
function Test1: integer;
end;
ITest2 = interface
function Test2: integer;
end;
TTest = class(TAggregatedObject, ITest1, ITest2)
function Test1: integer;
function Test2: integer;
end;
{ TTest }
function TTest.Test1: integer;
begin
Result:= 1;
end;
function TTest.Test2: integer;
begin
Result:= 2;
end;
{ TInterface<Intf> }
function TInterface<Intf>.I: Intf;
begin
pointer(IInterface(Result)):= P;
end;
class operator TInterface<Intf>.Implicit(const A: Intf): TInterface<Intf>;
begin
Result.P:= pointer(IInterface(A));
end;
var
I1: TInterface<ITest1>;
I2: TInterface<ITest2>;
Test: TTest;
begin
Test:= TTest.Create(nil); //Force AV on _AddRef, _Release
If (Test.Test1 = 1) then WriteLn(S);
I1:= Test;
If (I1.I.Test1 =1) then WriteLn(S);
I2:= Test;
If (I2.I.Test2 = 2) then WriteLn(S);
ReadLn(s);
Test.Free;
end.
The TAggregatedObject does not have a interface to handle the _AddRef/_Release calls.
During the lifetime of the program, no problems will occur, however Delphi does wrap the creation of TTest in a try-finally which will generate an exception when exiting the function.
In real-world use you'd have to use a TInterfacedObject. If you pass the interface references around a lot it might help though.
I want to query Active Directory in an app developed with Delphi (7 and up), but do not want to include "ActiveDs_TLB" in the "uses" clause to keep the EXE size down. When querying WMI it is possible to use the IBindCtx and IMoniker interfaces to avoid linking in the type library (see How do I use WMI with Delphi without drastically increasing the application's file size? for a solution).
Is it possible to do the same when performing AD queries? I my case I want to retrieve "IADsUser" and "IADsComputer". I am aware that I can decrease the EXE size by manually copying only the required definitions from "ActiveDs_TLB" into my program or to use an LDAP query, but I would prefer a solution similar to the one described for WMI.
I'm no Active Directory expert, but I just created two D7 console applications, one accessing the WnNTSystemInfo object using the ActiveDS_TLB.Pas type library and the other using late binding do do the same thing, namely get the ComputerName from AD.
First, the late binding one:
program ActiveDSLBConsole;
{$APPTYPE CONSOLE}
uses
SysUtils, ActiveX, ComObj;
var
SI : OleVariant;
S : String;
begin
CoInitialize(Nil);
SI := CreateOleObject('WinNTSystemInfo');
S := SI.ComputerName;
writeln(S);
readln;
end.
(what took me longest writing the above was checking the registry for the name of
the object to create)
Anyway, I hope that shows that, yes, you can query AD via late binding and that this minimal example will get you started querying AD that way.
The equivalent AD console application using ActiveDS_Tlb is
program ActiveDSConsole;
{$APPTYPE CONSOLE}
uses
SysUtils, ActiveX, ActiveDS_Tlb;
var
SI : IADsWinNTSystemInfo;
S : String;
begin
CoInitialize(Nil);
SI := CoWinNTSystemInfo.Create;
S := SI.ComputerName;
writeln(S);
readln;
end.
These have .Exe sizes of
ActiveDSConsole : 390144 bytes
ActiveDSLBConsole : 87552 bytes (late bound)
So there's evidently quite a bit of code pulled in to support the use
of the tlb objects, but neither is huge.
FWIW, the above re-written as Button1Click handlers of a minimalist VCL app gives Exe sizes
of
using ActiveDS_TLB : 396288 bytes
late bound : 392704 bytes
the difference between these two seems fairly marginal to me, but there's a clear
size advantage to late binding in a minimal D7 console application. Your mileage may vary,
so probably best to "suck it and see", if you'll pardon the mixed metaphors.
Btw, late binding has the advantage that you don't always have to supply arguments for each of the parameters in an interface method. And you can call a method with this special syntax that the compiler was enhanced to allow (when automation support was added, in D2) for variants that it knows contain late-bound automation objects:
(from an MS Word late binding example)
Table := MSWord.ActiveDocument.Tables.Add(Range:= MSWord.Selection.Range, NumRows:= Rows, NumColumns:= Columns, DefaultTableBehavior:= wdWord9TableBehavior, AutoFitBehavior:= wdAutoFitFixed);
Martyn's answer filled in the missing pieces. Here's an example on how to query IADsUser using late binding:
program GetUserObjectPath;
{$APPTYPE CONSOLE}
uses SysUtils, ActiveX, ComObj;
function GetObject (const Name: WideString) : IDispatch;
var
Moniker : IMoniker;
Eaten : Integer;
BindContext : IBindCtx;
begin
OleCheck (CreateBindCtx (0, BindContext));
OleCheck (MkParseDisplayName (BindContext, PWideChar (Name), Eaten,
Moniker));
OleCheck (Moniker.BindToObject (BindContext, NIL, IDispatch, Result));
end; { GetObject }
procedure Query_AD (const sQuery: String);
var
vUser : OleVariant;
begin
vUser := GetObject (sQuery); // = IADsUser
WriteLn ('Name = ' + vUser.FullName);
end; { Query_AD }
var
sQuery, sDomain, sUserName : String;
begin
sDomain := GetEnvironmentVariable ('USERDNSDOMAIN');
sUserName := GetEnvironmentVariable ('USERNAME');
sQuery := Format ('WinNT://%s/%s,user', [sDomain, sUserName]);
CoInitialize (NIL);
try
Query_AD (sQuery);
finally
// Causes Access Violation if AD query does not happen in subroutine
CoUninitialize;
end; { try / finally }
WriteLn;
Write ('Press [Enter] to continue ...');
ReadLn;
end.
The actual AD query should happen in a subroutine (here "Query_AD"), otherwise calling "CoUninitialize" is going to lead to an access violation (see Why does CoUninitialize cause an error on exit? for an explanation).
What List type I should use to store enum values? I have tried with TObjectList, I cast to TObject to Add the value, but can't cast it back to enum when reading from the list.
What list do you use to store enums?
Casting enums to Pointer or TObject and back works just fine. If your Delphi version supports generics use Tim's suggestion, it's better. Alternatively you can use an dynamic array (array of TTestEnum) or create a wrapper class around the dynamic array - that's how generic lists are implemented in Delphi versions capable of generics.
Here's a quick console demo, using TList, not TObjectList because TList makes fewer assumptions about the items it holds.
program Project1;
{$APPTYPE CONSOLE}
uses SysUtils, Classes;
type TTestEnum = (enum1, enum2, enum3, enum4);
var L: TList;
i: Integer;
E: TTestEnum;
begin
L := TList.Create;
try
L.Add(Pointer(enum1));
L.Add(Pointer(enum2));
L.Add(Pointer(enum3));
L.Add(Pointer(enum4));
for i:=0 to L.Count-1 do
begin
E := TTestEnum(L[i]);
case E of
enum1: WriteLn('enum1');
enum2: WriteLn('enum2');
enum3: WriteLn('enum3');
enum4: WriteLn('enum4');
end;
end;
finally L.Free;
end;
ReadLn;
end.
Could you not just use Generics for this?
TList<TEnumName>;
This answer may help. It's about storing records in a TList by creating a descendant to avoid all the typecasting. Note that you won't need to worry about allocating/freeing memory for the enum values, as they're simple ordinal types that fit in the space of a pointer.
Note that you have to typecast to Pointer when Adding to the list, and may have to typecast as `YourEnum(Integer(List[Index])) when reading back. However, the code I linked to shows how to handle both in the descendant class so it's only done once each way, and that's buried in the class implementation.
Given a text string containing a type name, is there some way to get the appropriate type itself?
I'm looking to do something like this:
type
TSomeType<T> = class
// yadda yadda
end;
procedure DoSomething;
var
obj : TObject;
begin
o := TSomeType<GetTypeByName('integer')>.Create;
// do stuff with obj
end;
I've looked at several RTTI explanations online and looked through the Delphi units and don't see what I'm looking for. Is this possible?
No, generics are entirely compiletime.
The new RTTI unit in Delphi 2010 has a way of retrieving types declared in the interface section of units. For any given type, represented by a TRttiType instance, the TRttiType.QualifiedName property returns a name that can be used with TRttiContext.FindType later to retrieve the type. The qualified name is the full unit name (including namespaces, if they exist), followed by a '.', followed by the full type name (including outer types if it nested).
So, you could retrieve a representation of the Integer type (in the form of a TRttiType) with context.FindType('System.Integer').
But this mechanism can't be used to retrieve instantiations of generic types that weren't instantiated at compile time; instantiation at runtime requires runtime code generation.
You can always register your types into some sort of registry (managed by a string list or dictionary) and create a factory function to then return the appropriate object. Unfortunately you would have to know in advance what types you were going to need. Something similar to the Delphi functions RegisterClass and FindClass (in the classes unit). My thinking is to put the generic template type into the list directly.
An example of possible usage:
RegisterCustomType('Integer',TSomeType<Integer>);
RegisterCustomType('String',TSomeType<String>);
if FindCustomType('Integer') <> nil then
O := FindCustomType('Integer').Create;
EDIT: Here is a specific simple implementation using a tDictionary from Generics.Collections to handle the registry storage...I'll leave extracting this into useful methods as a simple exercise for the reader.
var
o : TObject;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) then
ShowMessage(o.ClassName);
end;
Another EDIT: I was giving this some thought last night, and discovered another technique that you can merge into this concept. Interfaces. Here is a quick do nothing example, but can easily be extended:
TYPE
ITest = interface
['{0DD03794-6713-47A0-BBE5-58F4719F494E}']
end;
TIntfList<t> = class(TList<T>,ITest)
public
function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
procedure TForm1.Button7Click(Sender: TObject);
var
o : TObject;
fTestIntf : ITest;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TIntfList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) and Supports(o,ITest,fTestIntf) then
ShowMessage(o.ClassName);
end;
of course you would have to implement the QueryInterface, _AddRef and _Release methods and extend the interface to do something more useful.
If you forget generics and basic types, the "RegisterClass" function would be helpful. But it doesn't work for generics or basic types.