I came across this nice code from David Heffernan, but I cannot compile it in Lazarus.
Buffered files (for faster disk access)
I get 2 distinct error messages:
line 72 and 104: Duplicate identifier CacheSize
--> I just renamed it and: CacheSize:=aCacheSize
line 53 and 78: No matching implementation for interface method QueryInterface ...
I have no idea how to mend that. I tried to create to new interface function simply calling the original function, but it doesn't work.
Help, please!
The first can be fixed by enabling delphi mode ({$mode delphi} after the interface line, -Sd on the commandline, or the relevant tick in Lazarus properties.
The second needs modifications. The "const" in QueryInterface must be changed to constref
{$ifdef fpc}
function TBaseCachedFileStream.QueryInterface(constref IID: TGUID; out Obj): HResult;
{$else}
function TBaseCachedFileStream.QueryInterface(const IID: TGUID; out Obj): HResult;
{$endif}
both in implementation and interface. This change was made because on Intel const usually implies by reference, and on other CPUs it doesn't and forcing all CONST to constref internally leads to slower code on those processors.
Try this one
In interface part:
protected
function QueryInterface({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} iid : tguid;out obj) : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
function _AddRef : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
function _Release : longint;{$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};
And in the implementation:
function TMyObject.QueryInterface(constref iid: tguid; out obj): longint;
begin
if GetInterface(iid, obj) then
Result := 0
else
Result := -1;
end;
function TMyObject._AddRef: longint;
begin
Result := InterLockedIncrement(FRefCount);
end;
function TMyObject._Release: longint;
begin
Result := InterLockedDecrement(FRefCount);
if FRefCount = 0 then
Free;
end;
Related
I have the following binding:
function atexit(proc : access Procedure) return Integer with
Import, Convention => C;
As well as the procedure:
procedure Exiting is
begin
Put_Line("Exiting");
end Exiting;
When I try to call it like:
I : Integer := atexit(Exiting'Access);
it fails with subprogram "Exited" has wrong convention
however providing my own (incompatable) atexit which accepts a parameter, and modifying Exiting to use that same parameter, allows passing the procedure just fine.
So it seems like the issue is passing a parameterless procedure as an access type.
I've tried giving a named access type like
type Procedure_Access is access Procedure;
But the result is exactly the same.
How can I pass a parameterless procedure then?
You might have forgotten the Convention aspects in the declarations of Exiting and Procedure_Access. The following works in GNAT CE 2018:
foo.c
int _atexit(void (*f)(void))
{
(*f)();
return 0;
}
main.adb
with Ada.Text_IO; use Ada.Text_IO;
with Interfaces.C; use Interfaces.C;
procedure Main is
type proc_ptr is access procedure
with Convention => C;
function atexit(proc : proc_ptr) return int
with Import, Convention => C, Link_Name => "_atexit";
procedure Exiting
with Convention => C;
procedure Exiting is
begin
Put_Line("Exiting");
end Exiting;
I : Integer := Integer (atexit (Exiting'Access));
begin
Put_Line("atexit returned " & I'Image);
end Main;
default.gpr
project Default is
for Source_Dirs use ("src");
for Object_Dir use "obj";
for Main use ("main.adb");
for Languages use ("Ada", "C");
end Default;
output
Exiting
atexit returned 0
Consider this interfaces and its implementations.
unit utest;
interface
{$MODE OBJFPC}
type
IIntfA = interface
procedure writeA();
end;
IIntfB = interface(IIntfA)
procedure writeB();
end;
TADelegateClass = class(TInterfacedObject, IIntfA)
public
procedure writeA();
end;
TAClass = class(TInterfacedObject, IIntfA)
private
delegateA : IIntfA;
public
constructor create(const AInst : IIntfA);
destructor destroy(); override;
property A : IIntfA read delegateA implements IIntfA;
end;
TBClass = class(TAClass, IIntfB)
public
procedure writeB();
end;
implementation
procedure TADelegateClass.writeA();
begin
writeln('Implement IIntfA through delegation');
end;
constructor TAClass.create(const AInst : IIntfA);
begin
delegateA := AInst;
end;
destructor TAClass.destroy();
begin
inherited destroy();
delegateA := nil;
end;
procedure TBClass.writeB();
begin
writeln('Implement IIntfB');
end;
end.
Following program will not compile.
program test;
{$MODE OBJFPC}
uses
utest;
var b : IIntfB;
begin
b := TBClass.create(TADelegateClass.create());
b.writeA();
b.writeB();
end.
Free Pascal (version 3.0.4) complains
Error: No matching implementation for interface method "writeA;" found.
at line where TBClass is declared.
Of course, I can compile it successfully by implementing writeA either in TAClass or TBClass and call writeA method of TADelegateClass from there.
TAClass is concrete implementation of IIntfA interface through interface delegation but why TBClass, which is descendant of TAClass, is not considered a concrete implementation of IIntfA interface?
TAClass is concrete implementation of IIntfA interface through
interface delegation but why TBClass, which is descendant of TAClass,
is not considered a concrete implementation of IIntfA interface?
Short answer: it's not IIntfA that is the problem, it is IIntfB that is incomplete.
Long answer: Interface inheritance is C++ vtable inheritance, which is sometimes not intuitive.
In the example:
IIntfB = interface(IIntfA)
procedure writeB();
end;
could actually be written as
IIntfB = interface
procedure writeA();
procedure writeB();
end;
When implementing multiple interfaces, common parts are not reused. The compiler sets up individual tables from the implementing methods, such as:
TADelegateClass:
QueryInterface(IIntfA) = Self.vtable_IIntfA
vtable_IIntfA.writeA <- Self.writeA
TAClass:
QueryInterface(IIntfA) = delegateA.vtable_IIntfA
TBClass:
QueryInterface(IIntfA) = inherited delegateA.vtable_IIntfA
QueryInterface(IIntfB) = vtable_IIntfB
vtable_IIntfB.writeA <- (this is missing!)
vtable_IIntfB.writeB <- Self.writeB
TBClass does indeed not have an implementation of IIntfB.writeA.
This can be verified by manually assigning a method to the specific interface and observe the error disappearing:
TBClass = class(TAClass, IIntfB)
public
procedure IIntfB.writeA = writeB;
// dummy method, shows IIntfB.writeA is missing
Sadly, I don't know of any way to tell the compiler to access a mapping from another interface. FWIW, Delphi has the same bug/shortcoming.
I want to implement something similar to an interface using Ada 95 (so the typical OO interfaces are not available). I've done it by using generics and a set of "pointer to method" within a record. The code is below.
EDIT: I know that it can be done by passing subprograms as formal parameters to the generic package, but I would like to avoid passing too many parameters to it.
I think that there must be a much better way for implementing what I want, so I would like if I'm right and, if so, I would like to see an example of code.
The "interface" is declared in a generic package called Drivers. There, there is a record which is meant to contain a variable of a generic type that represents the driver and a record which contains its operations:
drivers.ads
generic
type T is private;
type Error is private;
NOT_IMPLEMENTED_CODE : Error;
package Drivers is
type Driver is private;
-- Need to declare these types because I compile with Ada 95.
type ToStringPtr is access function(self : in T) return String;
type ReadLinePtr is access procedure(self : in T; buffer : out String; err : out Error);
type DriverOps is
record
to_string_op : ToStringPtr := null;
read_line_op : ReadLinePtr := null;
end record;
function create_driver(underlying : T; ops : DriverOps) return Driver;
function to_string(self : in Driver) return String;
procedure read_line(self : in Driver; buffer : out String; err : out Error);
private
type Driver is
record
underlying : T;
ops : DriverOps;
end record;
end Drivers;
drivers.adb
package body Drivers is
function create_driver(underlying : T; ops : DriverOps) return Driver is
begin
return (underlying, ops);
end create_driver;
function to_string(self : in Driver) return String is
begin
if self.ops.to_string_op /= null then
return self.ops.to_string_op(self.underlying);
else
return "";
end if;
end to_string;
procedure read_line(self : in Driver; buffer : out String; err : out Error) is
begin
if self.ops.read_line_op /= null then
self.ops.read_line_op(self.underlying, buffer, err);
else
err := NOT_IMPLEMENTED_CODE;
end if;
end read_line;
end Drivers;
main.adb
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings.Fixed;
with Drivers;
procedure main is
type Error is (SUCCESS, NOT_IMPLEMENTED, UNKNOWN);
type MyInt is new Integer;
function to_string(self : in MyInt) return String is
begin
return Integer'Image( Integer(self) ); --'
end to_string;
procedure read_line(self : in MyInt; buffer : out String; err : out Error) is
begin
Ada.Strings.Fixed.Move(
Target => buffer,
Source => "Lets suppose we have read this from a device" & ASCII.LF,
Pad => ASCII.NUL);
err := SUCCESS;
end read_line;
package IntDrivers is new Drivers(MyInt, Error, NOT_IMPLEMENTED);
use IntDrivers;
underlying : MyInt := 25;
int_driver_ops : DriverOps := (
to_string_op => to_string'access, --'
read_line_op => read_line'access --'
);
my_driver : Driver := create_driver(underlying, int_driver_ops);
buffer : String(1..256) := (others => Character'Val(0)); --'
err : Error := SUCCESS;
begin
Put_Line(to_string(my_driver));
read_line(my_driver, buffer, err);
Put(buffer);
Put_Line(Error'Image(err)); --'
end main;
The only one I known of is described below, and may not be canonical. This is not strictly interface inheritance, but it can put you in the right direction.
It requires to use a discriminant tagged record.
The trick is to define 2 tagged types. One is your classic class definition, the other is used as "interface" inheritance.
You can then manipulate an object that gives access to the interface contract and the class contract using discriminants. Declaring both in the same package should give you full visibility over private parts, to be confirmed.
In short :
type InterfaceX is abstract ....; -- abstract class and services
type ClassA is tagged ...; -- or is new ....
type Trick (component : ClassA) is new InterfaceX ...; -- this type gives you access to classA and interfaceX primitives
Trick object realizes your InterfaceX contract.
You will have to define instantiaton/accessors to either ClassA object or the Trick object. I think types should also be limited.
I always hear people call this "Rosen trick", guess it is named after J.-P. Rosen.
Maybe you will find some more precise answers here http://www.adaic.org/resources/add_content/standards/95rat/rat95html/rat95-p2-4.html#6
An interface is an abstract tagged null record in Ada 95:
package Abstract_Driver is
type Instance is abstract tagged null record;
subtype Class is Instance'Class; --' (defect syntax highlighter)
function Image (Item : in Instance) return String is abstract;
procedure Read_Line (Item : in out Instance;
Buffer : out String) is abstract;
end Abstract_Driver;
with Abstract_Driver;
package Text_IO_Driver is
subtype Parent is Abstract_Driver.Instance;
type Instance is new Parent with private;
subtype Class is Instance'Class; --' (defect syntax highlighter)
function Image (Item : in Instance) return String;
Buffer_Too_Small : exception;
procedure Read_Line (Item : in out Instance;
Buffer : out String);
private
type Instance is new Parent with null record;
end Text_IO_Driver;
with Ada.Text_IO;
package body Text_IO_Driver is
function Image (Item : in Instance) return String is
begin
return "Ada.Text_IO.Standard_Input";
end Image;
procedure Read_Line (Item : in out Instance;
Buffer : out String) is
Last : Natural;
begin
Buffer := (Buffer'Range => ' '); --' (defect syntax highlighter)
Ada.Text_IO.Get_Line (Item => Buffer,
Last => Last);
if Last = Buffer'Last then --' (defect syntax highlighter)
raise Buffer_Too_Small;
end if;
end Read_Line;
end Text_IO_Driver;
I'm developing a component that works on several classes.
In order to allow adding the list of managed classes, I've written a TCollection's inherited class in which each item (inherited from TCollectionItem) defines a published "TargetClassName" property.
The "TargetClassName" property's setter function, calls the following function in order to find the corrisponding TClass:
function FindAnyClass(const Name: string): TClass;
var
ctx: TRttiContext;
typ: TRttiType;
list: TArray<TRttiType>;
begin
Result := nil;
ctx := TRttiContext.Create;
list := ctx.GetTypes;
for typ in list do
begin
if typ.IsInstance and (EndsText(Name, typ.Name)) then
begin
Result := typ.AsInstance.MetaClassType;
break;
end;
end;
ctx.Free;
end;
(Thanks to Dalija Prasnikar for writing the function Get class by its name in Delphi).
Now, I'm wondering if there's a better way to allow adding classes to a TCollectionItem at design time.. What do you think about it?
Hope to read interesting solutions!
Thanks to all.
in creation on TCollection You need To introduce Collation Class
it's Posible in two way
1 : hard coded in create time X := TMycollation.Create(TMyCollationClass)
2 : your solution X := TMycollation.Create(FindAnyClass('TMyCollationClass'));
This may prove difficult -- I'm using multiple CORBA interfaces on an object, so it looks somehow like this:
TBaseObject = class(TSuperBaseObject, IInterfaceA)
function Afunction; // implemented from IInterfaceA
end;
TOtherObject = class(TBaseObject, IInterfaceB);
function Bfunction; // implemented from IInterfaceB
end;
Now I have a function that takes a variant, and in case that variant is an object, it assumes that object to be a IInterfaceA object:
case var.vtype of
...
vtObject : begin
Something := (var.vObject as IInterfaceA).AFunction; (1)
end;
end;
Now once I run that code, and pass a TOtherObject to the function, in line (1) BFunction gets called with forced parameters!
Am I doing something wrong or is it a bug in the compiler? Also, any sane way to circumvent that without changing the class structure?
Full code for a EAccessViolation if anyone wants to try - http://pastebin.com/D7sDpDHx
Reported this as a bug to the FPC bugtracker - http://bugs.freepascal.org/view.php?id=20076
It turned out that FPC doesn't identify CORBA interfaces internally. To solve the problem one needs to identify them by himself:
type IInterfaceA = interface['interface_a']
function AFunction;
end;
Then the as keyword will work.
Not sure about FreePascal, but in Delphi you would use the supports function to query the interface.
var
IntfA : IInterfaceA;
IntfB : IInterfaceB;
begin
case var.vtype of
...
vtObject : begin
if supports(var.vObject,IInterfaceA,IntfA) then
Something := IntfA.AFunction
else if supports(var.vObject,IInterfaceB,IntfB) then
Something := IntfB.BFunction;
end;
end;
end;