home *** CD-ROM | disk | FTP | other *** search
- <COMMENT This is a lesson file for the Lovelace Ada tutorial>
- <COMMENT A program called genlesson is used to transform this file into a set>
- <COMMENT of useful HTML files for use by Mosaic & other WWW browsers.>
-
- <COMMENT Edit the following lines. >
- <TUTOR NAME="Lovelace">
- <LESSON NUMBER=16>
- <AUTHOR NAME="David A. Wheeler" EMAIL="wheeler@ida.org">
- <AUTHOR ADDRESS="<A HREF="dwheeler.htm">David A. Wheeler (wheeler@ida.org)</A>">
- <COMMENT $Id$ >
-
- <COMMENT You'll probably want to uncomment and edit these lines: >
- <COMMENT <PREVIOUS_LESSON LOCATION="URL_of_directory/" >
- <COMMENT <NEXT_LESSON LOCATION="URL_of_directory/" >
-
- <COMMENT A lesson is divided into 1 or more "sections".>
- <COMMENT Each section has a title; SECTION starts a new section.>
-
- <SECTION NAME="General Information on Interfacing to Other Languages">
- Ada 95 provides a set of packages and some
- special pragmas to interface with other computer languages.
- The three most useful pragmas are called Import, Export, and Convention:
- <OL>
- <LI>Pragma import "imports" a subprogram from another ("foreign") language
- into an Ada program.
- Use pragma import if you want to call,
- for example, an existing C function.
- <LI>Pragma export "exports" an Ada subprogram to a "foreign" language.
- For example, if you've written an Ada procedure and want to call it from
- C, use pragma export.
- <LI>Pragma Convention specifies that a specified type should
- use the storage conventions of a given "foreign" language.
- It is also used on subprograms if they are "callback" subprograms
- (described below).
- </OL>
- <P>
- Here's an example of each:
- <P>
- <PRE>
- pragma Import(C, getenv); -- Use the C program getenv in my Ada program.
- pragma Export(COBOL, Read_Sensor); -- Provide Ada procedure "Read_Sensor"
- -- to the COBOL compiler.
- pragma Convention(Fortran, State_Vector) -- Read and write State_Vector
- -- using Fortran storage conventions
- -- (e.g. column-major format)
- </PRE>
- <P>
- Here is the <A HREF="bnf.htm">BNF</A> for these pragmas:
- <PRE>
- import_pragma ::= "pragma Import("
- [ "Convention =>" ] language ","
- [ "Entity =>" ] unit
- [ "," [ "Link_Name =>" ] link_name ] ");"
-
- export_pragma ::= "pragma Export("
- [ "Convention =>" ] language ","
- [ "Entity =>" ] unit
- [ "," [ "Link_Name =>" ] link_name ] ");"
-
- convention_pragma ::= "pragma Convention("
- [ "Convention =>" ] language ","
- [ "Entity =>" ] unit ");"
- </PRE>
- <P>
- Ada compilers always support the Convention (language) Ada, naturally enough.
- Your Ada compiler probably also supports the languages C, Fortran,
- and possibly COBOL.
- GNAT supports C++ as the language name CPP, and you can also interface
- Ada and C++ programs by having both use the C convention to send
- information to each other.
- For assembly language modules, use the name of the high level language
- that the module's interface mimics.
- <P>
- The "Link_Name" parameter often isn't necessary, but it's useful in
- some circumstances, for example,
- if you need access to an object whose name has been "mangled"
- in a way the Ada compiler doesn't know about or if the name is not
- a legal Ada identifier (such as names with leading underscores).
- <P>
- "Callback" subprograms are subprograms which have access (pointer)
- values held in some external location and are then called later using
- that external value. If you have an Ada subprogram that will be called
- this way, use pragma Convention on both the subprogram and on the
- access type used.
- This is useful, for example, in dealing with the X window graphical
- user interface (GUI).
- <P>
- <COMMENT Should add a link to the Ada LRM from below>
- If the "main" subprogram is not in Ada, there is an additional
- issue to consider called "elaboration".
- The actual main subprogram should make sure that the environment
- for Ada is correctly set up.
- This is done automatically if the main subprogram is in Ada, but if
- it isn't, you have to do it yourself.
- The
- <A HREF="http://lglwww.epfl.ch/Ada/LRM/9X/rm9x/rm9x-B-01.html">Ada LRM
- section B.1(39)</A>
- suggests that compilers provide
- subprograms called "adainit" to start up the Ada environment and
- "adafinal" to clean it up after the Ada subprograms have stopped running.
- If you need to have a non-Ada main subprogram,
- check your compiler manual to see if
- it supports this and if there are any restrictions on what is and is
- not permitted.
-
- <QUESTION Type=Multiple-Choice>
- You're writing an Ada program and want to directly
- call an existing C function
- called <EM>display</EM>. Which of the following pragmas should you use?
- <CHOICES>
- <CHOICE ANS=1>pragma Import(C, display);
- <CHOICE ANS=2>pragma Export(C, display);
- <CHOICE ANS=3>pragma Convention(C, display);
- </CHOICES>
- <ANSWER ANS=1>
- <RESPONSES>
- <WHEN ANS=1>
- Right.
- In addition to the pragma, you'd also need to tell Ada what
- display's parameters were, so the complete form would probably
- look something like this:
- <P>
- <PRE>
- procedure display(Value : Integer);
- pragma Import(C, display);
- </PRE>
- <P>
- I say "something like this" because we haven't talked about how
- to send data types between languages.
- Let's do that now for C, a very common language;
- handling data types for other languages is handled similarly.
- <WHEN ANS=2>
- No, sorry.
- Export would send an <EM>Ada</EM> subprogram out so that a <EM>C</EM>
- program could call it.
- <WHEN ANS=3>
- Close, but not quite.
- Convention would make it possible to use an Ada subprogram called
- display out to C so that C could call it back.
- Try again.
- </RESPONSES>
- <SECTION NAME="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.
- <P>
- First, here are some general rules on how Ada and C correspond, based on
- the
- <A HREF="http://lglwww.epfl.ch/Ada/LRM/9X/rm9x/rm9x-B-03.html">LRM B.3(63):</A>
- <OL>
- <LI>An Ada procedure corresponds to a void-returning C function.
- <LI>An Ada function corresponds to a non-void-returning C function.
- <LI>An Ada array corresponds to a C pointer to the first element.
- <LI>Simple scalar types (integers, floats, and access/pointer types) correspond
- to the obvious type in the other language.
- </OL>
- <P>
- 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).
- <P>
- 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.
- <P>
- 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:
- <OL>
- <LI>constant <EM>Null_Ptr</EM>, which corresponds to C's <EM>(char*)NULL</EM>,
- <LI>procedure <EM>Free</EM>, which corresponds to C's <EM>free()</EM>, and
- <LI>function <EM>Value</EM>, which takes a chars_ptr and returns a
- normal Ada String. This function raises an exception Dereference_Error
- if passed a null pointer.
- </OL>
- <P>
- Let's work through a real-life example so you can see how this really works.
- This example is from
- <A HREF="http://wuarchive.wustl.edu/languages/ada/swcomps/cgi/cgi.html">"package
- CGI"</A>, 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]):
- <P>
- <PRE>
- char *getenv(char *name);
- </PRE>
- <P>
- This can be pretty straightforwardly translated into Ada as:
- <P>
- <PRE>
- function getenv(Variable : chars_ptr) return chars_ptr;
- pragma Import(C, getenv);
- </PRE>
- <P>
- 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:
- <P>
- <PRE>
- with Interfaces.C.Strings; use Interfaces.C.Strings;
- -- ...
-
- function Get_Environment(Variable : String) return String is
- -- Return the value of the given environment variable.
- -- If there's no such environment variable, return an empty string.
-
- function getenv(Variable : chars_ptr) return chars_ptr;
- pragma Import(C, getenv);
- -- getenv is a standard C library function; see K&R 2, 1988, page 253.
- -- it returns a pointer to the first character; do NOT free its results.
-
- Variable_In_C_Format : chars_ptr := New_String(Variable);
- Result_Ptr : chars_ptr := getenv(Variable_In_C_Format);
- Result : String := Value_Without_Exception(Result_Ptr);
-
- begin
- Free(Variable_In_C_Format);
- return Result;
- end Get_Environment;
- </PRE>
- <P>
- 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 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:
- <P>
- <PRE>
- function Value_Without_Exception(S : chars_ptr) return String is
- -- Translate S from a C-style char* into an Ada String.
- -- If S is Null_Ptr, return "", don't raise an exception.
- begin
- if S = Null_Ptr then return "";
- else return Value(S);
- end if;
- end Value_Without_Exception;
- pragma Inline(Value_Without_Exception);
- </PRE>
- <P>
- Now we can easily get environment variables in Ada. For example, to get
- the value of environment variable REQUEST_METHOD, use:
- <P>
- <PRE>
- Request_Method_Text : String := Get_Environment("REQUEST_METHOD");
- </PRE>
- <P>
- One thing we haven't covered are C <EM>struct</EM>s.
- Ada records and C structs clearly correspond, but how exactly should
- they correspond?
- The Ada LRM 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.
-
- <SECTION NAME="Ada Bindings">
- The previous material should help you develop a "binding" (interface)
- between software components, where one component is written in Ada
- and another component is written in another language.
- Naturally, it's easier if someone else or a tool
- does the job for you.
- <P>
- Before you can evaluate what someone else has done, you need to
- understand the major types of bindings between an Ada program and another
- program.
- These types are called "direct" and "abstract":
- <P>
- <UL>
- <LI>A "direct" (also called "thin") binding
- provides a one-to-one mapping to Ada
- of whatever interface the foreign program provides.
- Direct bindings are easy to understand if you understand the foreign
- program's interface, and direct bindings for Ada are easy to create.
- In particular, you can use the existing documentation, which is a very
- important advantage for complex interfaces (like windowing systems).
- Unfortunately, direct bindings are often a little clumsy to work with and
- often don't provide the protection usually provided by Ada interfaces.
- Thus, it's often nicer to work with "abstract" bindings.
- <LI>An "abstract" (also called "thick") binding provide a more
- abstract, Ada-like view of the foreign program.
- Unfortunately, while "abstract" bindings are nicer to work with, it takes
- work and time to create the right abstractions.
- Thus abstract bindings are harder to create.
- </UL>
- <P>
- Here are some other things you need to know about bindings:
- <UL>
- <LI>"Direct" and "abstract" are really extremes on a continuum;
- there are bindings that are "mostly direct" but have been abstracted a
- little, and there are "abstract" bindings that have some direct
- one-to-one mappings.
- <LI>The terms "thick" and "thin" have other
- related meanings (involving how a standard is written),
- which is why I've used the terms "direct" and "abstract" here.
- </UL>
- <P>
- Now that you understand these basic issues, you can go hunt for
- ways to make this interfacing job easier.
- <A HREF="ftp://sw-eng.falls-church.va.us/public/AdaIC/tools/bindings/">The
- Ada Information Clearinghouse maintains a document listing existing
- Ada bindings for other products and standards.</A>
- Their list is incomplete, but it's a good starting point, especially
- for common products or standards such as POSIX, X windows, Microsoft Windows,
- or SQL databases.
- If you're interfacing with a commercial product, ask the vendor
- to supply you with an Ada interface.
- You could also post a request to
- <A HREF="news:comp.lang.ada">comp.lang.ada</A>
- if you can't find what you're looking for.
- <P>
- There are also tools to automatically generate direct (thin)
- Ada bindings to C libraries.
- Here are two tools (there are others as well):
- <OL>
- <LI>Cbind translates C declarations and C preprocessor definitions
- into Ada package(s).
- Its strength is in ease-of-use; just type:
- <PRE>
- "cbind file.h > file.ads".
- </PRE>
- Cbind is available via
- <A HREF="ftp://rational.com/public/tools/cbind">Rational</A>
- and
- <A HREF="http://www.cdrom.com/pub/ada/swtools/cbind">Walnut Creek.</A>
- <P>
- <LI><A HREF="ftp://cs.nyu.edu/pub/gnat/contrib/forest/">CtoAda
- translates declarations from C to Ada.</A>
- CtoAda's strength is that it provides many "hooks" to allow a programmer
- to control the translation.
- This gives more control at the expense
- of requiring more work by the programmer.
- </OL>
-
- <QUESTION Type=Multiple-Choice>
- If you want to quickly create a binding to another language
- and don't mind that it might be a little clumsy to use,
- what kind of binding would you create?
- <CHOICES>
- <CHOICE ANS=1>Direct ("thin") binding
- <CHOICE ANS=2>Abstract ("thick") binding
- </CHOICES>
- <ANSWER ANS=1>
-