Generic method in property write - delphi

I came up with the following for an easy to expand "bit bucket":
unit BitBucket;
interface
type TBitBucket = class
private
class procedure ThrowAway<T>(value: T); static;
public
class property Integer: Integer write ThrowAway;
class property String_: String write ThrowAway;
class property Extended: Extended write ThrowAway;
class property Boolean: Boolean write ThrowAway;
end;
implementation
class procedure TBitBucket.ThrowAway<T>(value: T);
begin
end;
end.
However, although there's no squiggly underlines in the IDE, it won't compile, with the following errors:
[dcc32 Error] BitBucket.pas(9): E2008 Incompatible types
[dcc32 Error] BitBucket.pas(10): E2008 Incompatible types
[dcc32 Error] BitBucket.pas(11): E2008 Incompatible types
[dcc32 Error] BitBucket.pas(12): E2008 Incompatible types
Is there a trick I'm missing that will make this compile? I've tried specifying the generic type argument to ThrowAway, but that causes even more errors. The obvious alternative is to write a ThrowAway method for every type, but that would quickly lead to a lot of code to do effectively nothing.
For those wondering why, in delphi, you can use a compiler switch to prevent use of functions without assigning their return value for compatibility with older code. With a BitBucket you can say BitBucket.Integer := FunctionThatHasSideEffectsAndReturnsAnInteger(...);, without having to create a new variable. I also think it's just funny.

You are confusing a generic with a variant. You need something like this:
unit BitBucket;
interface
type
TBitBucket<T> = class
private
class procedure ThrowAway(const Value: T); static;
class var FVar: T;
public
class property MyProperty: T read FVar write ThrowAway;
end;
implementation
class procedure TBitBucket<T>.ThrowAway(const Value: T);
begin
FVar := Value;
end;
end.
The type is not decided until runtime when you access it i.e
TBitBucket<Integer>.MyProperty := 2;

This is the shortest solution i could come up with
type TBitBucket = class
class var ThrowAway: variant;
end;
Usage
type test = class
procedure Test;
end;
implementation
{ test }
procedure test.Test;
begin
TBitBucket.ThrowAway := 'AString';
TBitBucket.ThrowAway := 1;
TBitBucket.ThrowAway := 1.1234;
TBitBucket.ThrowAway := true;
end;
Tvalue example
And here a example with TValue instead from system.RTTI allowing to put objects into the bucket
type TBitBucket = class
class var ThrowAway: Tvalue;
end;
type test = class
procedure Test;
end;
implementation
{ test }
procedure test.Test;
begin
TBitBucket.ThrowAway := 'AString';
TBitBucket.ThrowAway := 1;
TBitBucket.ThrowAway := 1.1234;
TBitBucket.ThrowAway := true;
TBitBucket.ThrowAway := TObject.Create;
end;

Related

"Abstract Error" shows up when calling methods from child classes

I want to instantiate classes based on a parameter. Both classes are derived from TSample so I define my code as:
var T: TSample;
then I do
T := TMySample.Create;
or
T := TYourSample.Create;
and calling T.Hello gives an "Abstract Error".
type TSample = class
public
procedure Hello; virtual; abstract;
end;
TMySample = class(TSample)
public
procedure Hello;
end;
TYourSample = class(TSample)
public
procedure Hello;
end;
...
procedure TForm1.Button1Click(Sender: TObject);
var T: TSample;
a: Integer;
begin
if a = 1 then T := TMySample.Create
else T := TYourSample.Create;
T.Hello; //Abstract Error here
T.Free;
end;
procedure TMySample.Hello;
begin
showmessage('My');
end;
procedure TYourSample.Hello;
begin
showmessage('Your');
end;
You forgot to declare the overridden methods as, well, overridden:
TMySample = class(TSample)
public
procedure Hello; override; // <--
end;
TYourSample = class(TSample)
public
procedure Hello; override; // <--
end;
Actually, the compiler warned you about this, but you didn't listen :)
[dcc32 Warning] Unit1.pas(25): W1010 Method 'Hello' hides virtual method of base type 'TSample'
[dcc32 Warning] Unit1.pas(30): W1010 Method 'Hello' hides virtual method of base type 'TSample'
Also, you probably already know this, but there are two issues with your sample code:
Since local variables of non-managed types are not initialized, the value of a is undefined.
You don't protect the TSample object, so you might leak resources. (In fact, in this case, you will due to the exception!)
Fixed:
a := 123;
if a = 1 then
T := TMySample.Create
else
T := TYourSample.Create;
try
T.Hello; //Abstract Error here
finally
T.Free;
end;

Class function/procedure and instance function/procedure with the same name

Consider this class:
unit u_myclass;
interface
type
TMyClass = class
public
class function Foo : Integer;
function Foo : Integer;
end;
implementation
{ TMyClass }
class function TMyClass.Foo: Integer;
begin
Result := 10;
end;
function TMyClass.Foo: Integer;
begin
Result := 1;
end;
end.
I want to use a class function and an instance function with the same name.
Sadly Delphi doesn't like this and the compiler barfs these errors:
[DCC Error] u_myclass.pas(9): E2252 Method 'Foo' with identical parameters already exists
[DCC Error] u_myclass.pas(20): E2037 Declaration of 'Foo' differs from previous declaration
[DCC Error] u_myclass.pas(9): E2065 Unsatisfied forward or external declaration: 'TMyClass.Foo'
My Question: is this possible or is this simply a language limitation (And I need to rename one of the 2 methods)?
It's not possible to give use the same name for an instance method and a class method. That this is not allowed is that the compiler cannot distinguish between them in some scenarios.
For instance, if you write:
procedure TMyClass.Bar;
begin
Foo;
end;
then the compiler cannot determine whether or not you wish to call the class method or the instance method.
The only solution I found is to use overload and different parameters:
unit u_myclass;
interface
type
TMyClass = class
public
class function Foo(A : Integer) : Integer; overload;
function Foo : Integer; overload;
end;
implementation
{ TMyClass }
class function TMyClass.Foo(A: Integer): Integer;
begin
Result := A;
end;
function TMyClass.Foo: Integer;
begin
Result := 1;
end;
end.

How i can determine if an abstract method is implemented?

I'm using a very large delphi third party library without source code, this library has several classes with abstract methods. I need to determine when an abtract method is implemented by a Descendant class in runtime to avoid the EAbstractError: Abstract Error and shows a custom message to the user or use another class instead.
for example in this code I want to check in runtime if the MyAbstractMethod is implemented.
type
TMyBaseClass = class
public
procedure MyAbstractMethod; virtual; abstract;
end;
TDescendantBase = class(TMyBaseClass)
public
end;
TChild = class(TDescendantBase)
public
procedure MyAbstractMethod; override;
end;
TChild2 = class(TDescendantBase)
end;
How I can determine if an abstract method is implemented in a Descendant class in runtime?
you can use the Rtti, the GetDeclaredMethods function get a list of all the methods that are declared in the reflected (current) type. So you can check if the method is present in the list returned by this function.
function MethodIsImplemented(const AClass:TClass;MethodName : string): Boolean;
var
m : TRttiMethod;
begin
Result := False;
for m in TRttiContext.Create.GetType(AClass.ClassInfo).GetDeclaredMethods do
begin
Result := CompareText(m.Name, MethodName)=0;
if Result then
break;
end;
end;
or you can compare the Parent.Name property of the TRttiMethod and check if match with the current class name.
function MethodIsImplemented(const AClass:TClass;MethodName : string): Boolean;
var
m : TRttiMethod;
begin
Result := False;
m:=TRttiContext.Create.GetType(AClass.ClassInfo).GetMethod(MethodName);
if m<>nil then
Result:=CompareText(AClass.ClassName,m.Parent.Name)=0;
end;
function ImplementsAbstractMethod(AObj: TMyBaseClass): Boolean;
type
TAbstractMethod = procedure of object;
var
BaseClass: TClass;
BaseImpl, Impl: TAbstractMethod;
begin
BaseClass := TMyBaseClass;
BaseImpl := TMyBaseClass(#BaseClass).MyAbstractMethod;
Impl := AObj.MyAbstractMethod;
Result := TMethod(Impl).Code <> TMethod(BaseImpl).Code;
end;
Look at the implementation of the 32-bit version of the TStream.Seek() method in the VCL source code (in Classes.pas). It performs a check to make sure the 64-bit version of Seek() has been overridden before calling it. It doesn't involve TRttiContext lookups to do that, just a simple loop through its Parent/Child VTable entries, similar to how Zoƫ's answer shows.

Problem with generics and interfaces and inheritance

The following example is giving me this error:
[DCC Error] Unit2.pas(54): E2010 Incompatible types: 'IBar' and 'Unit2.TFoo<Unit2.IBar>'
I think the problem is somewhere around the Self.Create
Because after many tries to get it compiled I accidentally entered FFoo := TBar(Self).Create; and it compiled and worked.
I'm using Delphi XE
IFoo = interface
end;
TFoo<T: IInterface> = class(TInterfacedObject, IFoo)
private class var
FFoo: T;
public class
function Instance: T;
end;
IBar = interface(IFoo)
end;
TBar = class(TFoo<IBar>, IBar)
end;
class function TFoo<T>.Instance: T;
begin
if not Assigned(FFoo) then
begin
FFoo := Self.Create;
end;
Result := FFoo;
end;
The problem is in this line with the TBar declaration:
FFoo := Self.Create;
To understand, let's explain the types behind the code [noted like this]:
FFoo:[IBar] := Self:[TFoo(IBar)].Create():[TFoo<IBar>]
So, tu summarize, we have : [IBar] := [TFoo<IBar>]
Are these types compatible ?
A [TFoo] only implements IFoo interface, no IBar as it is stated in your code
TFoo<T: IInterface> = class(TInterfacedObject, IFoo)
This is the compilation error !
UPDATE : Solution 1
To fix the issue : change the TBar declaration
TBar = class(TFoo<IFoo>, IBar)
end;
UPDATE : Solution 2
Replace the FFoo := Self.Create by
FFoo := Self.Create.Instance;
and so it works !
Your TFoo does not implement T as interface. That's why FFoo and an instance of TFoo is not compatible. If you want to assign an instance of TFoo to FFoo you need to hardcast it.

Generic TypeIdenitifier convertion.How?

How do I convert the TypeIdenitifier to a class type? I need to use implicit convertion.
type
TMyChildArray<T>=class(TMyArray<T>)
private
FData:Array of T;
procedure AddEnd();
end;
TTypeIdenitifierParentClass=class(TAnotherParentClass)
protected
TestField:Cardinal;
end;
procedure TMyChildArray<T>.AddEnd();
var elem:T;
begin
for elem in Fdata do
TTypeIdenitifierParentClass(elem).TestField:=0;
end;
I get "Invalid typecast" on the implicit convertion "TTypeIdenitifierParentClass(elem).TestField:=0;".
The principle I want to use is that the TypeIdenitifier will represent a class that descends from TTypeIdenitifierParentClass.There are many class types,but all of them descend that class.
How do I do this?
The reason delphi is complaining about the cast is because the compiler has no way of knowing if T can be type casted to "TTypeIdenitifierParentClass". You need to limit T to classes descending from "TTypeIdenitifierParentClass"
Try the following
type
TTypeIdenitifierParentClass=class(TAnotherParentClass)
protected
TestField:Cardinal;
end;
TMyChildArray<T: TTypeIdenitifierParentClass>=class(TMyArray<T>)
private
FData:Array of T;
procedure AddEnd();
end;
procedure TMyChildArray<T>.AddEnd();
var elem:T;
begin
for elem in Fdata do
elem.TestField:=0;
end;

Resources