I created a class method in a class that implements an interface, but I can't seem to define it inside of the interface.
IMyClass = interface
procedure someproc();
class function myfunc() : TMyClass; // compiler doesn't like this!
end;
TMyClass = class( TInterfacedObject, IMyClass )
public
procedure someproc();
class function myfunc() : TMyClass;
end;
I want myfunc() to create and return an instance of TMyClass. For example:
somefunc( TMyClass.myfunc(), ... );
creates an instance of TMyClass and passes it into somefunc.
I can define function myfunc() : TMyClass; in the IMyClass interface, but if I put class in front of it, the compiler gives me an error. If I leave it off, it gives me several other errors "E2291 Missing implementation of interface method xyz.myfunc" It just doesn't accept the same signature in the interface as in the class.
I thought I've seen this work before (class methods defined in interfaces) but maybe not.
If this isn't supported directly, how do you do it?
(I'm using Delphi XE5, in case it matters.)
Interfaces are not classes and do not support methods marked as class, they only support non-class methods that are implemented and called on object instances.
What you are looking for will most likely have to be implemented as a class factory instead of using an interface.
What you are trying to achieve is impossible because it violates binary specifications of Delphi interfaces.
Any interface method in Delphi have hidden first parameter - pointer to an instance. Instance methods have the same hidden parameter (Self, the same pointer to an instance) and because of this they are binary compatible with interface methods.
On the other hand the hidden parameter of a class method is a pointer to the class vtable, so Delphi class methods are binary incompartible with Delphi interface methods.
Hypothetically you could think of the following (binary compatible) declarations:
IMyClass = interface
procedure someproc();
function myfunc(): TMyClass;
end;
TMyClass = class( TInterfacedObject, IMyClass )
public
procedure someproc();
class function myfunc(Inst: TMyClass) : TMyClass; static;
end;
Here I use static specifier to remove the hidden parameter (class vtable pointer) we don't need and add the instance pointer parameter explicitely; but the compiler does support such syntax because the above is essentially the same as a simpler
IMyClass = interface
procedure someproc();
function myfunc(): TMyClass;
end;
TMyClass = class( TInterfacedObject, IMyClass )
public
procedure someproc();
function myfunc() : TMyClass;
end;
You can define a class method (class procedure in Delphi) in a class definition (TMyClass) but not in an interface (IMyClass ).
Related
I have written this simple code:
type
TTestA = class
strict protected
a: integer;
public
f: integer;
constructor Create(x: Integer);
end;
type
TTestB = class(TTestA)
strict private
c: integer;
end;
I use strict because these classes are in the same unit as the TForm1 class. Since a is protected by definition, it should be accessible only in subclasses, but then why this code doesn't work?
procedure TForm1.Button1Click(Sender: TObject);
var
K: TTestB;
begin
k := TTestB.Create(3);
value = k.a; //I cannot access a
end;
Also, the protected can be useful to create an abstract class. In C++, if I declare a constructor as protected, I cannot create an instance of the object and only subclasses can. Can Delphi do this?
I am having the same problem with the variable and the constructor.
protected access means "accessible in the class and any sub-class".
This means that a in your example will be accessible to methods in class TTestB but this does not extend to consumers of instances of TTestB (or TTestA).
The code in TForm1 is part of a class which is not a subclass of TTestA.
Put another way, TForm1 does not inherit from TTestA and therefore cannot access any private or protected members of k.
The answer is in your own question:
Since a is protected by definition it should be accessible only in subclasses
Regardless of the fact that TForm1 uses a local k variable of type TTestB, since TForm1 itself is not a subclass of TTestA, it does not have access to the k.a member. TTestB is a subclass of TTestA, so internally it has access to a.
This is explained in more detail in Embarcadero's documentation:
Classes and Objects (Delphi): Visibility of Class Members
I need to create a Class like this
unit Classname;
interface
type
TSomeClass = class
public
member1 : SomeType;
member2 : AnotherDataType;
....
function SomeFunction: SomeType; stadcall;external 'ExternalDll.dll';
end;
Can it be done?
How can I declare a external function (stored in a c dll) from a Class in Delphi
I'm using Delphi 7.
Thanks in advance
External functions cannot be part of a class declaration. You need to declare the external function with global scope and arrange that the class's methods then call that external function.
You could create a class with pseudo methods like this:
type
TMyDllClass = class
public
NameOfDllEntryPoint: TFunctionTypeOfDllEntryPoint;
constructor Create;
end;
constructor TMyDllClass.Create;
begin
inherited Create;
LibHandle := LoadLibrary('path\to.dll');
#NameOfDllEntryPoint := GetProcAddress(LibHandle, 'NameOfDllEntryPoint');
end;
MyDllClass := TMyDllClass.Create;
MyDllClass.NameOfDllEntryPoint(some, parameters);
This works fine, I have done it multiple times, e.g. for mitab.dll, libjpeg_turbo.dll and other dlls. The idea is to reduce cluttering the name space with global functions.
Note: For clarity I left out any error handling (e.g. if the DLL cannot be loaded or doesn't have the entry point). In this example this would result in an access violation because the NameOfDllEntryPoint field would not have been initialized.
I have a class that I want to keep private, because I only use it in the implementation section.
However it is used by a class that is declared public in the interface section.
Is there a way to do something like this:
unit x;
interface
type
TPrivate = class; //forward declaration
TPublic = class(TSomething)
private
FPrivate: TPrivate;
procedure DoStuffWithFPrivate;
public
//...
end;
implementation
type
TPrivate = class(TObject)
procedure Test;
end;
Obviously the above code gives an error:
[dcc32 Error] UnitX.pas(27): E2086 Type 'TPrivate' is not yet completely defined
I don't want to resort to cheap tricks like:
FPrivate = TObject
....
procedure TPublic.DoStuffWithFPrivate;
begin
TPrivate(FPrivate).Test;
Is there a way to do what I want without having to spill TPrivate's internal details in the interface?
I know it's possible to declare TPrivate as a strict private sub type of TPublic, however I don't like the pollution of the interface section this gives.
Is there a way to keep TPrivate out the of interface section (as much as possible) whilst maintaining type safety?
As long as TPrivate is not used anywhere in the interface of TPublic besides the field declaration (f.i. as a methods parameter type) you can use a local class helper to achieve this.
Note: FPrivate is not a good name for that field!
interface
type
THiddenActual = class
end;
TPublic = class
private
FActual: THiddenActual;
procedure DoStuffWithFPrivate;
public
end;
implementation
type
TActual = class(THiddenActual)
public
procedure Foo;
end;
type
TPublicHelper = class helper for TPublic
private
function GetActual: TActual;
procedure SetActual(const Value: TActual);
public
property Actual: TActual read GetActual write SetActual;
end;
procedure TActual.Foo;
begin
end;
function TPublicHelper.GetActual: TActual;
begin
Result := FActual as TActual;
end;
procedure TPublicHelper.SetActual(const Value: TActual);
begin
FActual := Value;
end;
procedure TPublic.DoStuffWithFPrivate;
begin
Actual.Foo;
end;
OK, it is merely a little variance of that cheap trick, but what are the alternatives? You have to take what is available, don't you?
I'm not sure what exactly your definition of "pollution" is as regards the interface section, but if it's just a matter of keeping it from drowning your TPublic class definition with noise then one option might be simply inheritance :
TBasePublic = class(TSomething)
private
type
TPrivate = class
// ...
// ... keep TPrivate definition separate
end;
end;
TPublic = class(TBasePublic)
private
FPrivate : TPrivate;
end;
This would even allow you to define TPrivate in a completely different unit if segregation is your goal.
I am trying to write a basic factory method to return a generic interfaced class.
interface
type
IGenInterface<T> = interface
function TestGet:T;
end;
TBuilder<T> = class
class function Build: IGenInterface<T>;
end;
TBuilder = class
class function Build<T>: IGenInterface<T>;
end;
implementation
type
TInterfaceImpl<T> = class(TInterfacedObject, IGenInterface<T>)
function TestGet:T;
end;
{ TBuilder }
class function TBuilder.Build<T>: IGenInterface<T>;
begin
result := TInterfaceImpl<T>.create;
end;
{ TInterfaceImpl<T> }
function TInterfaceImpl<T>.TestGet: T;
begin
end;
It looks simple enough, and I'm sure I've written similar code before, but as soon as I try to compile I get E2506: Method of parameterized type declared in interface section must not use local symbol '.TInterfaceImpl` 1'. Neither flavour of TBuilder work, both failing with the same error.
Now I'm not sure where the . and 1 are coming from. In my 'real' code, the . isn't there, but the ` 1 is.
I've had a look at the other two SO questions that reference this error, but I'm not using any constants or assigning variables (other than the function return) nor do I have any class vars.
Does anyone have a way to do this without having to move a lot of code into my interface?
The issue relates to an implementation detail of generics. When you come to instantiate the generic type in a different unit it needs to see the TInterfaceImpl<T> type in that other unit. But the compiler cannot see it because it is in the implementation section of a different unit. So the compiler objects, as you have observed.
The simplest fix is to move TInterfaceImpl<T> to be a private type declared inside one of the types declared in the interface section.
type
TBuilder = class
private
type
TInterfaceImpl<T> = class(TInterfacedObject, IGenInterface<T>)
public
function TestGet: T;
end;
public
class function Build<T>: IGenInterface<T>;
end;
Or inside the other class:
type
TBuilder<T> = class
private
type
TInterfaceImpl = class(TInterfacedObject, IGenInterface<T>)
public
function TestGet: T;
end;
public
class function Build: IGenInterface<T>;
end;
I am trying to implement an interface to convert records in a dataset to Delphi records in a pre-generics version of Delphi. I don't like the interface at the moment, as it will always need calls to Supports which I'd like to avoid if possible and was wondering if there's a better way of doing it that I'm missing.
So far I have an navigation interface and data retrieval interface defined:
IBaseRecordCollection = interface
procedure First;
procedure Next;
function BOF: boolean;
... // other dataset nav stuff
end;
IRecARecordCollection = interface
function GetRec: TRecA;
end;
IRecBRecordCollection = interface
function GetRec: TRecB;
end;
Basically I have a concrete base class that contains a private dataset and implements IBaseRecordCollection and concrete class for each RecordCollection interface which derives from an abstract class implementing the IBaseRecordCollection (handled by an implements property) with the implementation of the record retrieval routine:
TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
FCollection: IBaseRecordCollection;
protected
property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
constructor Create(aRecordCollection: IBaseRecordCollection);
end;
TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRecARecordCollection);
public
function GetRec: TRecA;
end;
Now, to use this I'm forced to have a builder that returns a IRecARecordCollection and then mess around with Supports, which I'm not keen on as it will always be used in this fashion.
i.e.
procedure GetMyRecASet;
var
lRecARecordCollection: IRecARecordCollection;
lRecordCollection: IBaseRecordCollection;
begin
lRecARecordCollection := BuildRecACollection;
if not supports(lRecARecordCollection, IBaseRecordCollection, lRecordCollection) then
raise exception.create();
while not lRecordCollection.EOF do
begin
lRecARecordCollection.GetRec.DoStuff;
lRecordCollection.Next;
end;
end;
Although this works, I'm not keen on the supports call and mixing my lRecordCollections and my lRecARecordCollections like this. I had originally hoped to be able to do something like:
IBaseRecordCollection = interface
// DBNav stuff
end;
IRecARecordCollection = interface (IBaseRecordCollection)
function GetRec: TRecA;
end;
TRec1RecordCollection = class(TInterfacedObject, IRecARecordCollection)
private
FCollection: IBaseRecordCollection;
protected
property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
function GetRec: TRecA;
end;
but unfortunately Delphi wasn't smart enough to realise that the implementation of IRecARecordCollection was split over the base IBaseRecordCollection in the Collection property implements call and the TRec1RecordCollection object.
Are there any other suggestions for neater ways to acheive this?
-- edit to give a (longer) reply to #David's answer than possible in a comment
The suggested solution of:
IBaseRecordCollection = interface ['{C910BD0A-26F4-4682-BC82-605C4C8F9173}']
function GetRecNo: integer;
function GetRecCount: integer;
function GetFieldList: TFieldList;
function EOF: boolean;
function BOF: boolean;
...
end;
IRec1RecordCollection = interface (IBaseRecordCollection) ['{E12F9F6D-6D57-4C7D-AB87-8DD50D35DCA2}']
function GetRec: TRec1;
property Rec: TRec1 read GetRec;
end;
TAbstractTypedRecordCollection = class(TInterfacedObject, IBaseRecordCollection)
private
FCollection: IBaseRecordCollection;
protected
property Collection: IBaseRecordCollection read FCollection implements IBaseRecordCollection;
public
constructor Create(aRecordCollection: IBaseRecordCollection);
end;
TRec1RecordCollection = class(TAbstractTypedRecordCollection, IRec1RecordCollection, IBaseRecordCollection)
private
function GetRec: TRec1;
public
property Rec: TRec1 read GetRec;
end;
isn't compiling. It's complaining that TRec1RecordCollection cannot find methods related to IBaseRecordCollection. I also tried moving the Collection property from Abstract to Rec1RecordCollection and redeclaring the property in TRec1RecordCollection all with the same result
Looking a bit deeper it appears that direct inheritance of a class implementing IBaseRecordCollection would work but Delphi can't handle doing it indirectly via a property using implements.
Your code is almost there. The implements directive in your code fails to compile because you only declared that your class implements the derived interface. As it stands, your class does not implement the interface that the implements directive refers to, namely IBaseRecordCollection. You might think that would be inferred from the inheritance but it is not.
To solve your problem you simply need to declare that TRec1RecordCollection implements both interfaces:
type
TRec1RecordCollection = class(TInterfacedObject, IBaseRecordCollection,
IRecARecordCollection)
....
end;
Make just the one small change and your code will compile.
Update
Your edit to the question changes this somewhat. The code in my answer does indeed compile, given the code in your original question. However, add any method into IBaseRecordCollection and the compile will not accept it.
The compiler should accept this code and the fact that it does not is because of a compiler bug. Modern versions of Delphi will accept the code in your update to the question.
Unless you upgrade your compiler you will not be able to make your intended design work.