
Gem #116: Ada and C++ Exceptions
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…”.





