Interfacing with C (and Other Languages)

Call C functions with the Import aspect and Convention => C, map types with Interfaces.C (int, long, double, size_t, char_array) and Interfaces.C.Strings (chars_ptr, New_String, Value, Free), pass callbacks, lay out C-compatible records, export Ada subprograms to C, handle arrays and pointers, and know about binding generators, Fortran and C++ interfacing.

Ada was designed to live in multi-language systems. The Interfaces packages define types that match other languages exactly, and the Import, Export and Convention aspects connect Ada subprograms and objects to foreign ones. Calling C libraries — operating system APIs, graphics, databases — is routine, and C code can call Ada just as easily.

Importing C Functions

'function C_Strlen (S : chars_ptr) return size_t with Import, Convention => C, External_Name => "strlen";' declares an Ada view of a C function; the linker resolves the name. Use the types from Interfaces.C: int, unsigned, long, double, size_t, char, char_array (a C string buffer, created with To_C and converted with To_Ada). Interfaces.C.Strings.chars_ptr is a C char*; New_String allocates a C copy (with malloc) that must be released with Free, and Value reads a C string into an Ada String. Arrays and records need Convention => C to match C layout; C_Pass_By_Copy passes a record by value like a C struct.

• with Import, Convention => C, External_Name => "name". • Interfaces.C types: int, long, double, size_t, char_array. • Interfaces.C.Strings: chars_ptr, New_String, Value, Free. • Convention => C / C_Pass_By_Copy for arrays and structs.

Callbacks, Exports and Binding Generation

To pass an Ada function to C (like qsort's comparator), give the function and its access type Convention => C. To make an Ada subprogram callable from C, use 'with Export, Convention => C, External_Name => "ada_compute"'; when the main program is in C, call adainit before and adafinal after using Ada code (generated by the binder with gnatbind -n / a library project). Mapping C pointers: access types with Convention => C, System.Address, or Interfaces.C.Pointers. GCC can generate Ada bindings automatically: 'g++ -c -fdump-ada-spec header.h' produces .ads files. Interfaces.Fortran and Interfaces.COBOL exist too, and C++ is interfaced through the C++ convention and generated specs.

• Callbacks: Convention => C on the function and its access type. • Export + Convention => C to call Ada from C; adainit/adafinal. • -fdump-ada-spec generates bindings from C/C++ headers.

Code Examples

strlen, abs, atoi and qsort come from the C runtime linked by GNAT; qsort calls back into the Ada Compare function. On Windows x64, C long is 32 bits (on Linux x86-64 it is 64) — a reminder to use Interfaces.C types instead of guessing. Output: strlen = 14 Value (Text) = Hello from Ada Text = Null_Ptr after Free? TRUE abs (-17) = 17 atoi ("1234") = 1234 qsort result:-7 0 3 8 19 42 int'Size = 32, long'Size = 32, double'Size = 64, Point'Size = 128 To_Ada (To_C ("abc")) = abc

Mixing your own C files: compile them and pass the objects to the linker, or let GPRbuild handle multi-language projects.

function C_Abs (X : Integer) return Integer with Import, Convention => C, External_Name => "abs";

function C_Abs (X : Interfaces.C.int) return Interfaces.C.int with Import, ...;

Use Interfaces.C types; Ada's Integer is not guaranteed to be C's int.

Put_Line (Value (New_String ("x"))); -- leak S:= New_String ("x"); Put_Line (Value (S)); Free (S);

New_String allocates with malloc; free it.

C_Func (My_Ada_String); -- passing a String where C expects char*

C_Func (To_C (My_Ada_String)); -- char_array with a NUL terminator Ada strings are not NUL-terminated.

Which aspect set imports a C function named 'strlen'? A. with Export, External_Name => "strlen" B. with Import, Convention => C, External_Name => "strlen" C. pragma Link (strlen) D. extern C strlen

How do you free a string created with New_String? A. Unchecked_Deallocation B. Interfaces.C.Strings.Free C. It's garbage collected D. Not needed

Which Ada type matches C's size_t? A. Natural B. Interfaces.C.size_t C. Long_Integer D. System.Address

What must a callback passed to qsort have? A. Convention => C B. Inline C. Pure D. Volatile Trace the C interfacing example.

Real output: strlen = 14 Value (Text) = Hello from Ada Text = Null_Ptr after Free? TRUE abs (-17) = 17 atoi ("1234") = 1234 qsort result:-7 0 3 8 19 42 int'Size = 32, long'Size = 32, double'Size = 64, Point'Size = 128 To_Ada (To_C ("abc")) = abc

with Ada.Text_IO;          use Ada.Text_IO;
with Interfaces.C;         use Interfaces.C;
with Interfaces.C.Strings; use Interfaces.C.Strings;
with System;

procedure L60_C_Interface is
   --  Importing functions from the C standard library
   function C_Strlen (S : chars_ptr) return size_t
     with Import, Convention => C, External_Name => "strlen";
   function C_Abs (X : int) return int
     with Import, Convention => C, External_Name => "abs";
   function C_Atoi (S : char_array) return int
     with Import, Convention => C, External_Name => "atoi";

   --  qsort takes a C callback: the Ada compare function uses Convention C
   type Int_Array is array (1 .. 6) of int with Convention => C;
   function Compare (A, B : access constant int) return int
     with Convention => C;
   function Compare (A, B : access constant int) return int is
     (if A.all < B.all then -1 elsif A.all > B.all then 1 else 0);

   type Compare_Access is access function (A, B : access constant int) return int
     with Convention => C;
   procedure C_Qsort (Base : System.Address; N, Size : size_t; Cmp : Compare_Access)
     with Import, Convention => C, External_Name => "qsort";

   --  A C-compatible reco
…
gcc -c mathlib.c                 # compile the C part
gnatmake main.adb -largs mathlib.o   # link it with the Ada program
# or: gprbuild with a project declaring Languages => ("Ada", "C")