Section 16.2 - Interfacing with C

Since there are many useful utilities that can be called from C
it's a good idea to know how to call them from Ada.
This section assumes you know the C language to some basic level;
if you don't know C you can skim this section.

First, here are some general rules on how Ada and C correspond, based on
the
RM B.3(63):

An Ada procedure corresponds to a void-returning C function.

An Ada function corresponds to a non-void-returning C function.

An Ada array corresponds to a C pointer to the first element.

Simple scalar types (integers, floats, and access/pointer types) correspond
to the obvious type in the other language.

Ada 95 provides a set of predefined packages that make it easier to
interface with C.
The primary package is named "Interfaces.C", which contains definitions
for C types in Ada.
These include C's types int, long, unsigned, and double.
The C type float is called "C_float" in Ada so that it isn't
confused with Ada's type Float (Ada Float and C float are probably identical,
but that's not necessarily true).

The type "char_array" mimics C character arrays.
Many C functions assume that character arrays are terminated with
the special character "nul" (written in C as '\0').
Since Ada strings aren't normally nul-terminated, functions
To_C and To_Ada convert between Ada String types and C char_array types.

There are additional packages called Interfaces.C.Strings and
Interfaces.C.Pointers that provide additional types and
operations on C-style strings and C pointers.
In particular, package "Interfaces.C.Strings" defines the type "chars_ptr",
which corresponds to the typical C type "char*" when used to point to
a C string (i.e. a pointer to an array of characters).
The package also defines:

constant Null_Ptr, which corresponds to C's (char*)NULL,

procedure Free, which corresponds to C's free(), and

function Value, which takes a chars_ptr and returns a
normal Ada String. This function raises an exception Dereference_Error
if passed a null pointer.

Let's work through a real-life example so you can see how this really works.
This example is from
"package
CGI", an Ada binding to the World Wide Web
Common Gateway Interface (CGI).
Let's say that you want to get the value of an environment variable
from the Operating System, and you want to get this value via a
pre-existing C function that does this.
In C this function is called "getenv" and it has the following C definition
(see [Kernighan and Ritchie 1988, edition 2, page 253]):

That works, but it's inconvenient to have to keep translating
values in and out of type "chars_ptr" in an Ada program.
It's probably better to write a wrapper program that translates the Ada
Strings to C strings (chars_ptr) and back for us.
Let's define an Ada function to do that for us:

Notice that a lot of string manipulation is happening in the declaration
section.
That's an easy way to get things done,
because simple Ada Strings have a fixed length once they're declared.
There's a call to some function called Value_Without_Exception;
that's because normally an attempt to turn a null C pointer into a string will
raise an exception, and we just want to turn it into an empty string instead.
That means we'll have to define such a function; here's a definition:

Now we can easily get environment variables in Ada. For example, to get
the value of environment variable REQUEST_METHOD, use:

Request_Method_Text : String := Get_Environment("REQUEST_METHOD");

One thing we haven't covered are C structs.
Ada records and C structs clearly correspond, but how exactly should
they correspond?
The Ada RM advises, but does not require, that
Ada records always be passed to C as pointers to the beginning of the
corresponding C struct.
For those (relatively rare) cases where a C function expects to be passed
a structure by value (a copy instead of the more common pointer-to-structure),
you could create a new C function that converts a pointer into the
actual structure and then call that new C function from Ada.
However, this is simply advice, and
the GNAT compiler does not follow this advice - instead, GNAT sends
Ada records by value (copies).
Both approaches are reasonable, but unfortunately they are different.
The safest approach for passing Ada records is to always pass
"access to record" values - since they are scalar, they are guaranteed
to pass correctly in all Ada compilers.