AdaCore: Build Software that Matters
A vertical dark blue digital background featuring falling digital numbers above a geometric network of glowing blue hexagons connected by lines with illuminated nodes at the bottom.
Jul 21, 2026

Building and evolving multi-language systems with confidence using GNATpolyglot

Multi-language systems are the norm rather than the exception in high-integrity aerospace and defense applications. Software systems have long lifecycles and are often extended over time, adding new hardware interfaces, connectivity options, or user interfaces. Often these extensions require the use of different languages. This requires the team to maintain the interface between components in different languages, which is non-trivial; it requires expert engineers and often necessitates sub-optimal trade-offs to keep the interface simple. GNATpolyglot, a capability of GNAT Pro, frees development teams to design and evolve systems the way they want.

Introduction

Modern, large software systems often contain multiple languages, especially in the aerospace and defense domains. C language code is fast, but dangerous when accessing low-level hardware. The abstraction and polymorphism of C++ provide scalability. Ada provides type safety and is used for its safety and security in high-integrity layers. SPARK delivers mathematical proof of the absence of runtime errors and can also prove correctness properties. Java delivers run–everywhere capabilities at the additional expense of some runtime overhead and garbage collection. Rust is the new kid on the block with increased memory safety and an enthusiastic community behind it.

Multi-language systems

Choosing a single language for a system means compromises at some layer; choosing multiple languages means some sort of conversion logic between components in different languages. This conversion logic is not trivial; it requires language and domain experts to design and maintain that layer as the software system evolves. It requires thinking about memory management, exception management, concurrency, and many other small bits; a mistake in this layer is typically hard to find and can pop up in unexpected code paths.

The programming language experts at AdaCore have automated the hardest part of multi-language systems. They have distilled a decade of experience in multi-language integration into a tool that generates the binding layer for you. This tool, GNATpolyglot, is now available as a public beta in GNAT Pro starting with version 26.2, and is available now.

GNATpolyglot generates the binding

GNATpolyglot creates the binding logic between C++, Java, and Rust to Ada/SPARK code. This binding logic generation is 100% automated from Ada source code. Code generation is a two-step process. The first step is to generate a language-agnostic intermediary from an Ada-based source code set. The second step then creates the language-specific interface for that code, such as C++, Java, or Rust. Calling Ada from C is fairly straightforward; limited glue logic is required in that case.

The glue code allows idiomatic code on one side of the divide to access functionality on the other side. It enables a C++ method to invoke a virtual function on an Ada class (tagged type), and an Ada function to throw an exception so the C++ caller can catch it. Language features such as types, classes, including inheritance, pointers, and exceptions are translated and passed on.

There are, as one would expect, some limitations, such as 64-bit scalars. Access to subprograms (no callbacks across a binding) and several others. The limitations are listed in the documentation, and the team is keen on feedback to receive customer feedback on them as part of the beta program.

The main use case is to call Ada code from C++, Java, or Rust, which is the pattern we see most frequently. In this pattern, the C++, Java, or Rust application manages the larger program, spawns the user interface, reads data from network interfaces, and then interacts with the Ada code to perform operations on that data, or interacts with a high-integrity layer of IO devices such as sensors and actuators.

Maintainability

Creating the binding is only the first step. Software designs evolve as the team discovers more about the problem and solution domains. The binding needs to evolve as well. New classes and methods are added, parameters, types, exceptions, and other evolutions need to happen every sprint.

With a manually designed binding, this is a serious limitation. Maintaining the binding requires the attention of language and domain experts. They need to adjust the binding, test, and integrate. This requires the team to spend time and effort on the binding, but it also creates a latency in the collaboration; logic can only flow over the newly designed binding once the code has been modified, tested, and integrated. This adds, at best, days to the schedule, but typically weeks.

GNATpolyglot removes that latency. The binding is generated, so once the callee is modified, the binding code is re-generated, the application recompiled and re-linked, and the binding is functional within minutes.

Examples

GNATpolyglot provides a set of buildable examples for users to get started and to illustrate the various concepts. Below is a sample multi-language version of the classic Hello World program:

In its Ada form, the HelloWorld is as simple as you would expect:

Example.ads:

package Example is

   procedure Hello;
   
   procedure Hello (Name: String);

end Example;
with Ada.Text_IO; use Ada.Text_IO;

package body Example is

   procedure Hello is
   begin
       Put_Line ("Hello world!");
   end Hello;

   procedure Hello (Name: String) is
   begin
       Put_Line ("Hello " & Name & "!");
   end Hello;
   
end Example;

Then, from C++, you would include the proxy header file, instantiate the class, and call the function on it. Naturally, you would have to set up a build environment to include the proper libraries as well, but GNATpolyglot generates the appropriate GPR (GNAT Project file) file as well:

#include <iostream>

#include "../2cpp/include/example.h"
#include "gnatpolyglot_ada_strings.h"

int main() {
   example::hello();
   example::hello("C++");
}

The invocation is similarly simple for Java:

import com.adacore.libex0.example.ExamplePackage;

import com.adacore.gnatpolyglot.runtime.ada2java.PolyglotString;

public class Main {
   public static void main(String[] args) {
       ExamplePackage.hello();
       ExamplePackage.hello(new PolyglotString("Java"));
   }
}

The Rust example needs a footnote. Since Rust does not support overloading, the Ada procedure Hello() is named hello_1() in Rust, since hello() is the constructor.

use ex0::example;
use ex0::ada::strings::PolyglotString;

fn main() {
   example::hello();
   example::hello_1(&PolyglotString::from("Rust"));
}

Summary

A key use case of GNATpolyglot is to allow developers to choose the right language for the right job. In other words, if there is a particular piece of code that is considered to be of high importance in your application, you can now implement that logic in Ada or SPARK and access it from other components.

And yes, this includes hardware access, as well as the static guarantees of the absence of runtime errors and functional correctness that SPARK can provide. Say you have a piece of code that handles encryption, decryption, and data sanitation for data received from the internet. Or maybe you need to perform hardware access, and Ada Representation Clauses would help avoid problems when developers overwrite registers they shouldn't.

Interested in giving GNATpolyglot a try? You can pick it up from the continuous releases on the GNAT Tracker customer portal and try it for yourself, or reach out to your AdaCore contact to request a demonstration and arrange access.

Author

Mark Hermeling

Headshot
Head of Technical Marketing, AdaCore

Mark has over 25 years’ experience in software development tools for high-integrity, secure, embedded and real-time systems across automotive, aerospace, defence and industrial domains. As Head of Technical Marketing at AdaCore, he links technical capabilities to business value and is a regular author and speaker on on topics ranging from the software development lifecycle, DevSecOps to formal methods and software verification.

Blog_

Latest Blog Posts