Skip to content

Comment on Gnat 2021 GPL Community Edition Ada 202x compiler released

Comments

I'm half-obsessed with C interoperability in a variety of languages. I know Ada (or GNAT) has a C FFI, and I've done a little investigation/experimentation but not much.

Here are a few questions, which probably don't have an answer in the Ada standard but might in specific implementations:

* Is there a way to declare data layouts to have compatibility with C, similar to #[repr(C)] in rust?

* Is there a way to catch C++ exceptions?

* Is there a way to catch C longjmps()?

* Is there a way to have some kind of custom control over ABI issues, e.g. define custom FFIs to langauges with a different ABI? Or anything close to this? For that matter, does any language have something like this or does it not make sense?

* Is there a way to declare data layouts to have compatibility with C, similar to #[repr(C)] in rust?

Yes, and if you have a C header file, gcc can even translate most struct definitions for you. Here's an example of a "thin" binding generated that way: https://github.com/JeremyGrosser/notcursesada/blob/master/sr...

* Is there a way to catch C++ exceptions?

Yes. https://www.adacore.com/gems/gem-114-ada-and-c-exceptions

* Is there a way to catch C longjmps()?

By default, the GNAT runtime uses setjmp/longjmp to implement exceptions. I'm not sure how you'd connect that to a C library's setjmp though.

* Is there a way to have some kind of custom control over ABI issues, e.g. define custom FFIs to langauges with a different ABI? Or anything close to this? For that matter, does any language have something like this or does it not make sense?

ABIs are selected with the Convention aspect. Conventions are implementation defined and would require patches to the compiler to add new ones. GNAT currently supports Assembler, C, CPP, COBOL, and Fortran calling conventions.

"Conventions are implementation defined and would require patches to the compiler to add new ones."

Is the implementation of a Convention isolated enough that it would make sense to be extensible?

One thing I had in mind is something like Postgres. Some functions in Postgres are called in a specific way that converts arguments into an array first, and handles SQL NULL values. If I were to define this as a new Convention, it would inherit a lot from the "C" Convention, but would do some transformations first.

If it could handle a longjmp() as well, then the new Convention would also do whatever setup (manipulate some global variables and call setjmp()) so that the caller doesn't have to worry about the longjmp().

The challenge with handling longjmp() in rust is that you can't call setjmp() as a normal FFI function (because it can return twice, like fork(), which is not a normal control flow). Also, longjmp()ing into rust code is just not defined behavior, even if it "works" for now. I assume there are similar challenges in Ada.

Is the implementation of a Convention isolated enough that it would make sense to be extensible?

Conventions are currently implemented in GNAT as a part of the parser/lexer just like any other syntax element, so it's not easily separated from the rest of the compiler. You could change that behavior, but that's not a small task. I don't think new calling conventions come along very often, so it seems unlikely to be worth the effort.

One thing I had in mind is something like Postgres. Some functions in Postgres are called in a specific way that converts arguments into an array first, and handles SQL NULL values. If I were to define this as a new Convention, it would inherit a lot from the "C" Convention, but would do some transformations first.

I don't know the details of the Postgres implementation, but this sounds to me like the sort of thing that would work well as a generic procedure.

GNAT doesn't use runtime FFI, it dynamically links libraries and maps them into memory the same as any other C program that uses gcc and binutils. I think this makes what you're describing possible, but I'm not familiar enough with how setjmp/longjmp interacts with the stack to be certain without doing more research and testing.

The C interoperability of Ada is very good, you can import/export functions and specify whether records (structs) and arrays must have a C convention. I use it often to access libc and Linux kernel functions/syscalls [1][2].

[1] https://github.com/onox/inotify-ada [2] https://github.com/onox/evdev-ada

Can attest to that. C++ is also OK. You still have to write the 'binding' but the compiler can generate a good part for you. Then you can 'hide' all the C-like things (like 'a pointer is an address', 'an enumerated type is an integer', 'you have to call our free() when you're done'...) behind opaque types, controlled types, etc.

As a simple example you can lookup what the compiler generates as a low-level binding [0] that is wrapped in a higher level API [1].

BTW you can also bind Ada to Java (although support is a bit limited I know places where it's in prod). Don't remember if it's a commercial product or oss.

And for the python people, my company financed a first version of ada-py-bind, inspired by the great pybind11. Still a lot of code to write (or generate) but it works fine.

Thanks onox for those two links!

[0] https://github.com/persan/zeromq-Ada/blob/651ca44cce831c2d71...

[1] https://github.com/persan/zeromq-Ada/blob/651ca44cce831c2d71...

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.