如何在C++处理程序中捕获Ada异常信息?

13

使用GNAT Ada和Gnu C++,我正在将一个Ada代码片段与一个C++ wrapper进行接口处理,并且希望在运行此(愚蠢的)代码时正确地捕获Ada异常。

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;

相关的规范代码是:

package ada_throw is

   procedure ada_throw;
   pragma export (convention => C, entity => ada_throw, external_name => "ada_throw");

end ada_throw;

当在C++端执行此操作时:

#include <iostream>

extern "C"
{
  void ada_throw();
  void adainit();
}

int main()
{
  adainit();
  ada_throw();
  std::cout << "end of program" << std::endl;
  return 0;
}

我得到了这个:

hello

raised PROGRAM_ERROR : ada_throw.adb:8 explicit raise

所以异常机制起作用了,我的C++程序不会输出最后一行,并且返回代码是非零。
现在我想要捕获异常。如果我使用catch(...)它可以工作,但我无法再获取显式的错误信息,所以我尝试了这个:
#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;
}

它正常工作,我得到:

hello
exception
end of program

唯一的问题是abi::__foreign_exception没有what()方法,所以我无法获得有意义的错误消息。同时,尝试调试程序以侵入e也是死路一条,因为它只是一个具有适当类型的空指针。
(gdb) p &e
$2 = (const __cxxabiv1::__foreign_exception *) 0x0

有没有一种方式可以从C++中获得它?


这实际上取决于您的Ada和C++编译器如何实现异常处理。两种语言的规范不同,需要有意识的设计选择 - 在编译器代码生成和使用库特性方面 - 以确保跨语言边界的兼容性或至少一致的映射。任何解决方案都将针对特定的编译器集合。 - Peter
1
ABI的世界在C接口之外演变得非常缓慢。异常是编译器特定的东西,尽管可能有平台约定它们遵循。 - Khouri Giordano
1
还发现了这个:function Get_Exception_Machine_Occurrence (X : Exception_Occurrence) return System.Address; pragma Export (Ada, Get_Exception_Machine_Occurrence,"__gnat_get_exception_machine_occurrence"); -- 获取与异常实例对应的机器实例。如果没有机器实例(在不使用GCC机制的运行时中)或者已经丢失(Save_Occurrence不保存机器实例),则为Null_Address。 - LoneWanderer
关于ABI文档,可以在这里找到一些线索:https://dev59.com/7WMl5IYBdhLWcg3wHj1O(指向C++ Itanium异常处理 https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html)。这里有一些Ada异常的线索https://www2.adacore.com/gap-static/GNAT_Book/html/node25.htm,图18.2看起来很有趣。 - LoneWanderer
2
我正在与AdaCore GNAT维护者交谈,他们有一个线索:捕获异常,然后调用一些GNAT.Last_Exception(不记得确切的内容)来获取最后抛出的异常。在多线程环境下不完美,但大多数情况下都可以使用(不幸的是,该功能已经损坏,并且正在修复中,仅适用于GNATPro版的beta版本,因此直到2019年才可供公众/ GPL使用)。 - Jean-François Fabre
显示剩余6条评论
2个回答

3
在Ada中,您可以使用标准库的函数Ada.Exceptions.Exception_NameAda.Exceptions.Exception_Message获取有关异常发生的信息。其中之一是提供这两个函数的轻量级绑定,并在需要异常信息时调用它们。(如何将abi::__foreign_exception映射到Ada.Exceptions.Exception_ID由读者自行练习。)
另一个选择是明确告诉Ada编译器您正在另一侧编写C++代码,并使用CPlusPlus而不是C作为导出约定。这可能会使编译器提供C++可以理解的异常。
这只是部分答案,因为我对C++已经很陌生了,但我希望它能帮助您找到正确的方向。

2
CPlusPlus并不存在,但是CPP存在 :) 现在尝试一下。哎呀,Ada编译器似乎无法生成一个正确的C++名称。仍然是相同的_ada_throw符号... - Jean-François Fabre
2
我支持你的第一个选项,但我怀疑你的备选方案行不通,如果将其删除,这个答案会更好。 - user743382

3

根据在C++世界里想要处理异常的目的,编写一个Ada的包装器并调用ada_throw以及管理任何异常可能更容易/更简洁。然后只需从C++中调用这个包装器即可。

如果C++代码必须看到异常并了解发生了什么,那么包装器可以提供类似what()方法,并重新抛出相同或新的(自定义)异常。


1
GNAT.CPP_Exceptions 似乎是执行包装的完美工具。引用其 .ads 文件 -- 此包提供了一个接口,用于引发和处理 C++ 异常。我正在查看 https://www.adacore.com/gems/gem-114-ada-and-c-exceptions。 - LoneWanderer
1
很不幸,Ada Gem 展示了如何在不同语言之间传递异常,但没有提供关于如何在 C++ 部分获取异常信息的信息。 - LoneWanderer
这是个好主意。但是我有很多入口点,所以我得为每个入口点编写一个包装器。 - Jean-François Fabre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接