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
Related
type
TCDSWithRecalc = class(TClientDataset)
public
procedure GetCalcFields(Buffer: PChar); override;
end;
procedure TCDSWithRecalc.GetCalcFields(Buffer :PChar);
begin
inherited GetCalcFields(Buffer);
end;
E2137 Method 'GetCalcFields' not found in base class.
I don't understand what this error is. can you help?
You don't say which Delphi version you are using, but the type declaration of the Buffer parameter was changed from the original (D7 and before) PChar to TRecBuf in the modern (Unicode) versions. So, you you need to change your declaration of TCDSWithRecalc
to
type
TCDSWithRecalc = class(TCustomClientDataset)
public
procedure GetCalcFields(Buffer: TRecBuf); override;
end;
[...]
procedure TCDSWithRecalc.GetCalcFields(Buffer : TRecBuf);
begin
inherited GetCalcFields(Buffer);
end;
In Delphi 10.4.2, the compiler reports an additional error with your version of the declaration
[dcc32 Error] cds1u.pas(41): E2250 There is no overloaded version of 'GetCalcFields' that can be called with these arguments
which is true.
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 trying to use procedures within a structured data type as callback functions for a program using GTK+3 as its toolkit in FreePascal. (The GTK+3 bindings I have were generated by the gir2pascal tool (http://wiki.freepascal.org/gir2pascal))
In the example below, I use advanced records, but I would definitely consider classes or objects if it works better/at all with them.
The problem that occurs is that when the callback procedure is called, it cannot access anything else within its own record. It seems to "forget" where it comes from.
For instance, in the example below I have the integer myRecord.myInt, that I can set and retrieve happily by calling the procedure myRecord.testProcedure. However when testProcedure is used as a C callback (when I click the button), I will receive some number (e.g. 30976), but not 7.
{$MODESWITCH ADVANCEDRECORDS}
uses gobject2, gtk3, math;
type
myRecord=record
public
myInt: Integer;
procedure testProcedure; cdecl;
end;
procedure myRecord.testProcedure; cdecl;
begin
WriteLn(myInt);
end;
var
recordInstance: myRecord;
button, win: PGtkWidget;
begin
SetExceptionMask([exDenormalized, exInvalidOp, exOverflow,
exPrecision, exUnderflow, exZeroDivide]); {this is needed for GTK not to crash}
gtk_init(#argc, #argv);
win:=gtk_window_new(GTK_WINDOW_TOPLEVEL);
recordInstance.myInt:=7;
button:=gtk_button_new;
{The following does not work. The procedure will run when the button is
clicked; it will print some number, but not the content of recordInstance.myInt}
g_signal_connect_data(button, 'clicked',
TGCallback(#recordInstance.testProcedure), nil, nil, 0);
{add button to window}
gtk_container_add(PGtkContainer(win), button);
gtk_widget_show_all(win);
{Test call to recordInstance.testProcedure to see that it outputs
'7' correctly}
recordInstance.testProcedure;
gtk_main;
end.
When I try to use Classes or Objects instead of an Advanced Record, I receive error messages of the kind
"<procedure variable type of procedure of object;CDecl>" to "<procedure variable type of procedure;CDecl>"
What ways are there of using a structured data type with a procedure to use as a C callback as in the example above (if any)?
class static methods are compatible with procedures. But they also have the disadvantage that they don't have a reference to the data of the object.
{$mode delphi}
type
myRecord=record
public
myInt: Integer;
class procedure testProcedure; cdecl;static;
end;
tproctype = procedure; cdecl;
class procedure myrecord.testProcedure; cdecl;static;
begin
end;
var x : tproctype;
y : myrecord;
begin
x:=y.testprocedure;
end.
compiles, but the usage is sterile, since if it maps to plain C, it doesn't have (implicit) OO properties.
I'm trying to use a class in my program.
TStack = Class
Public
constructor Create; Overload;
Procedure Add(Frm:TForm);
Procedure Remove();
Procedure Do_Close;
Private
List : Array[1..Max_Forms] of Rec;
Count : Byte;
End;
Constructor:
constructor TStack.Create;
begin
Self.Count := 0;
end;
Procedure TStack.Add(Frm:TForm);
begin
Inc(Self.Count);
List[Count].Form := #Frm;
List[Count].Width := Frm.Width;
List[Count].Height := Frm.Height;
List[Count].left := Frm.Left;
List[Count].Top := Frm.Top;
end;
I can't change value of Count variable! It cause Run-Time error : Access violation....Write of address 000001E4
What's the problem?!
FOR MORE INFORMATION:
I'm trying to store a pointer to each form in a structure like this :
Rec = Record
Form : ^TForm;
Maximized : Boolean;
Width,
Height,
left,
Top : Integer;
End;
And then
Procedure TStack.Do_Close;
var
i : integer;
MyForm : TForm;
begin
i := .....some code here.......;
MyForm := #List[i].Form;
ShowMessage('I will close '+MyForm.Caption);
MyForm.Close;
end;
AND call constructor like this to initialize 'Count':
Stack.Create;
As described in comments you are attempting to create the object like this:
var
Stack: TStack;
....
Stack.Create;
This is a classic mistake, and one that we've all made. You are calling a method on an uninitialized instance variable.
In order to instantiate a class you need to write this:
Stack := TStack.Create;
On top of that I have the following comments:
Use zero-based indexing for arrays. That's the convention everywhere in Delphi apart from anachronistic strings. And even that is changing in newer versions.
Don't use static arrays for a stack unless you have a good reason for doing so. You'll just run the risk of running out of space. Or allocating more memory than you need. Use a dynamic array.
Rather than a dynamic array, you could use TList<T>.
Even so, one wonders why you are making your own stack class when there is the perfectly good TStack<T>.
You store the address of a local variable in your stack. In TStack.Add you add #Frm into the container. As soon as TStack.Add returns, #Frm is meaningless. That's because Frm is a local variable whose life ends when the function that owns it returns. I think you want to take a copy of Frm.
Picking up item 5 in more detail, your record is declared like this:
Rec = Record
Form : ^TForm;
Maximized : Boolean;
Width,
Height,
left,
Top : Integer;
End;
It is a mistake to use ^TForm. That is a pointer to a variable holding a pointer to an object. That's two levels of indirection, one too many. You must declare the Form field to be of type TForm. I suggest you revise the way Delphi object reference variables work. Delphi classes are what is known as reference types. A variable of type TMyClass where TMyClass is class(...) is already a pointer. The language automatically de-references the pointer when you use the . operator to access members.
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;