Reputation: 708
I'm working on some Ada code which I have to call from C and I've encountered a problem which I can't solve and don't know why it's happening.
Here is a test project to illustrate the problem:
lookup.ads
with Interfaces.C; use Interfaces.C;
package lookup is
procedure Printf(str : in Interfaces.C.char_array; i : in Positive);
pragma Import(C, printf, "printf");
procedure PrintLookup;
pragma Export(C, PrintLookup, "print_lookup");
end lookup;
lookup.adb
with Interfaces.C; use Interfaces.C;
package body lookup is
-- Month_Length : constant array (1..12) of Positive := (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
Month_Length : constant array (1..12) of Positive := (4 | 6 | 9 | 11 => 30, 2 => 28, others => 31);
procedure PrintLookup is
begin
printf("Month_Length(5): %d"&To_C(ascii.LF)&To_C(ascii.NUL), Month_Length(5));
end PrintLookup;
end lookup;
main.adb
with lookup;
procedure main is
begin
lookup.PrintLookup;
end main;
main.c
extern void print_lookup();
int main()
{
print_lookup();
return 0;
}
And I have a simple makefile to build it:
BUILD=ada
GM=gnatmake
CC=gcc
LIB=-L/usr/lib/gcc/i686-linux-gnu/4.9/adalib
ifeq ($(BUILD),ada)
main:
$(GM) lookup.adb main.adb
else
main: lookup.o main.o
$(CC) $(LIB) lookup.o main.o -o $@ -lgnat
lookup.o:
$(GM) lookup.adb
main.o:
$(CC) -c main.c
endif
.PHONY: clean
clean:
rm -f lookup.ali lookup.o
rm -f main.ali main.o
rm -f main
The makefile will generate an executable, called main. If the BUILD variable in the first line of the makefile is set to ada, it will use the Ada main.adb, otherwise the C main.c
Now, here comes the problem: if in the lookup.adb I use the first variant of the Month_Length array (which is commented out right now), I get the following output for both mains, which is correct:
Month_Length(5): 31
But in case of the other array (which is called a lookup table), the C variant returns 0:
Month_Length(5): 0
Does anyone have any idea why the lookup table array returns 0 when called from C? Does anyone encountered this issue? What am I missing? I appreciate your help.
Upvotes: 4
Views: 289
Reputation: 3341
As a complement answer, I remembered that there was an alternative that would make the call to init() automatically.
Simon Wright pointed out his detailed answer to this question
When a DLL is loaded, Windows systematically invokes a routine called DllMain. It would therefore be possible to call adainit directly from DllMain without having to provide an explicit initialization routine. Unfortunately, it is not possible to call adainit from the DllMain if your program has library level tasks because access to the DllMain entry point is serialized by the system (that is, only a single thread can execute “through” it at a time), which means that the GNAT run time will deadlock waiting for the newly created task to complete its initialization.
see this link
one can also have a look at detailed approach here
Upvotes: 2
Reputation: 708
As Vroomfondel mentioned in the comment, adainit must be called to initilise ADA. Here are the modifications I made to get this working:
Here is the makefile:
BUILD=c
GM=gnatmake
GB=gnatbind
CC=gcc
LIB=-L/usr/lib/gcc/i686-linux-gnu/4.9/adalib
ifeq ($(BUILD),ada)
main:
$(GM) lookup.adb main.adb
else
main: lookup.o main.o
$(CC) $(LIB) lookup.o b~lookup.o main.o -o $@ -lgnat
lookup.o:
$(GM) lookup.adb
$(GB) -n lookup.ali
$(GM) b~lookup.adb
main.o:
$(CC) -c main.c
endif
.PHONY: clean
clean:
rm -f lookup.ali lookup.o
rm -f b~lookup.*
rm -f main.ali main.o
rm -f main
gnatbind generates the b~lookup.ads and b~lookup.adb files which will contain the adainit() and adafinal() functions, then I've built them with gnatmake (I had to build because i'm not using gnatlink) and I've included the generated b~lookup.o file in the linking part.
The main.c had to be modified as follows (simply calling the init and final functions before and after the ADA call):
extern void print_lookup();
extern void adainit();
extern void adafinal();
int main()
{
adainit();
print_lookup();
adafinal();
return 0;
}
The rest remains the same.
Upvotes: 3