Gem #116: Ada and C++ Exceptions

by Quentin Ochem—AdaCore

 

Let’s get started…

So we’ve decided to have some fun and are building an application that combines Ada and C++. So far, so good. We used the C++ to Ada binding generator to bind the C++ classes directly to Ada, and extend them. However, there’s something that needs to be considered carefully –- what would happen if an exception were thrown/raised by C++ vs. Ada code? Let’s try it out:

Step 1 — The C++ code

We’re going to write some very simple C++ code, just two classes, one inheriting from the other, and overriding a “compute” primitive:

class COperation {
  public:
  virtual int compute (int a, int b);
  COperation ();
};

class CDivision : public COperation {
  public:
  virtual int compute (int a, int b);
  CDivision ();
};

int CDivision::compute (int a, int b) {
  if (b == 0) {
    throw new Problem ("Division by 0 in C++!");
  } else {
    return a / b;
  }
}

In the computation of CDivision, we’ll raise a C++ exception if a divide-by-zero occurs. Let’s add a second subprogram doing a dynamic dispatch:

void cpp_main (COperation & op) {
  try {
     cout << op.compute (1, 0);
  } catch (...) {
    cout << "Unknown exception caught, rethrowing..." << "
";
    throw;
  }
}

This function calls the compute operation, catches the exception to display a message, and then rethrows it.

We’re now going to bind this to Ada. Assuming all specifications are in a file base.hh, a simple call to gcc will do it:

g++ -fdump-ada-spec base.hh

Step 2 — Extending the C++ code in Ada

There is an implementation of CDivision in C++. After the binding phase, let’s implement the same code in Ada:

type Ada_Division is new base_hh.Class_COperation.COperation with
     null record;

function Compute
  (this : access Ada_Division;
   a : int;
   b : int) return int is
begin
   if b = 0 then
      raise Constraint_Error with "Division by 0 in Ada!";
   end if;
   return a / b;
end Compute;

We now have three classes in the application. One root C++ class, one pure C++ child, and one mixed Ada/C++ child. Let’s see how things how things work from there.

Step 3 — Catching a C++ exception in Ada

C++ exceptions do not have a known name once they reach the Ada world. They act just as if they were declared in the body of a package, so the only way to catch them is to use a general exception handler. Consider the following code:

   T_Cpp : aliased base_hh.Class_CDivision.CDivision;
   X : Interfaces.C.Int;
begin 
   X :=  T_Cpp.compute (1, 0);
exception
   when others =>
      Put_Line ("[1] Exception caught...");
end;

This will print “[1] Exception caught…” on the screen.

Step 4 — Handling exceptions across languages

Now let’s be a little bit more ambitious. We’re going to call the cpp_main function with an object coming from Ada:

   T_Ada : aliased Base_Ada.Ada_Division;
begin
   base_hh.cpp_main (T_Ada'Access);
exception
   when Constraint_Error =>
      Put_Line ("[2] Constraint_Error caught...");
   when others =>
      Put_Line ("[2] Exception caught...");
end;

Now, looking back at the cpp_main implementation, we call compute with (1, 0) as the arguments. This will trigger an exception from the Ada implementation, which is going to be caught first by the C++ code, printing “Unknown exception caught, rethrowing…” on the screen. The same exception is then rethrown by the C++ side, ending up back in the Ada code above, printing “[2] Constraint_Error caught…”.


About the Author

Quentin Ochem has a software engineering background, specialized in software development for critical applications. He has over 10 years of experience in Ada development. He works today as a technical account manager for AdaCore, following projects related to avionics, railroad, space and defense industries. He also teaches the avionics standard DO-178B course at the EPITA University in Paris.
Last Updated: 10/13/2017
Posted on: 2/1/2012