N. Bindings - JulTob/Ada GitHub Wiki

pragma import 	-- Import identifier from another language  // extern?
pragma export 	-- Export identifier to another language    // extern?
pragma import_function 	  -- Like import, but extra options 	
pragma import_procedure   -- Like import, but extra options 	
pragma import_valued_procedure 	-- Import a function that returns values as parameters 	 // extern?
pragma export_function 	 -- Like export, but extra options 	
pragma export_procedure  -- Like export, but extra options 	
pragma unchecked_union 	 -- (Ada 2005) Variant record is like a C union

Because gnat is tightly integrated with gcc, we can make certain assumptions that would otherwise be impossible.

  • The basic Ada data types are equivalent to their C counterparts: an Ada integer array is a C integer array
  • In parameters are the same as pass by copy parameters in C
  • In out parameters are the same as passing a pointer as a parameter in C
  • Ada string parameters ending in ASCII.NUL are the same as a C string
  • Ada procedures are the same as C void functions

There are rare cases when these assumptions don't hold (e.g. certain cases when null pointer parameters are not allowed by Ada), but, generally speaking, these assumptions are valid under Linux. Gnat has general purpose interfacing pragmas and support for C types in the Interfaces.C package. Use these if you want maximum portability.

Because of these assumptions, most C library calls are easily represented in Ada. For example, we check the man page for gettime and discover it returns the current time as a long integer. To call this from Ada, we use

   function gettime return long_integer;
  pragma Import( C, gettime );  

Since there is no Ada body for the gettime function, we use pragma import to let gnat know gettime is a C function. When we link, we need to specify the C library that function is in. In the case for the GNU C library, this is unnecessary since it's automatically linked. We can now call the C function gettime as if we wrote it ourselves.In C, it's possible to call a function and discard the result by not assigning it to anything. You can call C functions from Ada this way by declaring them a procedure. For example:

  procedure gettime;
  pragma Import( C, gettime );  

In this case, it's not particularly useful to call gettime and throw away the time it gives you. In general, you should avoid discarding the result because you may find it useful at some time in the future. However, there are certain C function where the result is provided only for flexibility, such as functions that return a pointer in a parameter and return the same pointer as the function result as well. These can safely be discarded by treating the function as a procedure.If we wanted to export an integer variable called TotalTimeEstimate to C, we'd use

   TotalTimeEstimate : integer;
  pragma Export( C, TotalTimeEstimate );  

A C function that returns void corresponds to an Ada procedure.

When importing or exporting to C, gnat converts the variable to lower case because C is a case-sensitive language. TotalTimeEstimate would be called totaltimeestimate in a C program. You can override this by providing a specific C name to link to. For example,

   pragma Export( C, TotalTimeEstimate, "TotalTimeEstimate" );  

Import and Export don't require the name be the same at all. However, using entirely different names in C and Ada will make your program hard to understand.

If you want to import functions from libraries other than the standard C library, you will have to explicitly link them in. For example, to use the C math library, libm.a, would have to be explicitly linked using -lm. In C, functions can have parameters that change value, while in Ada this kind of function is not allowed because functions can only have "in" parameters. To get around this problem, gnat defines an import_valued_procedure pragma. Suppose you have a C function like this:

   int SomeCFunction( char * param )  

Normally, there is no way to represent this kind of function in an Ada program. However, we can import it by treating it as a procedure using the import_valued_procedure pragma:

 procedure SomeCFunction ( result : out integer; param : in out integer );
pragma import( C, SomeCFunction);
pragma import_valued_procedure( SomeCFunction );  

The import_valued_procedure pragma tells gnat that this procedure corresponds to a C function: the first parameter is the result of the C function, and the remaining parameters correspond to the parameters of the C function. The first import pragma is not strictly required, but ACT recommends using it.

You can't import identifiers created by the #define statement since they only exist before a C program is compiled. You also can't import types (except for C++ classes) since types have no address in memory. [KB-true?]

There is one case where these tricks fail: when the C function returns a pointer to a C variable that it declared. In this case, the function is returning a new C pointer. Luckily, Ada provides a package called Address_To_Access_Conversions to convert between C pointers and Ada access types. You instantiate the package with the type you want to convert between, and the package creates an access type that can be converted to and from an address (which is a C pointer).The following program demonstrates conversions to and from C pointer types.


with Ada.Text_IO, System.Address_To_Access_Conversions;
use Ada.Text_IO;
procedure pointers is

package IntPtrs is
  new System.Address_To_Access_Conversions( integer );
  -- Instantiate a package to convert access types to/from addresses.
  -- This creates an integer access type called Object_Pointer.

  five : aliased integer := 5;
  -- Five is aliased because we will be using access types on it

  int_pointer : IntPtrs.Object_Pointer;
  -- This is an Ada access all type

  int_address : System.Address;
  -- This is an address in memory, a C pointer

begin
  int_pointer := five'unchecked_access;
  -- Unchecked_access needed because five is local to main program.
  -- If it was global, we could use 'access.

  int_address := five'address;
  -- Addresses can be found with the 'address attribute.
  -- This is the equivalent of a C pointer.

  int_pointer := IntPtrs.To_Pointer( int_address );
  int_address := IntPtrs.To_Address( int_pointer);
  -- Convert between Ada and C pointer types.

end pointers;

For example, the standard C library function get_current_dir_name returns a pointer to a C string which it declares. To use get_current_dir_name, we have to instantiate Address_To_Access_Conversions for an array of characters (a C string), and convert the address to an access type using something like

    CharArray_pointer := CharArrayPtrs.To_Pointer( get_current_dir_name );

There is no other way in Ada to access the array that get_current_dir_name points to.

To import a C union, you'll have to use pragma unchecked_union to disable Ada features that are incompatible with C (namely, that the discriminant is not saved).

KB-If your main program is a C program, you need to call adainit before any Ada code.