10

I need to pass 2 pieces of data from an Ada program to some C++ code for processing.

  • Data - double.
  • Time - unsigned 64 bits.

I was able to make a procedure in Ada that worked with my C++ method using a Long_Float (double in C++) and Integer (int in C++, obviously not 64-bits though). I used the following code (code not on me so syntax might be slightly off):

procedure send_data (this : in hidden_ptr; data : in Long_Float; time : in Integer);
pragma import (CPP, send_data, "MyClass::sendData");

Now that that's working, I'm trying to expand the time to the full 64-bits and ideally would like to have an unsigned long long on the C++ side. I don't see any types in Ada that match that so I created my own type:

type U64 is mod 2 ** 64;

When using that type with my send_data method I get an error saying there are no possible ways to map that type to a C++ type (something along those lines, again don't have the code or exact error phrase on me).

Is there a way to pass a user-defined type in Ada to C++? Perhaps there's another type in Ada I can use as an unsigned 64-bit value that would work? Is there a way to pass the address of my U64 type as a parameter to the C++ method instead if that's easier? I'm using the green hills adamulti compiler v3.5 (very new to ada, not sure if that info helps or not). Examples would be greatly appreciated!

Xeelot
  • 101
  • 1
  • 3
  • 6
    `mod 2 ** 64` should be correct. When I compile your example using that type with GNAT, it doesn't complain. Perhaps your Ada compiler doesn't think that C++ (or the C++ compiler it targets) supports a 64-bit unsigned type; standard C++ didn't get `unsigned long long` until the 2011 ISO standard. – Keith Thompson Sep 20 '12 at 01:18
  • @KeithThompson - Bah! Make these good comments of yours answers Keith, so my upvotes mean something, I can properly comment on them, and they can be accepted if they end up being the answer. – T.E.D. Sep 20 '12 at 13:46
  • You could try adding `pragma Convention (CPP, U64);`. – Simon Wright Sep 20 '12 at 15:03
  • Is your time actually one field or is it a record structure with seconds & milliseconds fields ? If so you could split it easily into 2 parameters... – NWS Sep 20 '12 at 16:20
  • Had a bit of time to play around with it again today. @Simon Wright Unfortunately pragma convention (CPP, U64) had no noticeable effect that I could see when added, received the same errors with everything I tried. – Xeelot Sep 20 '12 at 18:18
  • @NWS The time is one big 64-bit field, not a record structure. I'm considering passing it as 2 32-bit fields and just putting it back together on the C++ side. Not as elegant, but easily implemented. – Xeelot Sep 20 '12 at 18:18
  • 1
    Instead of `unsigned long long`, which could have different sizes in different implementations, try `std::uint64_t`. – Anthony Oct 27 '12 at 08:27

2 Answers2

1

As an addendum to @KeithThompson's comment/answer...

Ada's officially supported C interface types are in Interfaces.C, and there's no extra-long int or unsigned in there (in the 2005 version. Is the 2012 version official yet?).

You did the right thing to work around it. If your compiler didn't support that, more drastic measures will have to be taken.

The first obvious thing to try is to pass the 64-bit int by reference. That will require changes on the C side though.

I know C/C++ TIME structs tend to be 64-bit values, defined as unioned structs. So what you could do is define an Ada record to mimic one of those C structs, make sure it gets laid out the way C would (eg: with record representation and size clauses), and then make that object what your imported routine uses for its parameter.

After that you'll have to pull nasty parameter tricks. For example, you could try changing the Ada import's side of the parameter to a 64-bit float, unchecked_converting the actual parameter into a 64-bit float, and trying to pass it that way. The problem there is that a lot of CPU's pass floats in different registers than ints. So that likely won't work, and if it does it most certianly isn't portable.

There may well be other ways to fake it out, if you figure out how your compiler's CPP calling convention works. For example, if it uses two adajacent 32-bit registers to pass 64-bit ints, you could split your Ada 64-bit int into two and pass it in two parameters on the Ada side. If it passes 64-bit values by reference, you could just tell the Ada side that you're passing a pointer to your U64. Again, these solutions won't be portable, but they would get you going.

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
  • 1
    Thanks for the suggestions both KeithThompson and T.E.D. I tried creating a "type U64_ptr is access U64" and passing the U64_ptr as a pointer rather than the U64 type as a value. I received the same error, There is no compatible C++ type to map to U64. I also tried an unchecked_conversion to a long_float, passing it to a double on the C++ side and casting it to an unsigned long long and received some funky values (not what I expected, haven't investigated further). I'll keep playing around with it when I can and post back any more info I can find, thanks again! – Xeelot Sep 20 '12 at 18:26
  • @Xeelot - "Funky values" usually indicates one side was dereferencing more than the other side. – T.E.D. Sep 20 '12 at 18:58
  • @Xeelot -"Funky Values" are you sure you are not also crossing a big/little endian boundary ? – NWS Sep 21 '12 at 08:29
  • 1
    @NWS Great suggestion, but I checked the bits and I don't think that's the issue. I talked it over with the team and we just decided to take the easy approach and pass 2 32-bit values. Just made a method on the C++ side to put them back together as a 64-bit integer and then call the method we originally wanted to. Hate giving up on a solid problem like this, but deadlines are not my friend. Thanks again all for the help! – Xeelot Sep 25 '12 at 23:06
0
Put_Line("First = " & Interfaces.C.long'Image(Interfaces.C.long'First));
Put_Line("Last = " & Interfaces.C.long'Image(Interfaces.C.long'Last));

result:

First = -9223372036854775808
Last =  9223372036854775807
buddemat
  • 4,552
  • 14
  • 29
  • 49
Kirill
  • 1
  • 2
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 01 '21 at 16:10
  • I don't know Ada, but if you're trying to use C's `long` type, you'll have a problem on a 32-bit system. To guarantee at least 64 bits you must use `long long` instead. – Ruslan Dec 01 '21 at 22:01