【问题标题】:Is there some version of longjmp that can output long values?是否有一些版本的 longjmp 可以输出长值?
【发布时间】:2019-04-15 12:23:28
【问题描述】:

我正在开发一个 C 语言框架,为此我想实现异常,为此我将 longjump 与 setjump 一起使用,但在 x64 机器上,longjump 仍然输出一个整数。

我创建了一个类(本质上是 vptr 的结构),它表示异常,但要在代码中将其抛出,我需要抛出一个指向该结构的指针。对于 x64 机器,指针有一个 unsigned long long 值(qword),对于 x86 机器有一个 unsigned int(dword),所以我只需要输出 qword 来处理错误。

longjmpsetjmp的实现,可以输出qword吗?

或者也许我可以编写自己的 longjump,但需要原始源代码。

【问题讨论】:

  • 要便携,使用全局间接...
  • @Jean-BaptisteYunès 我在哪里可以找到这方面的信息?另外,这是 WinAPI 框架,我不确定是否需要可移植性
  • 没有longjmp() 的可移植(标准)变体返回long 而不是int。您需要查看特定于平台的手册中的选项,但我认为您不会找到。你不能简单地使用另一个全局变量吗?
  • 除非你在单个函数中跳转,因此 jmpbuf 变量是该函数的本地变量,否则你已经有一个多线程的混乱。无论您使用何种技术来为每个线程提供它自己的jmpbuf,也可以用于为long 返回值提供额外的空间。
  • C 或 C++ 中没有很多东西比 C++ 异常处理更糟糕,但 setjmp/longjmp 绝对是其中之一。避免。不惜一切成本。就像瘟疫一样。

标签: c setjmp


【解决方案1】:

您可以将jmp_buf 类型的变量包含在一个更大的结构中,可能仅大sizeof(void*)。然后就在调用longjmp() 之前,您可以将指针存储在该额外空间中。无需尝试将指针压缩到 int 中。

例子:

#include <stdio.h>
#include <setjmp.h>

struct jmp_buf_struct
{
  jmp_buf jb;
  void* exc_ptr;
};

void may_throw(jmp_buf jb)
{
  struct jmp_buf_struct* jbs_ptr = (struct jmp_buf_struct*)jb;
  jbs_ptr->exc_ptr = "Exception message!";
  longjmp(jb, 1);
}

int main()
{
  struct jmp_buf_struct jbs;
  if (setjmp(jbs.jb))
  {
    printf("Threw %p = \"%s\".", jbs.exc_ptr, (char*)jbs.exc_ptr);
  }
  else
  {
    may_throw(jbs.jb);
    puts("Didn't throw.");
  }
  return 0;
}

Output:

抛出 0x55638ebc78c4 = "异常消息!"。

【讨论】:

    【解决方案2】:

    如果你想要可移植,那么你可以使用数组索引,到一个存储所有 64 位指针的数组(或者只是一个指向结构的指针数组,其中包含有关在某些异常情况下如何处理的信息页面)。

    如何填充这样的数组是另一个问题。当然,您不需要使用所有可能成为异常的实例来填充数组,只需使用您已try-ed 并且能够catch 的实例。但可能您需要的不仅仅是一个指针(因为您必须处理运行时情况,在这种情况下您有相同的异常,在堆栈中的多个 active 位置捕获。)

    一旦你解决了上述问题,也许你甚至可以使用short int,一旦你了解了你需要解决的问题的性质。

    根据阅读 cmets,我看到您评论说,由于多线程问题,全局变量不适合。首先,您可以在线程的上下文中将它全局化(例如errno 变量),因为这就是使用void * 来调用线程执行的例程的原因,并且完成后返回。您可以在那里拥有它,专用于线程全局数据。

    第二点,如果你想从 C 的角度来管理这些奇怪的事情,比如以奇怪的方式操作堆栈,那么上面提到的函数就是这样做的(我不相信你完全了解 @ 的内部结构987654324@/longjmp() 工作。)我可以告诉你,setjmp()/longjmp() api 是很久以前(现在大约 50 年)在旧的 V6 unix 代码时代编写的处理未知的 unix 设备驱动程序错误处理 --- 一个非常受控和简单的环境 ---) 简单地说,使用 longjmp() 比切换到完全支持其核心异常的不同语言(如建议的 C++)(此建议不是我的,它已在 cmets 中针对您的问题提出)

    第三。如果您使用setjmp()longjmp(),您还需要知道它们(都)使用调用线程的堆栈来标记指针和去哪里 来存储信息。因此,您必须控制,例如,如果您在信号处理程序中执行longjmp(),您可能会严重破坏执行信号处理程序的线程的堆栈(这是被信号中断的线程),如果它与拨打setjmp() 的人不同。原因是被中断的线程将使用执行setjmp() 的线程之一切换其堆栈,并且两个线程将在不同点开始使用相同堆栈执行代码,这会跳回到两者的实现时间函数(只有一台 pdp 计算机可用,没有像今天常见的那样有多个 cpus/cores,所以只有一个堆栈)你必须在这里特别小心,因为通常,生成的线程异常与 catch 的位置相同,但对于异步陷阱(如信号处理)来说,这可能是错误的。

    顺便说一句,您所做的非常有趣,并且可以让您了解一种语言如何实现内部复杂的行为,例如异常处理。我为你尝试这种事情的勇气鼓掌,不要犹豫,如果你需要 C++ 的导师,我会为你服务。

    不要放弃!!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-18
      • 2019-02-04
      相关资源
      最近更新 更多