Contents


  1. About this tutorial and Forward
  2. Code Structure
  3. Packages
  4. Control Statements
  5. Conditions
  6. Loops
  7. Structures

About this tutorial and Forward


The following original tutorial was written by Quentin Ochem and published by AdaCore and has been adapted to use the optional Ada++ syntax. So please report/attribute any errors to our Github page

See the full original version here .

Ada++ is truly a general purpose, multiple paradigm language that allows the programmer to employ or avoid features like run-time contract checking, tasking, object oriented programming, and generics. Efficiently programmed Ada++ is employed in device drivers, interrupt handlers, and other low-level functions. It may be found today in devices with tight limits on processing speed, memory, and power consumption. But the language is also used for programming larger interconnected systems running on workstations, servers, and supercomputers.

Code structure


C++ programming style usually promotes the use of two distinct files: header files used to define specifications (.h, .hxx, .hpp), and implementation files which contain the executable code (.c, .cxx, .cpp). However, the distinction between specification and implementation is not enforced by the compiler and may need to be worked round in order to implement, for example, inlining or templates.

Ada++ is superficially similar to the C++ case: Ada++ compilation units are generally split into two parts, the specification and the body. However, what goes into those files is more predictable for both the compiler and for the programmer. With GNAT, compilation units are stored in files with a .ads extension for specifications and with a .adb extension for implementations.

Without further ado, we present the famous “Hello World” in three languages for comparison:

Ada++

use Ada.Wide_Text_IO;
proc Main:
{
  Put_Line ("Hello World");
}

C++

#include <iostream>
using namespace std;
int main(int argc, const char* argv[]) {
  cout << "Hello World" << endl;
}

Java

public class Main {
  public static void main(String [] argv) {
    System.out.println ("Hello World");
  }
}

Packages


The first line of Ada++ we see is the with clause, declaring that the unit (in this case, the Main subprogram) will require the services of the package Ada.Text_IO. This is different from how #include works in C++ in that it does not, in a logical sense, copy/paste the code of Ada.Text_IO into Main. The with clause directs the compiler to make the public interface of the Ada.Text_IO package visible to code in the unit (here Main) containing the with clause.

The use clause is the equivalent of using namespace in C++, or import in Java (though it wasn’t necessary to use import in the Java example above). It allows you to omit the full package name when referring to withed units. Without the use clause, any reference to Ada.Text_IO items would have had to be fully qualified with the package name. The Put_Line line would then have read: Ada.Text_IO.Put_Line ("Hello World");

The word “package” has different meanings in Ada++ and Java. In Java, a package is used as a namespace for classes. In Ada++, it’s often a compilation unit. As a result Ada++ tends to have many more packages than Java. Ada++ package specifications (“package specs” for short) have the following structure:

Package Specification - similar to a C++ header or Java interface

pkg Package_Name {
  -- Public declarations
priv
  -- Private declarations
}

Package Body

pkg body Package_Name {
  -- Implementation
}

The private reserved word is used to mark the start of the private portion of a package spec. By splitting the package spec into private and public parts, it is possible to make an entity available for use while hiding its implementation. For instance, a common use is declaring a record (Ada++’s struct) whose fields are only visible to its package and not to the caller. This allows the caller to refer to objects of that type, but not to change any of its contents directly. The package body contains implementation code, and is only accessible to outside code through declarations in the package spec.

Control Statements


Ada++ requires variable declarations to be made in a specific area called the declarative part, seen here before the begin keyword. Variable declarations start with the identifier in Ada++, as opposed to starting with the type as in C++ and Java (also note Ada++’s use of the : separator). Specifying initializers is different as well: in Ada++ an initialization expression can apply to multiple variables (but will be evaluated separately for each), whereas in C++ and Java each variable is initialized individually. In all three languages, if you use a function as an initializer and that function returns different values on every invocation, each variable will get initialized to a different value.

Ada++

--
-- Declares and modifies Ints
--
proc Main: -- In Ada++ "is" can now be replaced with a colon

  -- Variable declarations
  A, B : Int_32 := 0;
  C    : Int_32 := 100;
  D    : Int_32;
{
  A++;
  A-1;
  A+1;
  
  -- Regular addition
  D := A + B + C;
}

C++

/*
 * Declares and modifies ints
 */
int main (int argc, const char* argv[]) {
  // Variable declarations
  int a = 0, b = 0, c = 100, d;

  // C++ shorthand for incrementation
  a++;
  a--;

  // Regular addition
  d = a + b + c;
}

Java

/*
 * Declares and modifies ints
 */
public class Main {
  public static void main(String [] argv) {
    // Variable declarations
    int a = 0, b = 0, c = 100, d;

    // Java shorthand for incrementation
    a++;
    a--

    // Regular addition
    d = a + b + c;
  }
}

Conditions


In Ada++, everything that appears between the if and { is the conditional expression—no parentheses required. Comparison operators are the same, except for equality (=) and inequality (/=). The english words not, and, and or replace the symbols !, &, and |, respectively, for performing boolean operations.

Ada++

if Variable > 0 {
  Put_Line (" > 0");
elsif Variable < 0:
  Put_Line (" < 0");
else
  Put_Line (" = 0");
}

C++

if (Variable > 0) {
  cout << " > 0" << endl;
} else if (Variable < 0) {
  cout << " < 0" << endl;
} else {
  cout << " = 0" << endl;
}

Java

if (Variable > 0) {
  System.out.println(" > 0");
} else if (Variable < 0) {
  System.out.println(" < 0");
} else {
  System.out.println(" = 0");
}

It’s more customary to use && and || in C++ and Java than & and | when writing boolean expressions. The difference is that && and || are short-circuit operators, which evaluate terms only as necessary, and & and | will unconditionally evaluate all terms. In Ada++, and and or will evaluate all terms; and then and or else direct the compiler to employ short circuit evaluation. Here are what switch/case statements look like:

Ada++

case Variable:
  when 0      => Put_Line ("Zero");
  when 1 .. 9 => Put_Line ("Positive Digit");
  when 10 | 12 | 14 | 16 | 18 =>
    Put_Line ("Even Number between 10 and 18");
  when others => Put_Line ("Something else");
}

C++

switch (Variable) {
  case 0:
    cout << "Zero" << endl;
    break;
  case 1: case 2: case 3: case 4: case 5:
  case 6: case 7: case 8: case 9: 
    cout << "Positive Digit" << endl;
    break;
  case 10: case 12: case 14: case 16: case 18:
    cout << "Even Number between 10 and 18" << endl;
    break;
  default:
    cout << "Something else";
}

Java

switch (Variable) {
  case 0:
    System.out.println ("Zero");
    break;
  case 1: case 2: case 3: case 4: case 5:
  case 6: case 7: case 8: case 9: 
    System.out.println ("Positive Digit");
    break;
  case 10: case 12: case 14: case 16: case 18:
    System.out.println ("Even Number between 10 and 18");
    break;
  default:
    System.out.println ("Something else");
}

In Ada++, the case and end case lines surround the whole case statement, and each case starts with when. So, when programming in Ada++, replace switch with case, and replace case with when. Case statements in Ada++ require the use of discrete types (integers or enumeration types), and require all possible cases to be covered by when statements. If not all the cases are handled, or if duplicate cases exist, the program will not compile. The default case, default: in C++ and Java, can be specified usingwhen others => in Ada++. In Ada++, the break instruction is implicit and program execution will never fall through to subsequent cases. In order to combine cases, you can specify ranges using..and enumerate disjoint values using | which neatly replaces the multiple case statements seen in the C++ and Java versions.

Loops



Ada++’s for loop, however, is quite different from that in C++ and Java. It always increments or decrements a loop index within a discrete range. The loop index (or “loop parameter” in Ada++ parlance) is local to the scope of the loop and isimplicitly incremented or decremented at each iteration of the loop statements; the program cannot directly modify itsvalue. The type of the loop parameter is derived from the range. The range is always given in ascending order even if the loop iterates in descending order. If the starting bound is greater than the ending bound, the interval is considered to be empty and the loop contents will not be executed. To specify a loop iteration in decreasing order, use the reverse reserved word. Here are examples of loops going in both directions

Ada++

--  Outputs 0, 1, 2, ..., 9
for Variable in 0 .. 9 {
  Put_Line (Variable'Img);
}

--  Outputs 9, 8, 7, ..., 0
for Variable in reverse 0 .. 9 {
  Put_Line (Variable'Img);
}

C++

//  Outputs 0, 1, 2, ..., 9
for (int Variable = 0; Variable <= 9; Variable++) {
  cout << Variable << endl;
}

//  Outputs 9, 8, 7, ..., 0
for (int Variable = 9; Variable >=0; Variable--) {
  cout << Variable << endl;
}

Java

//  Outputs 0, 1, 2, ..., 9
for (int Variable = 0; Variable <= 9; Variable++) {
  System.out.println (Variable);
}

//  Outputs 9, 8, 7, ..., 0
for (int Variable = 9; Variable >=0; Variable--) {
  System.out.println (Variable);
}

Ada++ uses the Integer type’s ‘Image attribute to convert a numerical value to a String. There is no implicit conversion between Integer and String as there is in C++ and Java. We’ll have a more in-depth look at such attributes later on. It’s easy to express iteration over the contents of a container (for instance, an array, a list, or a map) in Ada++ and Java. For example, assuming that Int_List is defined as an array of Integer values, you can use:

Ada++

for I of Int_List {
  Put_Line (I'Img);
}

Java

for (int i : Int_List) {
  System.out.println (i);
}

Structures



Ada++

type Vec3 : rec {
  X, Y, Z : Int_32;
}

Obj_A, Obj_B : Vec3;

C++

struct Vec3 {
  int X, Y, Z;
} Obj_A,  Obj_B;

Java

@Struct
public static class Vec3 {
  public int x, y, z;
}

Vec3 Obj_A = new Vec3();
Vec3 Obj_B = new Vec3():





More coming soon!