Next: Rebuilding the GNAT Run-Time Library, Previous: Using an Ada Library, Up: GNAT and Libraries
The previous sections detailed how to create and install a library that was usable from an Ada main program. Using this library in a non-Ada context is not possible, because the elaboration of the library is automatically done as part of the main program elaboration.
GNAT also provides the ability to build libraries that can be used both in an Ada and non-Ada context. This section describes how to build such a library, and then how to use it from a C program. The method for interfacing with the library from other languages such as Fortran for instance remains the same.
Here is an example of simple library interface:
package Interface is procedure Do_Something; procedure Do_Something_Else; end Interface;
pragma Export
or pragma Convention
for the
exported entities.
Our package Interface
is then updated as follow:
package Interface is procedure Do_Something; pragma Export (C, Do_Something, "do_something"); procedure Do_Something_Else; pragma Export (C, Do_Something_Else, "do_something_else"); end Interface;
This step is performed by invoking gnatbind with the -L<prefix>
switch. gnatbind
will then generate the library elaboration
procedure (named <prefix>init
) and the run-time finalization
procedure (named <prefix>final
).
# generate the binder file in Ada $ gnatbind -Lmylib interface # generate the binder file in C $ gnatbind -C -Lmylib interface
$ gcc -c b~interface.adb
The procedure is identical to the procedure explained in Creating an Ada Library, except that b~interface.o needs to be added to the list of objects.
# create an archive file $ ar cr libmylib.a b~interface.o <other object files> # create a shared library $ gcc -shared -o libmylib.so b~interface.o <other object files>
The example below shows the content of mylib_interface.h
(note
that there is no rule for the naming of this file, any name can be used)
/* the library elaboration procedure */ extern void mylibinit (void); /* the library finalization procedure */ extern void mylibfinal (void); /* the interface exported by the library */ extern void do_something (void); extern void do_something_else (void);
Libraries built as explained above can be used from any program, provided
that the elaboration procedures (named mylibinit
in the previous
example) are called before the library services are used. Any number of
libraries can be used simultaneously, as long as the elaboration
procedure of each library is called.
Below is an example of C program that uses our mylib
library.
#include "mylib_interface.h" int main (void) { /* First, elaborate the library before using it */ mylibinit (); /* Main program, using the library exported entities */ do_something (); do_something_else (); /* Library finalization at the end of the program */ mylibfinal (); return 0; }
Note that this same library can be used from an equivalent Ada main program. In addition, if the libraries are installed as detailed in Installing an Ada Library, it is not necessary to invoke the library elaboration and finalization routines. The binder will ensure that this is done as part of the main program elaboration and finalization phases.
Invoking any library finalization procedure generated by gnatbind
shuts down the Ada run time permanently. Consequently, the finalization
of all Ada libraries must be performed at the end of the program. No
call to these libraries nor the Ada run time should be made past the
finalization phase.
The pragmas listed below should be used with caution inside libraries, as they can create incompatibilities with other Ada libraries:
Locking_Policy
Queuing_Policy
Task_Dispatching_Policy
Unreserve_All_Interrupts
Program_Error
will
be raised during the elaboration of the conflicting
libraries. The usage of these pragmas and its consequences for the user
should therefore be well documented.
Similarly, the traceback in exception occurrences mechanism should be enabled or disabled in a consistent manner across all libraries. Otherwise, a Program_Error will be raised during the elaboration of the conflicting libraries.
If the 'Version
and 'Body_Version
attributes are used inside a library, then it is necessary to
perform a gnatbind
step that mentions all ALI files in all
libraries, so that version identifiers can be properly computed.
In practice these attributes are rarely used, so this is unlikely
to be a consideration.