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-Z0-9_][a-zA-Z0-9_]*

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)

/** */, //!

Program Structure

Compile-time config

N/A

#if #ifndef #ifdef

#[cfg(...)]

Static assert

N/A

static_assert(expr, "message");

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 to storage pool

Ptr : access T;

N/A

Ada: Accesses elements within storage pools, may not point to arbitrary locations

Pointer to storage pool

Ptr : access T;

N/A

Ada: Accesses elements within storage pool, may not point to arbitrary locations.

Pointer

Ptr : access all T;

T* ptr;

Ada: May access storage pool or any aliased variable.

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 : const 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::unique_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

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

!

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)

xor

xor

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()

where clause

L2 : Float renames V.Length * V.Length

Using functions for a type unqualified.

use type P.Foo;

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

function Foo return T is (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 Integer

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()
a->b();

Preventing copying

type X is limited type;
class Foo {
    Foo(Foo&) = delete;
    Foo& operator=(const 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) { 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;
    }
    //...
};

String Handling

Concatenation

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

Concurrency

Terminology

<>

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

ABE

“Access before elaboration”

access type

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

ATC

“Asynchronous transfer of control

limited type

An uncopyable type.

pregenitor

Additional interfaces inherited.

parent

Non-abstract tagged type being extended.

tagged type

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

indefinite type

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

subprogram

Any function or procedure.

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.”