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:
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;
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;
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