Comparing Ada to Other Languages

This tries to provide a reference for equivalent structures between Ada and C++. I’m filling in the chart a little at a time, N/A means that the language has no equivalent.

This table is incomplete and may be inaccurate. If you notice a discrepancy or a way to improve it, please submit a pull request.

Names used in Examples

Concept

Ada

C++

Rust

Notes

Reference

Access

Reference

Reference

Ada: Access only points to members in storage pool.

Pointer

Access All

Pointer

Pointer

Ada: Access all may point to a storage or non-storage pool address.

Pointer

Access All

Pointer

Pointer

Ada: Access all may point to a storage or non-storage pool address.

Namespace

P, Q, R

P, Q, R

P, Q, R

Ada: Packages also act as elements of compilation units.

Class

Capricorn

Capricorn

Capricorn

Struct

Scorpio

Scorpio

Scorpio

Type

S, T, V, W

S, T, V, W

S, T, V, W

V and W, not U and V to be easier to read.

Variables

A, B, C

a, b, c

a, b, c

Function

Foo, Bar

foo, bar

foo, bar

Concept

Ada

C++

Rust

Notes

Overview

Identifiers

Can’t start with number or underscore, case insensitive

[a-zA-Z_][a-zA-Z0-9_]*

a non-empty unicode identifier

Keywords

Case insensitive, usually lower case

lower case

lower case

Naming Conventions(s)

Ada_Case (types and functions), keywords

camelCase, PascalCase (Unreal), snake_case (STL)

snake_case (functions), PascalCase (types)

Declaration file

FileName.ads

FileName.h

FileName.rs

Ada: .ads files are compiled, unlike headers. Rust:

Definition file

FileName.adb

FileName.cpp

FileName.rs

No separate declaration/specification file is used. C++: Other possible extensions exist (.hpp, .hh, etc.)

Dependency

with Package.Child;

#include "FileName.h"

use my_mod;

C++: Uses preprocessor.

#include <FileName.h>

C++: Usage of <> vs “” is implementation-defined.

Line comment

-- line comment

// line comment

// line comment

Block comment

N/A

/*  */

/* /* nestable */ */

Inline docs

-- before or after element

/** */ or /// (doxygen)

//!, ///

Rust: Includes doc tests.

Program Structure

Compile-time config

N/A

#if #ifndef #ifdef

#[cfg(...)]

Static assert

pragma Assert(cond); pragma Assert(cond, message);

static_assert(expr, "message");

static_assertions

N/A

constexpr

Namespacing

package P

namespace P { }

mod P { }

Child Namespaces

package P.R

namespace P { namespace R {}}

Namespacing

package P

namespace P { }

mod P { }

Namespacing

namespace P::Q { }

Namespace aliasing

package TIO renames Ada.Text_IO;

namespace fs = std::filesystem;

Using namespace

use Ada.Text_IO;

using namespace std;

Using subprograms of type

use type T;

Scope resolution

P.Q.R

P::Q::R

P::Q::R

private with Q;

limited with P;

private package P;

Ensuring stateless behavior

package P with Pure

Ada: Ensures the package has no state and only contains stateless subprograms.

No module initialization required

pragma Preelaborate(P);

Ada: Ensures the package has no initialization routine.

Ensure elaboration immediately after specification

pragma Elaborate_Body;

Ada: Prevents usage of components in a package before they are initialized.

Ensure other package is initialized before this one

pragma Elaborate(P);

Ensure other package and all dependencies are initialized before this one.

pragma Elaborate_All(P);

pragma Restrictions(No_Dependencies => Other_Package)

Memory

Pointer

Ptr : access all T;

T* ptr;

Ada: May access storage pool or any aliased variable.

Pointer to allocation from a specific pool

Ptr : access T;

N/A

Ada: Accesses elements within storage pools, may not point to arbitrary locations. These accesses can only point to members from their pool.

Pointer deference

Ptr.all

*ptr

Ada: Runtime null check might be done.

Reference

Ptr : not null access T;

T& ptr;

Variable used by Pointer

A: aliased T;

N/A

Ada: Required to get an “access” to this value.

Address

Ptr : access T := T'Access(A)

T* ptr = &A;

Address

Ptr : access all T := T'Unchecked_Access(A)

N/A

Constant pointer

Ptr : constant access T;

T* const ptr;

Pointer to constant

Ptr : access constant T;

const T* ptr;

Constant pointer to constant

Ptr : constant access constant T

const T* const ptr;

pragma Restrictions(No_Implicit_Heap_Allocation)

Ada: Prevents even implicit heap allocations made by the compiler.

Prevents allocations to anonymous access types.

pragma Restrictions(No_Anonymous_Allocators)

Ada: This prevents allocating for anonymous access types which results in un-freeable memory.

Dynamic allocation

A : access T := new T;
T* a = new T();

C++: Most newer C++ code prefers the usage of std::unique_ptr or std::shared_ptr.

T* a = new T[n]
std::unique_ptr<T> a = new std::make_unique<T>();
std::shared_ptr<T> a = new std::make_shared<T>();

Control Flow

if

if A then
    statements;
elsif B then
    statements;
else
    statements
end if;
if (A) {
    statements;
}
else if (B) {
    statements;
}
else {
    statements;
}
if A {
    statements;
}
else if B {
    statements;
}
else {
     statements;
}

Ada: Must be null; statement if empty. C++: Braces optional.

while

while A loop
    statements;
end loop;
while (A) {
    statements;
}

do-while

loop
    -- statements
    exit when A;
end loop;
do {
    statements;
} while(A);

value-based loop

for Value in 0 .. 99 loop
    statements;
end loop;
for (int i = 0; i < 99; ++i) {
    statements;
}

iterator-based loop

for Elem of Container loop
    statements;
end loop;
for (const auto& elem : container) {
    statements;
}

Multiple choice

case Value is
    when 0 => Handle_Zero;
    when 1 => Handle_One;
    when 2 .. 4 => Handle_Range;
    when 7 | 9 => Handle_Choices;
    when others => Handle_Default;
end case;
switch (Value) {
    case 0: Handle_Zero(); break;
    case 1: Handle_One(); break;
    case 2: // fallthrough
    case 3: // fallthrough
    case 4: // handle 2 through 4
        Handle_Range(); break;
    case 7: // fallthrough
    case 9:
        Handle_Choices(); break;
    default:
        Handle_Default();
}

Iterate over enum

for Elem in EnumName loop
    statements;
end loop;

start next iteration

N/A

continue

Stop iterating

exit

break

Start exception handling

declare
    statements;
exception
    when A =>
         statements;
    when others =>
         statements;
end;
try {
    statements;
}
catch (A) {
    statements;
}
catch(...) {
    statements;
}

N/A

Ada: Can put exception as ending section of any block of executable statements, such as in package body or declare or the end of a subprogram.

Empty statement

null;
;
do { } while (0);

C++ used to preserve behavior of macros with no behavior.

Label

<<LABEL_NAME>>
label_name:

goto

goto LABEL_NAME
goto label_name;

Expressions

qualified expression

for all A of B => expr
for some A of B => expr

if expression

A : Boolean := (if A then B else C);
bool a = (A ? B : C);

Case expression

A : Integer = (case Value is
                 when 0 => 1,
                 when 1 => 1,
                 when 2 .. 4 => 5,
                 when 5 | 9 => 10,
                 when others => 0);

Ada: Parentheses are required around case expressions.

Mathematics

In-place

A := A + 1;
A += 1;

Pre-increment

N/A

++a;

Post-increment

N/A

a++;

Modulus

mod

%

Remainder

rem

std::div

Exponentiation

A ** B

N/A

Bit shifting

In standard library

A <<= B;
A >>= B;
A = A << B;
A = A >> B;

Boolean

Equality

A = B
A == B

In-Equality

A /= B
A != B

Ada:Inequality (/=) is automatically defined to be the opposite of equality if = is overriden to return a Boolean.

Not

not A
! A

Boolean operators

A or B
A and B
A | B
A & B

C++: Used rarely for optimizations to reduce branching since they make assumptions as to how boolean values are stored.

Short circuiting boolean operators

A or else B
A and then B
A || B
A && B

Exclusive-Or (XOR)

A xor B
A xor B

Implies (not A or B)

(if A then B)

Functions and Procedures

Procedure

procedure Foo(X: in T; Y: in V) is
begin
    statements;
end Foo;
void Foo(T X, V Y) {

}

Function

function Fibonacci(X: Natural) return Natural is
    if X = 0 or X = 1 then
        return X;
    else
        return Fibonacci(X - 1) + Fibonacci(X - 2)
    end if;
end Fibonacci;
int fibonacci(int i) {
    if (x == 0 || x == 1) {
        return x;
    }
    else {
        return fibonacci(x-1) + fibonacci(x-2);
    }
}

Subprogram call (no parameters)

A;
a();
a();

Named Parameters

Foo(Bar1 => Value, Baz => Value2)

Override specifier

overriding procedure Foo
void foo () override

Ensure that a subprogram definition does not override an existing one

not overriding
procedure Foo(obj : in Object)

Pass by pointer

procedure Foo (B : in access Bar)
void foo(Bar* b)

Pass by reference

procedure Foo(B : in Bar)
void foo(Bar& b)

Ada: limited and tagged types are always passed by reference.

Functions and Procedures

Inline

procedure Foo
    with Inline
inline void Foo()
#[inline]
fn foo()

Expression renaming

L2 : Float renames V.Length * V.Length

Using functions for a type unqualified.

use type P.Foo;      -- Make primitive ops visible
use all type P.Foo;  -- Make all ops visible for type

N/A

Ada: Allows functions which use or return a type to be used without the package prefix.

Modifiable parameters

procedure Foo(B : in out Bar)
void Foo(Bar& bar)
fn foo(bar : &mut Bar)

Expression function (Ada)

function Foo return T is (expr)
T foo() { return expr; }

Empty procedure

procedure Foo is null;
void Foo() {}

Types

Statically sized array

type Buffer is array(1 .. 128) of Integer;
int buffer[128];

Ada: Convention seems to be indexes starting from 1, though arrays can be arbitrarily indexed.

Array Access

A(i)
a[i];

Multi-dimensional Array

Mat4 : array (1 .. 4, 1 .. 4) of Float;
float Mat4[4][4];

Built-In Variable length array

type Buffer is array(1 .. N) of Integer;

Ada variable-length arrays can avoid heap allocation and have their bounds determined at runtime, even when stored within types. This behavior is still checked for size constraints.

Semantic type

type Microseconds is new Integer;

Range checks on type

type My_Positive is range 1 .. 10;

N/A

Size

T'Size
sizeof(T)

Alignment

T'Alignment
alignof(T)

Type Aliasing

subtype T is W;
using T = W;
type T = W;
typedef W T

Inferred typing

N/A

auto        a = b
const auto  a = b;
auto&       a = b;
auto&&      a = b;
let A = B;

Type parameterized by value (compile-time)

template <int T = 5>

Type parameterized by value (run-time)

type S(T: t) is record -- ...

Modification of a record at runtime.

Enum range

A'Range

Ada: Treated like a range, similar to 1 .. 3, e.g. can be used like for A in A'Range

Membership test

A in E
A not in E

Ada: Works to see if types meet subtype contraints, also use to determine instance-of relationship.

Type invariant checks

type T is new V with Type_Invariant => Expr(T)

Ada: Type invariants are checked on initialization, when used as a parameter, after subprograms using T in the same package. The type must also be private.

Sum Types

type S is (T, V, W);
std::variant<T, V, W>
enum S { T, V, W }

Coersion (casting)

A := B(C);
B a = static_cast<B>(c);

Object-Oriented Programming

Class-like

type T is private;
class T {};

struct T {
    private:
};
struct T {
    // do not use pub on variables.
}

Abstract Class

type T is interface;
function Foo(A : T) return V is abstract;
procedure Bar(A: in out T) is abstract;
class T {
public:
    V foo() const = 0;
    void bar() = 0;
};

Subprogram call of object-like type

A.B;
a.b();
a.b()

Member access from pointer

A.all.B;  -- Explicit
A.B;      -- Implicit
a->b();

Preventing copying

type X is limited type;
class Foo {
    Foo(Foo&) = delete;
    Foo& operator=(const Foo&) = delete;
};

Preventing moving

N/A

class Foo {
    Foo(Foo&&) = delete;
    Foo&& operator=(Foo&&) = delete;
};

Inheritance

type Foo is Bar with null record;
class Foo : public Bar {};

Ada: null record just means the new type has no additional variables.

Dynamic dispatching

procedure Foo(A : T'Class)
// In parent class
virtual void Foo();

// call
a.foo();  // with T& a
a->foo(); // with T* a

Prevent implicit cast

type T is new W;
class T {
    explicit T(W);
};

Ada: Prevents conversions since new makes a new set of possible values. Ada also does not have mixed-mode arithmetic or implicit conversions of integer <-> float.

Runtime type checking

if A in T then

end if;
if (dynamic_cast<T*>(A)) {

}

Passing parameter by base class

procedure Foo(A : BaseClass'Class)
void foo(BaseClass& class)

Array-like indexing of user-defined type.

type My_Container is tagged type
    with
        Constant_Indexing => Foo
        Variable_Indexing => Bar
-- Foo and Bar are functions defined on the type.
const T& operator[](int i) const { return foo(i); }
T& operator[](int i) { return bar(i); }

Automatic dereference of a handle-type to the handle’s contents.

type Handle(Target: not null access Element) is
    with
        Implicit_Dereference => Element;

-- Old usage, calling Foo
A_Handle.Target.all.Foo

-- New usage
A_Handle.Foo
class Handle {
    T* Target;

public:
    T& operator->() {
        return *Target;
    }

    T& operator*() {
        return *Target;
    }
    //...
};

Iterator for loops for user-defined types.

type My_Container
    with
        Default_Iterator  => Iterate,
        Iterator_Element  => Element_Type;

type Cursor;
function First (M : in My_Container) return Cursor;
procedure Next  (C : in out Cursor);
function Has_Element (C : in Cursor) return Boolean;
class MyContainer {
    MyIterator begin();
    MyIterator end();





};

String Handling

Concatenation

A & B
std::string C = A + B;

Concurrency

Terminology

<>

“Box”. Used for defaults or also “not specified.”

'

“Tick”. Access built-in attributes of types.

ABE

“Access before elaboration”

allocator

“new” construct that allocates storage and returns a pointer. Failure to allocate results in an exception being raised.

access type

Similar to a pointer, a type which refers to the placement of another object in memory.

access-to-object type

Pointer type that points to an object in memory. It can be divided further into access-to-constant vs access-to-variable, named access vs anonymous access, pool-specific access (that can only point to the heap) vs general access (that can point to both heap and stack).

access-to-subprogram type

Pointer type that points to a subprogram (function or procedure).

aggregate

The literal value for a composite object (array, record or container). A comma-separated list of values enclosed in parentheses or square-brackets (for homogeneous collections only, and only since Ada 2022).

aliased

Objects (both variables and components) can be declared explicitly as aliased, so that it is valid to use attribute Access to point to the object.

aspect

Additional specification attached to a declaration, either related to its behavior (like preconditions and postcondition for subprograms) or its representation (like size or alignment for objects).

attribute

Value or function attached to a type or object, which can be retrieved using the syntax Type'Attribute or Object'Attribute. For example, attributes First and Last denote the first and last indexes of a (constrained) array type, or of any array object.

ATC

“Asynchronous transfer of control”

bounded error

The result of a violation of Ada program semantics, when the consequences of the error are precisely bounded by the language. E.g. reading an uninitialized variable may lead to any value of the corresponding base type being read.

completion

An initial declaration for a type, constant, subprogram or package may be completed by a second declaration, called the completion of the initial declaration.

component

A record field or array element.

configuration pragma

A pragma at the very start of a file, or even provided in a separate file depending on the compiler, that applies to the compilation unit as a whole.

controlled type

Type that supports RAII (Resource Acquisition Is Initialization) through the insertion by the compiler of calls to specific procedures at object creation, assignment and end-of-life.

definite type

A type for which which requires no explicit constraint or initial value when declared.

discriminant

Special field in record types, which may be used to control the structure of the type itself, either through a variant-clause (so the presence of other fields depends on the value of the discriminant) or through the constraint on the array subtype for the last field (so the size of this field depends on the value of the discriminant).

entry

The other kind of callable entities, in addition to subprograms. It is used for queued operations called concurrently, as part of a task or protected object API.

erroneous behavior

The result of a violation of Ada program semantics, when the consequences of the error are not bounded by the language. E.g. deactivating runtime checks and violating the corresponding conditions may lead to arbitrary code execution.

indefinite type

A type for which you cannot declare an object without supply bounds a constraint or an initial value.

limited type

An uncopyable type.

parent

Non-abstract tagged type being extended.

pragma

A directive to the compiler. There are many different pragmas defined in Ada, and even more are compiler-specific.

progenitor

Additional interfaces inherited.

qualification

Expression used to verify that an object respects the constraint of a subtype, using the syntax Subtype'Object. This is different from type conversion, as the object and its qualification share the same type.

subprogram

A function (returning a result) or procedure (with no result). This does not include entries of tasks or protected objects, which are used for queued operations called concurrently.

subtype

A type together with additional constraints, like a range of values for a scalar type. An object can be freely converted to a different subtype of the same type, but the corresponding constraint will be checked at runtime if necessary.

tagged type

A type with an associated “tag”, which specifies its type and allows for dynamic dispatch.

type conversion

Expression to change the type of its argument, typically between different scalar types. There are no implicit type conversions in Ada.

unchecked type conversion

Blind conversion of a bit pattern from one type to another, using the predefined generic function Ada.Unchecked_Conversion which must be instantiated with the types of source and target.

Concept

Ada

C++

Rust

Notes

Equality

A = B

Inequality

A /= B

Assignment

A := B

Array Access

A(i)

Range

min .. max

“Box”

<>

Exponentiation

Base ** Exponent

Discrete type

(<>)

Ada: Used in generics to indicate “any discrete type.”

“Tick”

'