【问题标题】:how to get Ada exception message when catching from C++ handler?从 C++ 处理程序捕获时如何获取 Ada 异常消息?
【发布时间】:2018-05-21 11:58:19
【问题描述】:

使用 GNAT Ada 和 Gnu C++,我将一段 Ada 代码与一个 c++ 包装器连接起来,并且我想在运行这个(愚蠢的)代码时正确捕获 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++ 编译器如何实现异常。两种语言都以不同的方式指定事物,并且在编译器代码生成和库功能的使用中都需要经过深思熟虑的设计选择,以确保兼容性或至少跨语言边界的一致映射。任何解决方案都将针对特定的编译器集。
  • 是的,这个问题也被标记为 gnat。所以它使用 g++/gnat,我会编辑
  • ABI 的世界只是非常缓慢地超越了 C 接口。异常是编译器特定的东西,尽管它们可能遵守平台约定。
  • 还发现了这个:function 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).
  • 我正在与 AdaCore GNAT 维护人员交谈,但他们对此有指导意见:捕获异常,然后调用一些 GNAT.Last_Exception(不记得确切)以获取最后抛出的异常。在多线程环境中并不完美,但大部分时间都可以工作(不幸的是,该功能已损坏,目前正在修复,仅适用于 GNATPro 版本的 beta 版本,因此直到 2019 年才向公众/GPL 提供)跨度>

标签: c++ exception g++ ada gnat


【解决方案1】:

在 Ada 中,您可以使用标准库中的函数 Ada.Exceptions.Exception_NameAda.Exceptions.Exception_Message 获取有关异常发生的信息。一种选择是提供对这两个函数的精简绑定,并在您需要有关异常的信息时调用它们。 (具体如何将abi::__foreign_exception 映射到Ada.Exceptions.Exception_ID 留给读者练习。)

另一种选择是向 Ada 编译器明确说明您正在编写 C++,并使用 CPlusPlus 而不是 C 作为导出约定。这可能会使编译器提供 C++ 可以理解的异常。

这只是部分答案,因为我非常不熟悉 C++,但我希望它可以帮助你朝着正确的方向发展。

【讨论】:

  • CPlusPlus 不存在,但CPP 存在:) 现在尝试。哎呀,Ada 编译器似乎无法生成正确的 C++ 名称。还是一样的_ada_throw 符号...
  • 赞成您的第一个选项,但我怀疑您的替代方案行不通,并且通过删除它可以改进此答案。
【解决方案2】:

根据您想在 C++ 世界中处理异常的方式,在 Ada 中编写一个包装器可能更容易/更干净,它调用 ada_throw 并处理任何异常。然后简单地从 C++ 调用这个包装器。

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

【讨论】:

  • GNAT.CPP_Exceptions 似乎是执行包装的最佳选择。引用其 .ads 文件 -- This package provides an interface for raising and handling C++ exceptions。我在看adacore.com/gems/gem-114-ada-and-c-exceptions
  • 不幸的是,Ada Gem 展示了如何跨语言传递异常,但没有提供有关如何在 C++ 部分中获取异常信息的信息。
  • 这是个好主意。但是我有很多入口点,所以我必须为每个入口点编写一个包装器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-13
  • 2017-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多