Jump to content

Ada Programming/Interfacing

From Wikibooks, open books for an open world

Ada. Time-tested, safe and secure.
Ada. Time-tested, safe and secure.

Interfacing

[edit | edit source]

Ada is one of the few languages where interfacing is part of the language standard. The programmer can interface with other programming languages, or with the hardware.

Other programming languages

[edit | edit source]

The language standard defines the interfaces for C, Cobol and Fortran. Of course any implementation might define further interfaces — GNAT for example defines an interface to C++.

Interfacing with other languages is actually provided by pragma Export, Import and Convention.

Interfacing with C

[edit | edit source]

Calling a C function from Ada

[edit | edit source]

The simplest interfacing with C is to call a function without parameters. A C function can be used in Ada by creating a "wrapper": an Ada function declaration that is used to represent the C function and its parameters.

For example, suppose the given C function say_hello:

 void say_hello() { printf("Hello from C\n"); } 

A wrapper in Ada for this function can be declared as follows:

procedure Say_Hello; pragmaImport (C, Say_Hello, "say_hello"); 

Another way to declare the same procedure using Aspects in Ada 2012 style:

procedure Say_Hello withImport =>  True, Convention =>  C, External_Name =>  "say_hello"; 

Both states that Say_Hello procedure is imported under the C convention, from a function called "say_hello". The External_Name aspect is optional if the name of the function is the same in both languages. Thus, the following will also work, because the procedure name is converted to a C function name down-cased to "say_hello" automatically:

procedure Say_Hello withImport =>  True, Convention =>  C; 

In the following example, the Ada main procedure is C_Test in the c_test.adb file. The program prints "Hello from Ada" to standard output first, then call say_hello from C, and then prints "Again from Ada".

File: c_test.adb (view, plain text, download page, browse all)
withAda.Text_IO; useAda.Text_IO; procedure C_Test isprocedure Say_Hello; pragmaImport (C, Say_Hello, "say_hello"); begin Put_Line ("Hello from Ada"); Say_Hello; Put_Line ("Again from Ada"); end C_Test; 

The hello.c file contains the "say_hello" function declaration and implementation. To use printf, the stdio.h library is required.

 #include <stdio.h> void say_hello() { printf("Hello from C\n"); } 

To compile both files the compiler must create the object (*.o) files, and then the linker must used them as input to produce the executable. In GCC/GNAT this can be accomplished by the following terminal instructions:

 gcc -c -o hello.o hello.c gnatmake c_test.adb -largs hello.o 

The first statement compiles hello.c into an object file hello.o. The second one, compiles the c_test.adb generating its object file, and links it with hello.o. The "-largs hello.o" is a parameter passed directly to the linker.

Another tool to compile is gprbuild. It requires a project file. The following can compile the project creating the main program c_test.

 project Ctest is for Source_Dirs use ("."); for Main use ("c_test.adb"); for Languages use ("Ada", "C"); end Ctest; 

To run gprbuild and start compilation:

 gprbuild -Pctest.gpr 

Interfacing in general

[edit | edit source]

The package Interfaces.C is used to define C types. C function wrappers should be used to encapsulate types and functions on the C side. This way the code is portable and forward-compatible. This is similar to the way of interfacing with C in Java's JNI. Wrappers should be used for:

  • Translating typedefs defined in C includes to types defined in Interfaces.C;
  • Using macros and exposing macro values to the Ada side;
  • Using variable parameter list functions;
  • Defining multiple function wrappers for a function that takes weakly typed parameters such as a function that takes a char_array or a null pointer;
  • Using getters and setters for C structs that depend on operating system version or other compile-type aspect;
  • Using pointer arithmetic;
  • Keeping Ada source memory-safe.

Importing C variables in Ada

[edit | edit source]

Variables can be imported from C to Ada programs. The package Interfaces.C defines several types to contain the data from C. Therefore, to use any variable defined in C in Ada, a wrapper counterpart must be declared with the Import pragma or Import =>  aspect.

For example, a C integer variable int my_number = 10 can be declared in Ada with:

withInterfaces.C; ... My_Number : Interfaces.C.int withImport =>  True, Convention =>  C, External_Name =>  "my_number"; 

The following table shows the types in C and its corresponding Ada type. The first column shows different C data types, the second its interfaces intended to store the same data. The third column shows the Ada data type defined in the standard intended for the same use.

No convertion in numbers are necessarily required. The Interfaces.C package defines them as scalars types, which inherits several attributes and operations from other scalars. This feature allows to use numbers variables as usual. However, other types (mostly characters and strings) my require a function to convert. In this case, the function is usually called To_Ada (...) to convert from interface types to Ada, and To_C (...) from Ada to interface types.

Types in Ada used to store C data.
CAda interfaceAda
intInterfaces.C.intInteger
shortInterfaces.C.shortShort_Integer
longInterfaces.C.longrange...
floatInterfaces.C.C_floatFloat
doubleInterfaces.C.doubleStandard.Long_Float
charInterfaces.C.charCharacter
char[N]Interfaces.C.char_arrayString
char* (strings)Interfaces.C.Strings.chars_ptrString
nul
NULLSystem.Null_Address

A string in C can be created with two forms of declarations: as an array (ex.: char mystring[] = "TEXT"), and as a pointer (ex.: char* mystring = "TEXT"). In both cases, the zero character is considered as part of the string. In Ada, there are two types for each declaration form: Interfaces.C.char_array for the C array declaration, and Interfaces.C.Strings.chars_ptr for the C pointer declaration. Using them interchangeably may lead to store garbage information or to raise Storage_Error (stack overflow) exceptions.

The following code C and Ada code are a complete example. The C code defines several variables of different types, and the Ada program imports them and prints them on output. The intention is to show an example of the interface code, along with the data stored when executed.

 #include <stdio.h> char text1[] = "Another hello from C"; char* text2 = "Hello pointer from C"; int number = 123; float f_number = 123.4567; 
File: data_test.adb (view, plain text, download page, browse all)
withAda.Text_IO; useAda.Text_IO; withInterfaces.C; useInterfaces.C; withInterfaces.C.Strings; useInterfaces.C.Strings; procedure Data_Test is Text1 : char_array (0 .. 20) with Import => True, Convention => C, External_Name => "text1"; Text1_Pointer : chars_ptr with Import => True, Convention => C, External_Name => "text1"; Text2 : char_array (0 .. 20) with Import => True, Convention => C, External_Name => "text2"; Text2_Pointer : chars_ptr with Import => True, Convention => C, External_Name => "text2"; Number : int with Import => True, Convention => C, External_Name => "number"; F_Number : float with Import => True, Convention => C, External_Name => "f_number"; begin Put_Line ("Text1 is: " & To_Ada (Text1)); -- if Text1 (Text1'Last) = Char'Val (0) then Put_Line (" Text1 ends with zero character."); else Put_Line (" Text1 does not end with zero character."); endif; -- The following raises a Storage_Error exception.-- Put_Line (Interfaces.C.Strings.Value (Text1_Pointer, 19)); Put_Line ("Text2 is: " & To_Ada (Text2)); -- Prints garbage. Put_Line ("Text2_Pointer is:" & Value (Text2_Pointer)); declare Tmp : Char_Array := Value (Text2_Pointer); beginif Tmp (Strlen (Text2_Pointer)) = Char'Val (0) then Put_Line (" Text2_Pointer ends with zero character."); else Put_Line (" Text2_Pointer does not end with zero character."); endif; end; Put_Line ("Number: " & Number'Image); Put_Line ("F_Number: " & F_Number'Image); end Data_Test; 

Example

[edit | edit source]
with Interfaces.C; with System; with Ada.Text_IO; procedure Main isprocedure W32_Open_File_Dialog ispackage C renames Interfaces.C; use C; type OPENFILENAME isnew System.Address; type Window_Type isnew System.Address; function GetOpenFileName (p : OPENFILENAME) return C.int; pragma Import (C, GetOpenFileName, "ada_getopenfilename"); function Allocate return OPENFILENAME with Import => True, Convention => C, External_Name => "ofn_allocate"; procedure Set_Struct_Size (X : OPENFILENAME) with Import => True, Convention => C, External_Name => "ofn_set_struct_size"; procedure Set_Owner (X : OPENFILENAME; Owner : Window_Type) with Import => True, Convention => C, External_Name => "ofn_set_owner"; procedure Set_File (X : OPENFILENAME; File : char_array; Length : C.int) with Import => True, Convention => C, External_Name => "ofn_set_file"; procedure Set_Filter (X : OPENFILENAME; Filter : char_array); pragma Import (C, Set_Filter, "ofn_set_filter"); procedure Set_Filter_Index (X : OPENFILENAME; N : C.int) with Import => True, Convention => C, External_Name => "ofn_set_filter_index"; function Get_File (X : OPENFILENAME) return System.Address; pragma Import (C, Get_File, "ofn_get_file"); function Get_File_Length (X : OPENFILENAME) return C.size_t; pragma Import (C, Get_File_Length, "ofn_get_file_length"); procedure Free (X : OPENFILENAME) with Import => True, Convention => C, External_Name => "ofn_free"; OFN : OPENFILENAME; Ret : C.int; File : aliased C.char_array := "test.txt" & C.nul; Filter : aliased C.char_array := "All" & C.nul & "*.*" & C.nul & C.nul & C.nul; begin OFN := Allocate; Set_Struct_Size (OFN); Set_Owner (OFN, Window_Type (System.Null_Address)); Set_File (OFN, File, 256); Set_Filter (OFN, Filter); Set_Filter_Index (OFN, 0); Ret := GetOpenFileName (OFN); if Ret = 0 then Free (OFN); Ada.Text_IO.Put_Line ("No file selected."); return; endif; declare Selected_File_Address : System.Address := Get_File (OFN); Selected_File_Length : C.size_t := Get_File_Length (OFN); Selected_File : char_array (1 .. Selected_File_Length + 1); for Selected_File'Address use Selected_File_Address; begin Ada.Text_IO.Put_Line (To_Ada (Selected_File, Trim_Nul => True)); end; Free (OFN); end W32_Open_File_Dialog; begin W32_Open_File_Dialog; end Main; 



#include<windows.h>#include<stdlib.h>OPENFILENAME*ofn_allocate(){OPENFILENAME*ofn;ofn=(OPENFILENAME*)malloc(sizeof(OPENFILENAME));if(ofn==NULL)returnNULL;memset(ofn,0,sizeof(OPENFILENAME));returnofn;}voidofn_set_struct_size(OPENFILENAME*ofn){ofn->lStructSize=sizeof(OPENFILENAME);}voidofn_set_owner(OPENFILENAME*ofn,void*owner){ofn->hwndOwner=(HWND)owner;}voidofn_set_file(OPENFILENAME*ofn,char*file,intlength){if(ofn->lpstrFile)free(ofn->lpstrFile);ofn->lpstrFile=(char*)malloc(length);if(ofn->lpstrFile==NULL){ofn->nMaxFile=0;return;}strncpy(ofn->lpstrFile,file,length);ofn->nMaxFile=length;}voidofn_set_filter(OPENFILENAME*ofn,char*filter){ofn->lpstrFilter=filter;}voidofn_set_filter_index(OPENFILENAME*ofn,intn){ofn->nFilterIndex=n;}voidofn_free(OPENFILENAME*ofn){if(ofn->lpstrFile)free(ofn->lpstrFile);free(ofn);}intada_getopenfilename(OPENFILENAME*ofn){return(int)GetOpenFileNameA(ofn);}char*ofn_get_file(OPENFILENAME*ofn){returnofn->lpstrFile;}size_tofn_get_file_length(OPENFILENAME*ofn){return(size_t)lstrlen(ofn->lpstrFile);}

The following project file (getopenfilename.gpr) shows how to link to comdlg32.dll:

project Getopenfilename is for Languages use ("Ada", "C"); for Source_Dirs use ("src"); for Object_Dir use "obj"; for Main use ("main.adb"); package Linker is for Default_Switches ("ada") use ("-lComdlg32"); end Linker; end Getopenfilename; 

Hardware devices

[edit | edit source]

Embedded programmers usually have to write device drivers. Ada provides extensive support for interfacing with hardware, like using representation clauses to specify the exact representation of types used by the hardware, or standard interrupt handling for writing Interrupt service routines.

See also

[edit | edit source]

Wikibook

[edit | edit source]

Ada Reference Manual

[edit | edit source]

Ada 95 Rationale

[edit | edit source]

Ada Quality and Style Guide

[edit | edit source]
close