No matter how good a new language is, there is usually a great deal of investment in existing programs. The designers of Ada realised this and provide a standard mechanism to interface with other language. The facilites provided are optional, and just which languages are supported depend on the compiler vendor. Considerably more support has been built into Ada95. Consult your compiler's reference manual for full details.
Assume you are on a Unix system and you wish to make use of the kill command. You should perform the following.
function kill( pid :in integer; sig :in integer) return integer; pragma interface(C,kill);
The first parameter to pragma interface is the language of the called routine, the second the name the routine is known by in Ada.
Another example for a package is
package MATHS is function sqrt(x:float) return float; function exp (x:float) return float; private pragma interface(fortran,sqrt); pragma interface(fortran,exp); end MATHS;
The pragma interface cannot be used with generic subprograms.
Ada95 makes extensive use of the predefined libraries to enable data type translation between Ada and foriegn languages. Together with the pragmas import, export and convention they allow Ada systems to be easily used in a multi-language environment.
The pragma Import allows the inclusion of foreign language entities within an Ada program, such as variables or procedures/functions. The code below shows an example of the use of pragma import for the Unix function read.
Pragma Import consists of three parameters,
- the language convention (only
Ada and Instrinsic must be supported)
- Ada
- Instrinsic
- C
- Fortran
- Cobol
- any other implementation defined value
- the Ada name for the object
- the foreign langauge name for the
object, as a string.
------------------------------------------------------------
procedure read( file_descriptor :in integer;
buffer :in out string;
no_bytes :in integer;
no_read : out integer) is
function read( file_descriptor :integer;
buffer :system.address;
no_bytes:integer) return integer;
pragma import(C, read, "read");
begin
no_read := read(file_descriptor,
buffer(buffer'first)'address,
no_bytes);
end;
The interface package hierachy consists of packages designed to ease the
interfacing of Ada with other langauges. The standard suggests interfaces for
C, COBOL and Fortran.
The package hierachy is
package Interfaces package Interfaces.C package Interfaces.C.Pointers package Interfaces.C.Strings package Interfaces.COBOL package Interfaces.Fortran
The packages give sufficient power to deal with most foreign language interfacing issues.
One area in which Ada has problems interfacing to other languages is functions that contain non homogenous variable length parameter lists, such as printf. Such functions are inherently type unsafe, and there is no satisfactory way to handle such situations.
Ada can, however, handle functions where the argument types are homogenous; this is achieved through the use of unconstrained array types.
A simple example would be a C function...
void something(*int[]);
it could be accessed as follows...
type vector is array(natural range <>) of integer;A larger and more complex example is given below for the Unix C function execv. The added complication is caused by the necessity of translation from Ada strings to C style character arrays (and is not necessarily as good as it could be. See the LRM for more information on using interfaces.c child family).
procedure something(item :vector) is
function C_Something(address:system.address);
pragma import(C, C_something, "something);
begin
if item'length = 0 then
C_something(system.null_address);
else
C_something(item(item'first)'address);
end if;
end;
The C function is defined as...
int execv(const char *path, char *const argv[]);
you need to declare a few things...
----------------------------------------------------------
type string_ptr is access all string;
type string_array is array(natural range <>) of string_ptr;
function execv( path :string;
arg_list :string_array) return interfaces.c.int;
--------------------------------------------------------- -- execv replaces the current image with a new one. -- A list of arguments is passed as the command line -- parameters for the called program. -- -- To call this routine you can... -- -- option2 :aliased string := "-b"; -- option3 :aliased string := "-c"; -- option4 :string := "Cxy"; -- result :interfaces.C.int; -- ... -- result := execv(path => "some_program", -- -- build up an array of string pointers... -- argv => string_array'(new string'("some_program"), -- new string'("-a"), -- option2'Unchecked_access, -- option3'Unchecked_access, -- new string'('-' & option4)); -- -- Any mixture of dynamic string allocation and -- 'Unchecked_access to aliased variables is allowed. Note however that -- you can't do "some_string"'access, as Ada requires a name, -- not a value, for the 'access attribute to be applied to. ------------------------------------------------------------
This function could be implemented as follows. Note that the address of the first item in the array is passed, not the address of the array. Arrays declared from unconstrained array types often have a vector which includes extra information such as the lower and upper bounds of the array.
function execv( path :string;
argv :string_array )return interfaces.C.int is
Package C renames Interfaces.C;
Package Strings renames Interfaces.C.Strings;
C_path :constant Strings.chars_ptr(1..path'length+1)
:= Strings.New_string(path);
type char_star_array is array(1..argv'length + 1) of
Strings.char_array_ptr;
C_argv :char_star_array;
index :integer;
result :C.int;
------------------------------------------------------------
function C_execv( path :Strings.Char_ptr;
C_arg_list:Strings.Char_ptr)
return C.int;
pragma import(C, C_execv, "execv");
------------------------------------------------------------ begin
-- set up the array of pointers to the strings
index := 0;
for i in argv'range loop
index := index + 1;
C_argv(index) := Strings.New_String(argv(i).all));
end loop;
-- append C style null to the end of the array of addresses
C_argv(C_argv'last) := Strings.Null_Ptr;
-- pass the address of the first element of each
-- parameter, as C expects.
result := C_execv( C_path(1)'address, C_argv(1)'address));
-- Free up the memory as obviously this didn't work
for i in argv'range loop
Strings.free(argv(i));
end loop;
Strings.free(C_path);
return result;
end execv;