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")