comp.lang.ada
 help / color / mirror / Atom feed
* on calling Ada from C/C++
@ 1996-06-12  0:00 Nasser Abbasi
  1996-06-13  0:00 ` Nasser Abbasi
  0 siblings, 1 reply; 4+ messages in thread
From: Nasser Abbasi @ 1996-06-12  0:00 UTC (permalink / raw)




this is fyi:

This is a little note about calling Ada subprogram from C++ .

I started 2-3 hours ago to learn how to call Ada from C++, I still
need to learn more about this. But it turned out much easier than I
as I thought it would be. The main issues are on how
to declare the Ada subprograms in C++, and how to make sure the way
the Ada subprogram is called matches the way it expects to be
called.

A C++ main calls an Ada procedure to update an integer. (I also
use C in this example just for testing.)

I also show the steps used to build the program, and few points at the end
about few things.

This was done on solaris 2.5, using gnat 3.04.

file main.cc
-------------
#include <iostream.h>
#include <stdio.h>

extern "C" { void Ada_Function(int*); 
             void adainit();  // this and adafinal are calls to ada
             void adafinal(); // rtl to insure things are initialized
           }                  // and finailized ok...

main()
{
 int i=0;

 adainit();   // call befor any other ada call

 printf("This is a  C printf ... \n");
 cout<<"this is C++ cout..."<<endl;

 cout<<" before calling Ada, i="<<i<<endl;

 Ada_Function(&i);  

 cout<<" after calling Ada, i="<<i<<endl;

 adafinal();  // call before program terminates

 return 0;

}

file: ada_function.adb
----------------------
with Text_Io; use Text_Io;
procedure Ada_Function(I:in out integer) is
begin
   Put_Line("This is Ada Put_Line ...");
   Put_Line("Number passed in is" & Integer'Image(I));
   I := I+1;
end;
pragma Export(C,Ada_Function,"Ada_Function");



Steps used to build main* are : (note: gcc here is the gcc that comes
with gnat 3.04. for the g++ include files, and libg++.a and
libstdc++.a libraries, those were obtained seperatly and build in different
directory. I found out for example that  cc1plus* was needed to be under
 2.7.2/ directory in the gnat 2.4 solaris installtion tree, but it was not, 
so I copied that file from the 2.7.2/ directory under the tree that was build
seperatly for the 2.5 solaris. This way I was able to use gnat gcc to build
C++, without cc1plus* one can not build C++ files using the gcc that comes
with gnat 3.04-solaris 2.4.)


gcc -c main.cc -I/usr/local/netshare/lib/g++-include
gnatbind -n ada_function.ali
gnatlink -L/usr/local/netshare/lib ada_function.ali main.o \
          -lg++ -lstdc++ -o main

...now run the program...

{95} main
This is a  C printf ... 
this is C++ cout...
 before calling Ada, i=0
This is Ada Put_Line ...
Number passed in is 0
 after calling Ada, i=1


few points I noticed:
1) In C++, one must declare the Ada procedure and functions as
   C functions, else C++ will mangle the ada function names, and the
   gnatlink step will fail as poor linker will not be able to resolve
   the name.  for example, if one do not use extern "C" {..}, and
   then look at the object file generated, one sees :
   $ nm -x main.o

main.o:

[Index]   Value      Size      Type  Bind  Other Shndx   Name
[5]     |0x00000000|0x00000000|NOTY |GLOB |0x0  |UNDEF  |Ada_Function__Fi

   But after using the extern "C" {...} to declare the Ada procedure,
   the function name is not changed by C++ :
   $nm -x main.o

main.o:

[Index]   Value      Size      Type  Bind  Other Shndx   Name
[14]    |0x00000000|0x00000000|NOTY |GLOB |0x0  |UNDEF  |Ada_Function

   Now, the function name matches that in the ada object file, and
   the linker is a happy camper.


2) In the Ada procedure, defining Ada_function as
   procedure Ada_Function(I:in out integer) 
                            ^^^^^
   or
   procedure Ada_Function(I:out integer)
                            ^^^^

   resulted in no difference in build or run results. 

3) more about point 2: How to make sure that the way the Ada 
   subprogram is called from the C++ module matches that the way 
   Ada subprogram is defined in the Ada module?

   For example, in the above example, in main.cc, one can change the 
   argument being passed to the Ada procedure from int* to char*, and rebuild
   the C++ main.cc, link to the ada procedure, and no errors are
   detected,  untill the program is run.

   So, it is important to "manually" make sure the way the Ada program 
   is called matches the way it is defined. Is there better way? ..

   When calling C++ from C++, one uses common function prototypes (in
   common headers) to make sure all modules agree on the function 
   signature, so that the way a function is called matches the way the is 
   defined. Similar idea is used when calling Ada from Ada. The Ada
   compiler makes sure the formal and actual parameters agree.

   But how to insure this when calling Ada from C++ ?

Nasser

==========================================================
Nasser Abbasi. Senior software engineer. C/C++/Solaris. 
GeneAssist- a client/server application for Nucleic acid 
and protein sequence search and analysis.
PE-Applied BioSystem division.        nasser@apldbio.com   


-- 
Nasser Abbasi. Senior software engineer. C/C++/Solaris. 
GeneAssist- a client/server application for Nucleic acid 
and protein sequence search and analysis.
PE-Applied BioSystem division.        nasser@apldbio.com   

 





^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: on calling Ada from C/C++
  1996-06-12  0:00 on calling Ada from C/C++ Nasser Abbasi
@ 1996-06-13  0:00 ` Nasser Abbasi
  1996-06-13  0:00   ` Robert Dewar
  1996-06-13  0:00   ` Robert Dewar
  0 siblings, 2 replies; 4+ messages in thread
From: Nasser Abbasi @ 1996-06-13  0:00 UTC (permalink / raw)
  To: Nasser Abbasi



little more on this subject..

If you want to call more than one ada subprogram from C++, I found that
gnatlink (the gnat ada linker) does not take more than .ALI file, this 
means that you can not have separate ada functions/procedure in separate 
files and have C++ call them (unless I missed something), the easy solution 
is to put them in one ada package, this way you can have one .ALI file , 
and the gnatlink is happy.

Below is little larger example of a C++ main calling a number of
Ada subprograms, also using arrays.  the Interfaces.C package  has
usefull stuff for interfacing to C/C++, but it does not have
converter functions to convert C arrays to Ada and visversa arrays 
where the array compononet is other than char,char_array,wchar_t,
wchar_array, but it is easy to write these.

So if you pass an array of int (say) to Ada, and you want Ada to
process the array as starting from 1 not from 0, then you need
to convert/"slide" over the array inside the Ada function before
processing it, also to convert the array component types from the C++
int type to the Ada Integer type. Or you can just leave it the way it 
is starting from zero, and cast the array compononet to Integer from int.

A simple function will do the conversion. Is there is way to
slide the array over on the fly ? (since the array compononets are
different types, one in standard.integer and one is Interfaces.C.int
I could not do it on the fly, so I wrote a function, probably there
is a more elegent way that this... Need to look more into this..)

I happend to dislike arrays that start from 0, I think arrays that
start from 1, as in Fortran say , are more natural to use, and also
easier to process.

The following are the steps used to build main, and the source code
example. This example is just for testing/learning how to call
Ada from C++, so please dont scream at me if you think it is not the 
most efficient.

steps:

1. first I compile the C++ main

gcc -c main.cc -I/usr/local/netshare/lib/g++-include

2. compile the ada package that contains all the ada functions/procedures
that will be called from C++

gcc -c my_pkg.adb

3. bind my_pkg.ali (the .ali file is generated from step 2) :

gnatbind -n my_pkg.ali 

4. link everything togother (with the C++ libraries also offcourse)

gnatlink -L/usr/local/netshare/lib my_pkg.ali main.o -lg++ -lstdc++ -o main


main.cc
------
#include <iostream.h>
#include <stdio.h>

extern "C" { void Increment( int* ); 
             int  Sum( int[] , int );
             int  Sum_Version2( int[] , int );
             void Modify( int[] , int );
             void adainit();
             void adafinal();
           }

main()
{
 int i=0;
 int a[5]= {0,1,2,3,4}; 

 adainit();

 Increment(&i);  // we pass address since it is declared IN OUT in ada

 cout<<" after calling Ada, i="<<i<<endl;

 i = Sum(a,sizeof(a)/sizeof(int)); 
                       
 cout<<" in C++, first sum is "<<i<<endl;

 // Now call ada to modify elements of array
 Modify(a,sizeof(a)/sizeof(int));

 i = Sum(a,sizeof(a)/sizeof(int)); 
 cout<<" In C++, Ada did another sum, and the result is "<<i<<endl;

 i = Sum_Version2(a,sizeof(a)/sizeof(int)); 
 cout<<" In C++, Ada did Sum_Version2, and the result is "<<i<<endl;

 adafinal();

 return 0;

}

my_pkg.ads
---------
with Interfaces.C; 

package My_Pkg is
  package C renames Interfaces.C;

  type Ada_Int_Array_type is array(Integer range <>) of Integer;

  type C_Int_Array_type is array(integer range <>) of C.int;

  procedure Modify(A: in out C_Int_Array_type ; Len: in C.Size_t );

  function Sum(A: in C_Int_Array_type ; Len: in C.Size_t ) return C.int;

  function Sum_version2(A: in C_Int_Array_type ; Len: in C.Size_t ) 
                                                           return C.int;

  procedure Increment(I:in out C.int);

  function To_Ada(A: in C_Int_Array_Type; Len: in C.Size_t) 
                                                   return Ada_Int_Array_type;
end My_Pkg;




my_pkg.adb
==========

with Ada.Text_Io; use Ada.Text_Io;
package body My_Pkg is


--
--
--
function Sum(A: in C_Int_Array_type; Len : in C.Size_t ) return C.Int is
The_Sum: Integer;
Length: Integer := Integer(Len);
begin
   The_Sum := 0;
   Put_Line("array from C++ has length " & integer'Image(Length));
   for I in 0 .. Length-1 loop
       Put_line("array from C++ at index " & integer'Image(I) & " is " &
            C.int'Image(A(I)));
       The_Sum := The_Sum + Integer(A(I));
   end loop;
   return C.Int(The_Sum);
end Sum;
pragma Export(C,Sum,"Sum");



--
--
--
function Sum_version2(A: in C_Int_Array_type; Len : in C.Size_t ) 
                                                            return C.Int is
The_Sum: Integer;
Length: Integer := Integer(Len);
Ada_Int_Array: Ada_Int_Array_Type(1..Integer(Len));
begin
   Ada_Int_Array := To_Ada(A,len);
   The_Sum := 0;
   Put_Line("array from C++ has length " & integer'Image(Length));
   for I in 1 .. Length loop
       The_Sum := The_Sum + Ada_Int_array(I);
   end loop;
   return C.Int(The_Sum);
end Sum_version2;
pragma Export(C,Sum_Version2,"Sum_Version2");

--
--
--
procedure Modify(A: in out C_Int_Array_type; Len: in C.Size_t ) is
Length: Integer := Integer(Len);
use Interfaces.C;
begin
   for I in 1 .. Length loop
       A(I) := A(I) * 2;
   end loop;
end Modify;
pragma Export(C,MOdify,"Modify");

--
--
--
procedure Increment(I:in out C.int) is
use Interfaces.C;
begin
   Put_Line("Number passed in is" & C.int'Image(I));
   I := I+1;
end Increment;
pragma Export(C,Increment,"Increment");


--
--
---
function To_Ada(A: in C_Int_Array_Type;Len: C.Size_t) 
                                            return Ada_Int_Array_Type is
Back : Ada_Int_Array_Type(1..Integer(Len));
begin
 for I in Back'Range loop
     Back(I) := Integer(A(I-1));
 end loop;
 return Back;
end To_Ada;
begin
 null;
end My_Pkg;

Nasser
-- 
Nasser Abbasi. C/C++/Ada Solaris. GeneAssist - A client/server 
application for Nucleic acid and protein sequence search and analysis.
PE-Applied BioSystem division. email:  nasser@apldbio.com   
MSEE, MSCS, MSCE, FM (and Karpov is my chess hero! ..).




^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: on calling Ada from C/C++
  1996-06-13  0:00 ` Nasser Abbasi
  1996-06-13  0:00   ` Robert Dewar
@ 1996-06-13  0:00   ` Robert Dewar
  1 sibling, 0 replies; 4+ messages in thread
From: Robert Dewar @ 1996-06-13  0:00 UTC (permalink / raw)



Nasser said

"If you want to call more than one ada subprogram from C++, I found that
gnatlink (the gnat ada linker) does not take more than .ALI file, this
means that you can not have separate ada functions/procedure in separate
files and have C++ call them (unless I missed something), the easy solution
is to put them in one ada package, this way you can have one .ALI file ,
and the gnatlink is happy.
"

That's wrong, use the -n switch for gnatbind, that's what it is for!





^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: on calling Ada from C/C++
  1996-06-13  0:00 ` Nasser Abbasi
@ 1996-06-13  0:00   ` Robert Dewar
  1996-06-13  0:00   ` Robert Dewar
  1 sibling, 0 replies; 4+ messages in thread
From: Robert Dewar @ 1996-06-13  0:00 UTC (permalink / raw)



Nasser said

"gnatbind -n my_pkg.ali"

odd, so you do know about -n, so how come the fiddling around? Just put
a list of ali files after the -n switch!





^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~1996-06-13  0:00 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1996-06-12  0:00 on calling Ada from C/C++ Nasser Abbasi
1996-06-13  0:00 ` Nasser Abbasi
1996-06-13  0:00   ` Robert Dewar
1996-06-13  0:00   ` Robert Dewar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox