Using GNAT Ada and Gnu C++, I'm interfacing an Ada piece of code with a c++ wrapper and I'd like to catch Ada exceptions properly when running this (stupid) code:
with ada.text_io;
package body ada_throw is
procedure ada_throw is
begin
ada.text_io.put_line ("hello");
raise program_error;
end ada_throw;
end ada_throw;
relevant spec code is:
package ada_throw is
procedure ada_throw;
pragma export (convention => C, entity => ada_throw, external_name => "ada_throw");
end ada_throw;
Whe doing this on the C++ side:
#include <iostream>
extern "C"
{
void ada_throw();
void adainit();
}
int main()
{
adainit();
ada_throw();
std::cout << "end of program" << std::endl;
return 0;
}
I'm getting this:
hello
raised PROGRAM_ERROR : ada_throw.adb:8 explicit raise
So the exception mechanism works, I don't get the last print of my C++ program and the return code is non-zero.
Now I want to catch the exception. If I use catch(...)
it works, but I can't get the explicit error message anymore, so I tried this:
#include <iostream>
#include <cxxabi.h>
extern "C"
{
void ada_throw();
void adainit();
}
int main()
{
adainit();
try
{
ada_throw();
}
catch (abi::__foreign_exception const &e)
{
std::cout << "exception" << std::endl;
}
std::cout << "end of program" << std::endl;
return 0;
}
it works properly, I get:
hello
exception
end of program
The only catch is that abi::__foreign_exception
doesn't have a what()
method, so I cannot get hold of the meaningful error message.
And debugging the program to try to hack into e
is also a dead end since it's just a null pointer with the proper type:
(gdb) p &e
$2 = (const __cxxabiv1::__foreign_exception *) 0x0
Is there a way to get it from the C++ ?
a-except.ads
. The file contains a line that I believed could be a clue :Machine_Occurrence : System.Address; -- The underlying machine occurrence. For GCC, this corresponds to the _Unwind_Exception structure address.
But since you have no valid pointer ... – Overissuefunction Get_Exception_Machine_Occurrence (X : Exception_Occurrence) return System.Address; pragma Export (Ada, Get_Exception_Machine_Occurrence,"__gnat_get_exception_machine_occurrence"); -- Get the machine occurrence corresponding to an exception occurrence. It is Null_Address if there is no machine occurrence (in runtimes tha doesn't use GCC mechanism) or if it has been lost (Save_Occurrence doesn't save the machine occurrence).
– OverissueThe other approach is called ’zero cost’ exception handling. With this method, the compiler builds static tables to describe the exception ranges. No dynamic code is required when entering a frame containing an exception handler.[...] Note that in this mode and in the context of mixed Ada and C/C++ programming, to propagate an exception through a C/C++ code, the C/C++ code must be compiled with the -funwind-tables GCC’s option."
– Overissue-funwind_tables
is set by default since the exception propagates to the C++ layer all right. It's just that thee
variable is a null-reference (with latest GNAT 17.2 as well) – EntiretyGNAT.Last_Exception
(don't remember exactly) to get the last thrown exception. Not perfect in multithreaded environment, but would work most of the time (unfortunately the feature is broken, and being fixed at the current time, only for beta releases of the GNATPro edition, so not available to the public / GPL until 2019) – Entirety